Enforcing Product Partition Interfaces

Android 11 unbundles the product partition, making it independent of the system and vendor partitions. As part of these changes, you can now control the product partition’s access to native and Java interfaces (which is similar to how interface enforcement works for vendor partitions).

Enforcing native interfaces

To enable the native interface enforcement, set PRODUCT_PRODUCT_VNDK_VERSION to current. (The version is automatically set to current when the shipping API level for the target is greater than 29.) Enforcement allows:

  • Native modules in the product partition to link:
    • Statically or dynamically to other modules in the product partition that include static, shared, or header libraries.
    • Dynamically to VNDK libraries in the system partition.
  • JNI libraries in unbundled APKs in the product partition to link to libraries in /product/lib or /product/lib64 (this is in addition to the NDK libraries).

Enforcement doesn't allow other links to partitions other than the product partition.

Build time enforcement (Android.bp)

In Android 11, system modules can create a product image variant in addition to core and vendor image variants. When native interface enforcement is enabled (PRODUCT_PRODUCT_VNDK_VERSION is set to current):

  • Native modules in the product partition are in the product variant instead of the core variant.

  • Modules with product_available: true in their Android.bp files are available to the product variant.

  • Libraries or binaries that specify product_specific: true can link to other libraries that specify product_specific: true or product_available: true in their Android.bp files.

  • VNDK libraries must have product_available: true in their Android.bp files so product binaries can link to VNDK libs.

The following table summarizes the Android.bp properties used to create image variants.

Properties in Android.bp Variants created
Before enforcement After enforcement
default (none) core

(includes /system, /system_ext and /product)

core

(includes /system and /system_ext but not /product)

system_ext_specific: true core core
product_specific: true core product
vendor: true vendor vendor
vendor_available: true core, vendor core, vendor
product_available: true N/A core, product
vendor_available: true AND product_available: true N/A core, product, vendor
system_ext_specific: true AND vendor_available: true core, vendor core, vendor
product_specific: true AND vendor_available: true core, vendor product, vendor

Build time enforcement (Android.mk)

When native interface enforcement is enabled, native modules installed to the product partition have a native:product link type that can link only to other native:product or native:vndk modules. Attempting to link to any modules other than these causes the build system to generate a link type check error.

Runtime enforcement

When native interface enforcement is enabled, the linker configuration for the bionic linker doesn't allow system processes to use product libraries, creating a product section for the product processes that can't link to libraries outside the product partition (however, such processes can link to VNDK libraries). Attempts to violate the runtime link configuration cause the process to fail and generate a CANNOT LINK EXECUTABLE error message.

Enforcing Java interfaces

To enable the Java interface enforcement, set PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE to true. (The value is automatically set to true when the shipping API level for the target is greater than 29.) When enabled, enforcement allows/disallows the following access.

API /system /system_ext /product /vendor /data
Public API
@SystemApi
@hide API

As in the vendor partition, an app or a Java library in the product partition is allowed to use only public and system APIs; linking to a library that uses hidden APIs isn't allowed. This restriction includes linking at build time and reflection in runtime.

Build time enforcement

At build time, Make and Soong verify that Java modules in the product partition don't use hidden APIs by checking the platform_apis and sdk_version fields. The sdk_version of apps in the product partition must be filled with current, system_current, or numeric version of the API, and the platform_apis field must be empty.

Runtime enforcement

The Android runtime verifies that apps in the product partition don't use hidden APIs, including reflection. For details, refer to Restrictions on non-SDK interfaces.

Enabling product interface enforcement

Use the steps in this section to enable product interface enforcement.

Step Task Required
1 Define your own system makefile that specifies the packages for the system partition, then set the artifacts path requirement check in the device.mk (to prevent nonsystem modules from installing to the system partition). N
2 Clean up the allowed list. N
3 Enforce native interfaces and identify runtime link failures (can run in parallel with Java enforcement). Y
4 Enforce Java interfaces and verify runtime behavior (can run in parallel with native enforcement). Y
5 Check runtime behaviors. Y
6 Update device.mk with product interface enforcement. Y

Step 1: Create makefile and enable artifact path check

In this step, you define the system makefile.

  1. Create a makefile that defines the packages for the system partition. For example, create an oem_system.mk file with the following:

    $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk)
    $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk)
    
    # Applications
    PRODUCT_PACKAGES += \
        CommonSystemApp1 \
        CommonSystemApp2 \
        CommonSystemApp3 \
    
    # Binaries
    PRODUCT_PACKAGES += \
        CommonSystemBin1 \
        CommonSystemBin2 \
        CommonSystemBin3 \
    
    # Libraries
    PRODUCT_PACKAGES += \
        CommonSystemLib1 \
        CommonSystemLib2 \
        CommonSystemLib3 \
    
    PRODUCT_SYSTEM_NAME := oem_system
    PRODUCT_SYSTEM_BRAND := Android
    PRODUCT_SYSTEM_MANUFACTURER := Android
    PRODUCT_SYSTEM_MODEL := oem_system
    PRODUCT_SYSTEM_DEVICE := generic
    
    # For system-as-root devices, system.img should be mounted at /, so we
    # include ROOT here.
    _my_paths := \
     $(TARGET_COPY_OUT_ROOT)/ \
     $(TARGET_COPY_OUT_SYSTEM)/ \
    
    $(call require-artifacts-in-path, $(_my_paths),)
    
  2. In the device.mk file, inherit the common makefile for the system partition and enable the artifact path requirements check. For example:

    $(call inherit-product, $(SRC_TARGET_DIR)/product/oem_system.mk)
    
    # Enable artifact path requirements checking
    PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := strict
    

