|
|
# How To APEX
|
|
|
|
|
|
[go/android-apex-howto](http://go/android-apex-howto) (internal link)
|
|
|
|
|
|
This doc reflects the current implementation status, and thus is expected to
|
|
|
change regularly.
|
|
|
|
|
|
## Reference
|
|
|
|
|
|
To understand the design rationale, visit this
|
|
|
[public doc](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md#alternatives-considered-when-developing-apex)
|
|
|
and [go/android-apex](http://go/android-apex) (internal).
|
|
|
|
|
|
## Building an APEX
|
|
|
|
|
|
A cheat sheet:
|
|
|
|
|
|
```
|
|
|
apex {
|
|
|
name: "com.android.my.apex",
|
|
|
|
|
|
manifest: "apex_manifest.json",
|
|
|
|
|
|
// optional. if unspecified, a default one is auto-generated
|
|
|
androidManifest: "AndroidManifest.xml",
|
|
|
|
|
|
// libc.so and libcutils.so are included in the apex
|
|
|
native_shared_libs: ["libc", "libcutils"],
|
|
|
binaries: ["vold"],
|
|
|
java_libs: ["core-all"],
|
|
|
apps: ["myapk"],
|
|
|
prebuilts: ["my_prebuilt"],
|
|
|
|
|
|
compile_multilib: "both",
|
|
|
|
|
|
key: "com.android.my.apex.key",
|
|
|
certificate: ":com.android.my.apex.certificate",
|
|
|
}
|
|
|
```
|
|
|
|
|
|
`apex_manifest.json` should look like:
|
|
|
|
|
|
```
|
|
|
{
|
|
|
"name": "com.android.my.apex",
|
|
|
"version": 1
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The file contexts files should be created at
|
|
|
`/system/sepolicy/apex/com.android.my.apex-file_contexts`:
|
|
|
|
|
|
```
|
|
|
(/.*)? u:object_r:system_file:s0
|
|
|
/sub(/.*)? u:object_r:sub_file:s0
|
|
|
/sub/file3 u:object_r:file3_file:s0
|
|
|
```
|
|
|
|
|
|
The file should describe the contents of your apex. Note that the file is
|
|
|
amended by the build system so that the `apexd` can access the root directory of
|
|
|
your apex and the `apex_manifest.pb` file. (Technically, they are labeled as
|
|
|
`system_file`.) So if you're
|
|
|
[building the apex without Soong](#building-apex-without-soong), please be sure
|
|
|
that `apexd` can access the root directory and the `apex_manifest.pb` file. (In
|
|
|
the example above, the first line does that.)
|
|
|
|
|
|
#### A script to create a skeleton of APEX
|
|
|
|
|
|
For convenience, you might want to use a
|
|
|
[script](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/tools/create_apex_skeleton.sh)
|
|
|
that creates a skeleton (`Android.bp`, keys, etc.) of an APEX for you. You only
|
|
|
need to adjust the `APEX_NAME` variable to be your actual APEX name.
|
|
|
|
|
|
#### File types and places where they are installed in apex
|
|
|
|
|
|
file type | place in apex
|
|
|
-------------- | ----------------------------------------------------------
|
|
|
shared libs | `/lib` and `/lib64` (`/lib/arm` for translated arm in x86)
|
|
|
executables | `/bin`
|
|
|
java libraries | `/javalib`
|
|
|
android apps | `/app` or `/priv-app`
|
|
|
prebuilts | `/etc`
|
|
|
|
|
|
### Transitive dependencies
|
|
|
|
|
|
Transitive dependencies of a native shared lib or an executable are
|
|
|
automatically included in the APEX. For example, if `libFoo` depends on
|
|
|
`libBar`, then the two libs are included even when only `libFoo` is listed in
|
|
|
`native_shared_libs` property.
|
|
|
|
|
|
However, if a transitive dependency has a stable ABI, it is not included
|
|
|
transitively. It can be included in an APEX only by directly being referenced.
|
|
|
Currently (2019/08/05), the only module type that can provide stable ABI is
|
|
|
`cc_library`. To do so, add `stubs.*` property as shown below:
|
|
|
|
|
|
```
|
|
|
cc_library {
|
|
|
name: "foo",
|
|
|
srcs: [...],
|
|
|
stubs: {
|
|
|
symbol_file: "foo.map.txt",
|
|
|
versions: ["29", "30"],
|
|
|
},
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Use this when a lib has to be accessed across the APEX boundary, e.g. between
|
|
|
APEXes or between an APEX and the platform.
|
|
|
|
|
|
### apex_available
|
|
|
|
|
|
Any module that is “included” (not just referenced) in an APEX either via the
|
|
|
direct dependency or the transitive dependency has to correctly set the
|
|
|
`apex_available` property in its `Android.bp` file. The property can have one or
|
|
|
more of the following values:
|
|
|
|
|
|
* `<name_of_an_apex>`: Like `com.android.adbd`. By specifying the APEX names
|
|
|
explicitly, the module is guaranteed to be included in those APEXes. This is
|
|
|
useful when a module has to be kept as an implementation detail of an APEX
|
|
|
and therefore shouldn’t be used from outside.
|
|
|
* `//apex_available:anyapex`: This means that the module can be included in
|
|
|
any APEX. This is useful for general-purpose utility libraries like
|
|
|
`libbase`, `libcutils`, etc.
|
|
|
* `//apex_available:platform`: The module can be installed to the platform,
|
|
|
outside of APEXes. This is the default value. However, `if apex_available`
|
|
|
is set to either of `<name_of_an_apex` or `//apex_available:anyapex`, the
|
|
|
default is removed. If a module has to be included in both APEX and the
|
|
|
platform, `//apex_available:platform` and`//apex_available:anyapex` should
|
|
|
be specified together.
|
|
|
|
|
|
The act of adding an APEX name to the `apex_available` property of a module has
|
|
|
to be done or be reviewed by the author(s) of the module. Being included in an
|
|
|
APEX means that the module will be portable, i.e., running on multiple versions
|
|
|
of the current and previous platforms, whereas it usually was expected to run on
|
|
|
the current (the up-to-date) platform. Therefore, the module might have to be
|
|
|
prepared to not have version-specific dependencies to the platform, like the
|
|
|
existence of a dev node, a system call, etc.
|
|
|
|
|
|
### Handling multiple ABIs
|
|
|
|
|
|
`compile_multilib`: specifies the ABI(s) that this APEX will compile native
|
|
|
modules for. Can be either of `both`, `first`, `32`, `64`, `prefer32`. For most
|
|
|
of the cases, this should be `both`.
|
|
|
|
|
|
`native_shared_libs`: installed for **_both_** primary and secondary ABIs of the
|
|
|
device. Of course, if the APEX is built for a target having single ABI (i.e.
|
|
|
32-bit only or 64-bit only), only libraries with the corresponding ABI are
|
|
|
installed.
|
|
|
|
|
|
`binaries`: installed only for the **_primary_** ABI of the device. In other
|
|
|
words,
|
|
|
|
|
|
* If the device is 32-bit only, only the 32-bit variant of the binary is
|
|
|
installed.
|
|
|
* If the device supports both 32/64 ABIs, but with
|
|
|
`TARGET_PREFER_32_BIT_EXECUTABLES=true`, then only the 32-bit variant of the
|
|
|
binary is installed.
|
|
|
* If the device is 64-bit only, then only the 64-bit variant of the binary is
|
|
|
installed.
|
|
|
* If the device supports both 32/64 ABIs, but without
|
|
|
`TARGET_PREFER_32_BIT_EXECUTABLES=true`, then only the 64-bit variant of the
|
|
|
binary is installed.
|
|
|
|
|
|
In order to fine control the ABIs of the native libraries and binaries to be
|
|
|
installed, use
|
|
|
`multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]`
|
|
|
properties.
|
|
|
|
|
|
* `first`: matches with the primary ABI of the device. This is the default for
|
|
|
`binaries`.
|
|
|
* `lib32`: matches with the 32-bit ABI of the device, if supported
|
|
|
* `lib64`: matches with the 64-bit ABI of the device, it supported
|
|
|
* `prefer32`: matches with the 32-bit ABI of the device, if support. If 32-bit
|
|
|
ABI is not supported, it is matched with the 64-bit ABI.
|
|
|
* `both`: matches with the both ABIs. This is the default for
|
|
|
`native_shared_libraries`.
|
|
|
* `java libraries` and `prebuilts`: ABI-agnostic
|
|
|
|
|
|
Example: (let’s assume that the device supports 32/64 and does not prefer32)
|
|
|
|
|
|
```
|
|
|
apex {
|
|
|
// other properties are omitted
|
|
|
compile_multilib: "both",
|
|
|
native_shared_libs: ["libFoo"], // installed for 32 and 64
|
|
|
binaries: ["exec1"], // installed for 64, but not for 32
|
|
|
multilib: {
|
|
|
first: {
|
|
|
native_shared_libs: ["libBar"], // installed for 64, but not for 32
|
|
|
binaries: ["exec2"], // same as binaries without multilib.first
|
|
|
},
|
|
|
both: {
|
|
|
native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
|
|
|
binaries: ["exec3"], // installed for 32 and 64
|
|
|
},
|
|
|
prefer32: {
|
|
|
native_shared_libs: ["libX"], // installed for 32, but not for 64
|
|
|
},
|
|
|
lib64: {
|
|
|
native_shared_libs: ["libY"], // installed for 64, but not for 32
|
|
|
},
|
|
|
},
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### APEX image signing
|
|
|
|
|
|
**Note**: the APEX skeleton creation
|
|
|
[script](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/tools/create_apex_skeleton.sh)
|
|
|
automates this step.
|
|
|
|
|
|
Each APEX must be signed with different keys. There is no concept of the
|
|
|
platform key. `apexd` in the future might reject if multiple APEXes are signed
|
|
|
with the same key. When a new key is needed, create a public-private key pair
|
|
|
and make an `apex_key` module. Use `key` property to sign an APEX using the key.
|
|
|
The public key is included in the zip container of the APEX as a file entry
|
|
|
`apex_pubkey`.
|
|
|
|
|
|
How to generate the key pair:
|
|
|
|
|
|
```
|
|
|
# create an rsa key pair
|
|
|
$ openssl genrsa -out com.android.my.apex.pem 4096
|
|
|
|
|
|
# extract the public key from the key pair
|
|
|
$ avbtool extract_public_key --key com.android.my.apex.pem \
|
|
|
--output com.android.my.apex.avbpubkey
|
|
|
|
|
|
# in Android.bp
|
|
|
apex_key {
|
|
|
name: "com.android.my.apex.key",
|
|
|
public_key: "com.android.my.apex.avbpubkey",
|
|
|
private_key: "com.android.my.apex.pem",
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Important: In the above example, the name of the public key (that is
|
|
|
`com.android.my.apex`) becomes the ID of the key. The ID of the key used to sign
|
|
|
an APEX is recorded in the APEX. At runtime, a public key with the same ID in
|
|
|
the device is used to verify the APEX.
|
|
|
|
|
|
### APK (APEX container) signing
|
|
|
|
|
|
**Note**: the APEX skeleton creation
|
|
|
[script](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/tools/create_apex_skeleton.sh)
|
|
|
automates this step.
|
|
|
|
|
|
An APEX should also be signed just like APKs. So, an APEX is signed twice; once
|
|
|
for the mini file system (`apex_payload.img` file) and once for the entire file.
|
|
|
|
|
|
Just like APK, the file-level signing is done via the `certificate` property. It
|
|
|
can be set in three ways.
|
|
|
|
|
|
* not set: if unset, the APEX is signed with the certificate located at
|
|
|
`PRODUCT_DEFAULT_DEV_CERTIFICATE`. If the flag is also unset, it defaults to
|
|
|
`build/target/product/security/testkey`
|
|
|
* `<name>`: the APEX is signed with the certificate named `<name>` in the same
|
|
|
directory as `PRODUCT_DEFAULT_DEV_CERTIFICATE`
|
|
|
* `<name>`: the APEX signed with the certificate which is defined by a
|
|
|
Soong module named `<name>`. The certificate module can be defined as
|
|
|
follows.
|
|
|
|
|
|
```
|
|
|
android_app_certificate {
|
|
|
name: "com.android.my.apex.certificate",
|
|
|
// This will use com.android.my.apex.x509.pem (the cert) and
|
|
|
// com.android.my.apex.pk8 (the private key)
|
|
|
certificate: "com.android.my.apex",
|
|
|
}
|
|
|
```
|
|
|
|
|
|
How to generate the certificate/private key pair:
|
|
|
|
|
|
```
|
|
|
# Create certificate and private in PEM form
|
|
|
$ openssl req -x509 -newkey rsa:4096 -nodes -days 999999 -keyout key.pem -out com.android.my.apex.x509.pem
|
|
|
|
|
|
# Enter following info via the interactive prompts
|
|
|
# Country Name: US
|
|
|
# State: California
|
|
|
# Locality Name: Mountain View
|
|
|
# Organization Name: Android
|
|
|
# Organization Unit Name: Android
|
|
|
# Common Name: <your-apk-name>
|
|
|
# Email address: android@android.com
|
|
|
|
|
|
# Convert the private to pkcs8 format
|
|
|
$ openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out com.android.my.apex.pk8 -nocrypt
|
|
|
```
|
|
|
|
|
|
### Signing APEXs with release keys
|
|
|
|
|
|
The procedures described in the [APEX image signing](#apex-image-signing) and
|
|
|
[APK (APEX container) signing](#apk-apex-container_signing) sections require the
|
|
|
private keys to be present in the tree. This is not suitable for public release.
|
|
|
Please refer to the
|
|
|
[APEX signing key replacement](https://source.android.com/devices/tech/ota/sign_builds#apex-signing-key-replacement)
|
|
|
documentation to prepare the APEX packages for release.
|
|
|
|
|
|
For the Google-specific procedure for release keys, the documentation is
|
|
|
available at
|
|
|
[go/android-apex-howto-internal](http://go/android-apex-howto-internal)
|
|
|
(internal only).
|
|
|
|
|
|
### Linker namespaces for native libraries and binaries
|
|
|
|
|
|
The linker needs to be set up with separate namespaces for each APEX, for
|
|
|
isolation. It is done through `ld.config.txt` files, which are autogenerated by
|
|
|
`linkerconfig`. Normally you only need to ensure that the APEX manifest
|
|
|
correctly lists the native libraries it requires (from platform or other APEXes)
|
|
|
and provides, which by default is taken from the build system.
|
|
|
|
|
|
Refer to the [design doc](go/linker-config-apex) for more information about
|
|
|
linkerconfig and apex.
|
|
|
|
|
|
## Installing an APEX
|
|
|
|
|
|
Use
|
|
|
|
|
|
```
|
|
|
adb install --staged <path_to_apex> && adb reboot
|
|
|
```
|
|
|
|
|
|
The `adb install --staged` command triggers a verification for the staged APEX
|
|
|
which might fail when the APEX is signed incorrectly.
|
|
|
|
|
|
Note that on Q devices when the `adb install --staged` command completes you
|
|
|
still will have to wait until the verification for the staged APEX is finished
|
|
|
before issuing `adb reboot`.
|
|
|
|
|
|
On R devices we added the `--wait` option to `adb install` to wait until the
|
|
|
verification is completed before returning. On S devices the `--wait` option is
|
|
|
implicit.
|
|
|
|
|
|
## Hot swapping an APEX (development only)
|
|
|
|
|
|
Use
|
|
|
|
|
|
```
|
|
|
adb sync && adb shell cmd -w apexservice remountPackages
|
|
|
```
|
|
|
|
|
|
Note that for this command to remount your APEX, you must ensure that all
|
|
|
processes that have reference to your APEX are killed. E.g. if you are
|
|
|
developing an APEX that contributes to system\_server, you can use the
|
|
|
following:
|
|
|
|
|
|
```
|
|
|
adb root
|
|
|
adb remount
|
|
|
adb shell stop
|
|
|
adb sync
|
|
|
adb shell cmd -w apexservice remountPackages
|
|
|
adb shell start
|
|
|
```
|
|
|
|
|
|
## Using an APEX
|
|
|
|
|
|
After the reboot, the apex will be mounted at `/apex/<apex_name>@<version>`
|
|
|
directory. Multiple versions of the same APEX can be mounted at the same time. A
|
|
|
mount point that always points to the latest version of an APEX is provided:
|
|
|
`/apex/<apex_name>`.
|
|
|
|
|
|
Clients can use the latter path to read or execute something from APEX.
|
|
|
|
|
|
So, typical usage of APEX is as follows.
|
|
|
|
|
|
1. an APEX is pre-loaded under `/system/apex`when the device is shipped.
|
|
|
2. Files in it are accessed via the `/apex/<apex_name>/`path.
|
|
|
3. When an updated version of the APEX is installed in `/data/apex/active`, the
|
|
|
path will point to the new APEX after the reboot.
|
|
|
|
|
|
## Updating service with APEX
|
|
|
|
|
|
Using APEX, you can update a service. To do so, you need …
|
|
|
|
|
|
1) Mark the service in system partition as updatable. Add the new option
|
|
|
‘updatable’ to the service definition.
|
|
|
|
|
|
```
|
|
|
/system/etc/init/myservice.rc:
|
|
|
|
|
|
service myservice /system/bin/myservice
|
|
|
class core
|
|
|
user system
|
|
|
…
|
|
|
updatable
|
|
|
```
|
|
|
|
|
|
2) Create a new `.rc` file for the updated service. Use ‘`override`’ option to
|
|
|
redefine the existing service.
|
|
|
|
|
|
```
|
|
|
/apex/my.apex/etc/init.rc:
|
|
|
|
|
|
service myservice /apex/my.apex/bin/myservice
|
|
|
class core
|
|
|
user system
|
|
|
…
|
|
|
override
|
|
|
```
|
|
|
|
|
|
Note that you can only have service definitions in the rc file in APEX. You
|
|
|
cannot have action triggers in APEXes.
|
|
|
|
|
|
Also note that if a service marked as updatable is started before APEXes are
|
|
|
activated, the start is delayed until the activation of APEXes is finished.
|
|
|
|
|
|
## Configuring system to support APEX updates
|
|
|
|
|
|
Set the following system property to true to support APEX file updates.
|
|
|
|
|
|
```
|
|
|
<device.mk>:
|
|
|
|
|
|
PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true
|
|
|
|
|
|
BoardConfig.mk:
|
|
|
TARGET_FLATTEN_APEX := false
|
|
|
|
|
|
or just
|
|
|
<device.mk>:
|
|
|
|
|
|
$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
|
|
|
```
|
|
|
|
|
|
## Flattened APEX
|
|
|
|
|
|
For legacy devices, it is sometimes impossible or infeasible to update the old
|
|
|
kernel to fully support APEX. For example, the kernel might have been built
|
|
|
without `CONFIG_BLK_DEV_LOOP=Y`, which is crucial for mounting the file system
|
|
|
image inside an APEX.
|
|
|
|
|
|
Flattened APEX is a specially built APEX that can be activated on devices with a
|
|
|
legacy kernel. Files in a flattened APEX are directly installed to a directory
|
|
|
under the built-in partition. For example, `lib/libFoo.so` in a flattened APEX
|
|
|
my.apex is installed to `/system/apex/my.apex/lib/libFoo.so`.
|
|
|
|
|
|
Activating a flattened APEX doesn't involve the loop device. The entire
|
|
|
directory `/system/apex/my.apex` is directly bind-mounted to `/apex/name@ver`.
|
|
|
|
|
|
Flattened APEXs can‘t be updated by downloading updated versions of the APEXs
|
|
|
from network because the downloaded APEXs can’t be flattened. Flattened APEXs
|
|
|
can be updated only via a regular OTA.
|
|
|
|
|
|
Note that flattened APEX is the default configuration for now (2019/Aug). This
|
|
|
means all APEXes are by default flattened unless you explicitly configure your
|
|
|
device to support updatable APEX (explained above).
|
|
|
|
|
|
Also note that, mixing flattened and non-flattened APEXes in a device is NOT
|
|
|
supported. It should be either all non-flattened or all flattened. This is
|
|
|
especially important when shipping pre-signed APEX prebuilts for the projects
|
|
|
like Mainline. APEXes that are not pre-signed (i.e. built from the source)
|
|
|
should also be non-flattened and signed with proper keys in that case. The
|
|
|
device should inherit from `updatable_apex.mk` as explained above.
|
|
|
|
|
|
## Building APEX without Soong
|
|
|
|
|
|
An APEX can be built without relying on the build commands generated by Soong.
|
|
|
|
|
|
1) Prepare following files:
|
|
|
|
|
|
- APEX manifest file (in JSON)
|
|
|
|
|
|
- AndroidManifest file (in XML, optional)
|
|
|
|
|
|
- AVB private key
|
|
|
|
|
|
- APK certificate (`*.x509.pem`)
|
|
|
|
|
|
- APK private key (`*.pk8`)
|
|
|
|
|
|
- `file_contexts` file
|
|
|
|
|
|
- files to be packaged into the APEX
|
|
|
|
|
|
2) Create `canned_fs_config` file
|
|
|
|
|
|
It is a file that specifies access bits and uid/gid of each file in the APEX.
|
|
|
|
|
|
```
|
|
|
/ 1000 1000 0755
|
|
|
/apex_manifest.json 1000 1000 0644
|
|
|
/apex_manifest.pb 1000 1000 0644
|
|
|
/file1 1000 1000 0644
|
|
|
/file2 1000 1000 0644
|
|
|
/dir 0 2000 0755
|
|
|
/dir/file3 1000 1000 0644
|
|
|
...
|
|
|
```
|
|
|
|
|
|
Note that ALL files AND directories must be specified. And don’t forget to have
|
|
|
a line for `/`and `/apex_manifest.pb`. (`/apex_manifest.json` line is for
|
|
|
Q-targeting modules)
|
|
|
|
|
|
3) Invoke `apexer`
|
|
|
|
|
|
```
|
|
|
$ apexer \
|
|
|
--manifest <apex_manifest_file> \
|
|
|
--file_contexts <file_contexts_file> \
|
|
|
--canned_fs_config <canned_fs_config_file> \
|
|
|
--key <avb_private_key_file> \
|
|
|
--payload_type image \
|
|
|
--android_manifest <android_manifest_file> \
|
|
|
--override_apk_package_name com.google.foo \
|
|
|
<input_directory> \
|
|
|
<output_apex_file>
|
|
|
```
|
|
|
|
|
|
`--android_manifest` and -`-override_apk_package` are optional arguments and
|
|
|
thus can be omitted if not needed.
|
|
|
|
|
|
Note: The `<apex_manifest_file>` shouldn’t be under `<input_directory>`.
|
|
|
|
|
|
4) Sign it
|
|
|
|
|
|
`apexer` signs the `apex_payload.img` file only. The entire apex (which is a zip
|
|
|
file) has to be signed with `Signapk`.
|
|
|
|
|
|
```
|
|
|
$ java \
|
|
|
-Djava.library.path=$(dirname out/soong/host/linux-x86/lib64/libconscrypt_openjdk_jni.so)\
|
|
|
-jar out/soong/host/linux-x86/framework/signapk.jar \
|
|
|
-a 4096 \
|
|
|
<apk_certificate_file> \
|
|
|
<apk_private_key_file> \
|
|
|
<unsigned_input_file> \
|
|
|
<signed_output_file>
|
|
|
```
|
|
|
|
|
|
This will sign the input file with the cert/privkey pairs to produce the output
|
|
|
file.
|
|
|
|
|
|
## Re-packaging an existing APEX
|
|
|
|
|
|
If an APEX has been build by passing `--include_build_info` to `apexer` (this is
|
|
|
the default when building via Soong), it will then include a file named
|
|
|
`apex_build_info.pb` which will store as much information as possible about how
|
|
|
the apex was built (see the `ApexBuildInfo` proto
|
|
|
[definition](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/proto/apex_build_info.proto)
|
|
|
for more info) with the exception of the signing keys.
|
|
|
|
|
|
We also provide a tool named `deapexer` to extract the payload content of an
|
|
|
APEX in a local directory.
|
|
|
|
|
|
By using these tools, you can then adapt the procedure described in the
|
|
|
[building the apex without Soong](#building-apex-without-soong) section and pass
|
|
|
the `--build_info apex_build_info.pb` file where `apex_build_info.pb` contains
|
|
|
all the build parameters that you would otherwise pass via flag to `apexer`.
|
|
|
|
|
|
We do this programmatically in some unit test code to generate "unusual" APEX
|
|
|
files, see for example
|
|
|
[here](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/apexer/apexer_test.py)
|
|
|
and
|
|
|
[here](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/tests/testdata/sharedlibs/build/shared_libs_repack.py).
|