About the artifact path requirements

When PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS is set to true or strict, the build system prevents packages defined in other makefiles from installing to the paths defined in require-artifacts-in-path and prevents packages defined in the current makefile from installing artifacts outside the paths defined in require-artifacts-in-path.

In the example above, with PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS set to strict, makefiles outside oem_system.mk can't include modules installed to the root or system partition. To include these modules, you must either define them in the oem_system.mk file itself or in an included makefile. Attempts to install modules to disallowed paths cause build breaks. To fix breaks, do one of the following:

  • Option 1: Include the system module in the makefiles included in oem_system.mk. This makes it so the artifact path requirement is met (as the modules now exist in an included makefile) and thus allows installation to the set of paths in `require-artifacts-in-path.

  • Option 2: Install modules to the system_ext or product partition (and don't install modules to the system partition).

  • Option 3: Add modules to the PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST. This lists allowed modules to be installed.

Step 2: Empty the allowed list

In this step, you make the PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST empty so all devices sharing oem_system.mk can also share a single system image. To empty the allowed list, move any modules in the list to the system_ext or product partition or add them to system make files. This step is optional because defining a common system image isn't required to enable product interface enforcement. However, emptying the allowed list is helpful for defining the system boundary with system_ext.

Step 3: Enforce native interfaces

In this step, you set PRODUCT_PRODUCT_VNDK_VERSION := current, then look for build and runtime errors and resolve them. To check the device boot and logs and find and fix runtime link failures:

  1. Set PRODUCT_PRODUCT_VNDK_VERSION := current.

  2. Build the device and look for build errors. You're likely to see a few build breaks for missing product variants or core variants. Common breaks include:

    • Any hidl_interface module that has product_specific: true won't be available for system modules. To fix, replace product_specific: true with system_ext_specfic: true.
    • Modules might be missing the product variant required for product modules. To fix, make that module available to the product partition by setting product_available: true or move the module to the product partition by setting product_specific: true.
  3. Resolve build errors and ensure that the device builds successfully.

  4. Flash the image and look for runtime errors in the device boot and logs.

    • If the linker tag from a test case log shows a CANNOT LINK EXECUTABLE message, the make file is missing a dependency (and wasn't captured at build time).
    • To check it from the build system, add the required library to the shared_libs: or required: field.
  5. Resolve the missing dependencies using the guidance given above.

Step 4: Enforce Java interfaces

In this step, you set PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true, then find and fix resultant build errors. Look for two specific types of errors:

  • Link type errors. This error indicates that an app links to Java modules that have a broader sdk_version. To fix, you can broaden the app's sdk_version or restrict the library's sdk_version. Example error:

    error: frameworks/base/packages/SystemUI/Android.bp:138:1: module "SystemUI" variant "android_common": compiles against system API, but dependency "telephony-common" is compiling against private API.Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.
    
  • Symbol errors. This error indicates that a symbol can't be found because it's in a hidden API. To fix, use a visible (non-hidden) API or find an alternative. Example error:

    frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java:1051: error: cannot find symbol
                ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
                                               ^
      symbol:   class ProxyAuthenticate
      location: class SipSessionGroup.SipSessionImpl
    

Step 5: Check runtime behaviors

In this step, you verify runtime behaviors are as expected. For apps that are debuggable, you can monitor hidden API usage by log using StrictMode.detectNonSdkApiUsage (which generates a log when the app uses a hidden API). Alternatively, you can use the veridex static analysis tool to get the type of usage (linking or reflection), restriction level, and call stack.

  • Veridex syntax:

    ./art/tools/veridex/appcompat.sh --dex-file={apk file}
    
  • Example veridex result:

    #1: Linking greylist-max-o Landroid/animation/AnimationHandler;-><init>()V use(s):
           Lcom/android/systemui/pip/phone/PipMotionHelper;-><init>(Landroid/content/Context;Landroid/app/IActivityManager;Landroid/app/IActivityTaskManager;Lcom/android/systemui/pip/phone/PipMenuActivityController;Lcom/android/internal/policy/PipSnapAlgorithm;Lcom/android/systemui/statusbar/FlingAnimationUtils;)V
    
    #1332: Reflection greylist Landroid/app/Activity;->mMainThread use(s):
           Landroidx/core/app/ActivityRecreator;->getMainThreadField()Ljava/lang/reflect/Field;
    

For details on veridex usage, refer to Test using the veridex tool.

Step 6: Update device.mk

After fixing all build and runtime failures, and verifying that runtime behaviors are as expected, set the following in device.mk:

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true