APEX 基础篇

6,381 阅读4分钟

APEX

简介

Android Pony EXpress (APEX) 是 Android 10 中引入的一种容器格式,用于较低级别系统模块的安装流程中。此格式可帮助更新不适用于标准 Android 应用模型的系统组件。一些示例组件包括原生服务和原生库、硬件抽象层 (HAL))、运行时 (ART) 以及类库。

架构

image.png

目前官网的模块化列表:

模块名称软件包名称类型推出的版本
adbdcom.android.adbdAPEXAndroid 11
ARTcom.android.artAPEXAndroid 12
强制门户登录com.android.captiveportalloginAPKAndroid 10
CellBroadcastcom.android.cellbroadcastAPEXAndroid 11
Conscryptcom.android.conscryptAPEXAndroid 10
设备调度com.android.schedulingAPEXAndroid 12
DNS 解析器com.android.resolvAPEXAndroid 10
DocumentsUIcom.android.documentsuiAPKAndroid 10
ExtServicescom.android.ext.servicesAPK (Android 10)APEX (Android 11)Android 10
IPsec/IKEv2 库com.android.ipsecAPEXAndroid 11
媒体编解码器com.android.media.swcodecAPEXAndroid 10
媒体com.android.mediaAPEXAndroid 10(提取器、MediaSession API)Android 11 (MediaParser API)
MediaProvidercom.android.mediaproviderAPEXAndroid 11
ModuleMetadatacom.android.modulemetadataAPKAndroid 10
网络堆栈权限配置com.android.networkstack.permissionconfigAPKAndroid 10
网络组件com.android.networkstackAPKAndroid 10
NNAPI 运行时com.android.neuralnetworksAPKAndroid 11
PermissionControllercom.android.permissioncontrollerAPKAndroid 10
SDK 扩展com.android.sdkextAPEXAndroid 11
Statsdcom.android.os.statsdAPEXAndroid 11
网络共享com.android.tetheringAPKAndroid 11
时区数据com.android.tzdataAPEXAndroid 10
Wi-Ficom.android.wifi.apexAPEXAndroid 11

APEX 格式

image.png

APEX 文件

系统编译后 apex 文件通常放在 /system/apex/ 目录下,下面以 wifi 模块的 apex 为例:

image.png

  • META-INF

    这个跟 APK 一样存放文件签名信息。

    image.png

    image.png

  • AndroidManifest.xml

    含包名称和版本信息,可以使用 aapt 工具查看。

    image.png

  • apex_payload.img

    由 dm-verity 支持的 ext2 文件系统映像,ubuntu 上双击即可打开。

    image.png

    apex 支持的文件类型:

    • 本机共享库
    • 本机可执行文件
    • JAR文件
    • 数据文件
    • 配置文件
  • apex_pubkey

    是用于对文件系统映像进行签名的公钥。在运行时,此密钥可确保下载的 APEX 使用在内置分区中签署相同 APEX 的同一实体进行签名。

APEX 签名

使用两种方式为 APEX 文件签名。第一种方式是使用密钥为 apex_payload.img文件签名。第二种方式是使用 APK 签名方案 v3 为整个 APEX 签名。在此过程中使用两个不同的密钥。

image.png

内置APEX

APEX文件可以位于内置分区中,例如 /system 分区;分区已经通过 dm-verity 校验,因此APEX文件直接安装在 loop 设备上。

如果内置分区中存在 APEX,可以通过提供具有相同软件包名称和更高或相同版本代码的 APEX 软件包来更新 APEX。新的 APEX 存储在 /data中,与 APK 类似,新安装的版本会替换内置分区中已存在的版本。但与 APK 不同的是,新安装的 APEX 版本仅在重新启动后才会激活。

如何构建 APEX

  1. 工程创建

    apex 工程通常都命名为 apex ,工程一般包含以下内容:Android.bp,AndroidManifest.xml,apex_manifest.json, .avbpubkey, .pem,framework/。

    image.png

  2. 创建 AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <!--
     * Copyright (C) 2019 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      <http://www.apache.org/licenses/LICENSE-2.0>
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     -->
    <manifest xmlns:android="<http://schemas.android.com/apk/res/android>"
              package="com.haley.demo">
        <!-- APEX does not have classes.dex -->
        <application android:hasCode="false" />
        <!-- Setting maxSdk to lock the module to R. minSdk is auto-set by build system -->
    </manifest>
    
  3. 创建 apex_manifest.json

    {
      "name": "com.haley.demo",
      "version": 1
    }
    
  4. 创建 Android.bp

    apex_defaults {
        name: "com.haley.demo-defaults",
       //设置AndroidManifest.xml
        androidManifest: "AndroidManifest.xml",
        
        //设置vbmeta签名
        key: "com.haley.demo.key",
        //设置zip签名,这里使用系统自带的签名,也可以自定义
        certificate: "platform",
    }
    
     
    apex_key {
        name: "com.haley.demo.key",
        public_key: "com.haley.demo.avbpubkey",
        private_key: "com.haley.demo.pem"
    }
    
    apex {
    		// 设置apex name
        name: "com.haley.demo",
       // 设置 apex_manifest.json
        manifest: "apex_manifest.json",
       
        defaults:["com.haley.demo-defaults"],
    }
    
  5. 生成签名,下面以com.haley.demo为例:

    openssl genrsa -out com.haley.demo.pem 4096
    avbtool extract_public_key --key com.haley.demo.pem --output com.haley.demo.avbpubkey
    
    • 签名注意 apex_key 的 name 要和 .key、*.avbpubkey 命名要一致。
  6. 添加 file_context

    需要为 apex 添加一个 file_context,文件位于 /system/sepolicy/apex/com.haley.demo-file_contexts

    (/.*)?                u:object_r:system_file:s0
    
  7. 编译 apex

    直接使用 mmm 编译 apex 模块即可。

  8. 产物

    编译 apex 模块时,会在 out/soong/.intermediates/device/generic/haley/apex/test 生成 apex 的临时文件。编译成功后拷贝到 out/target/product/generic_x86_64/system/apex 目录下。

安装 APEX

apex 可以使用adb 直接安装:

adb install apex_file_name
adb reboot

安装成功之后,系统会提示你重启生效。 image.png

设备重启之后就可以在系统的 /apex 目录下找到我们安装的apex。

image.png

APEX 应用

添加一个app

修改 Android.bp 在 apex {} 中设置apps 字段,如下所示:

apex {
     //... 省略部分代码
	   apps: ["JniDemo",],
}

添加一个jar

首先创建要添加的 jar 模块,如下所示:

image.png

修改 Android.bp 在 apex {} 中设置java_libs 字段,如下所示:

apex {
     //... 省略部分代码
	   java_libs: ["JarDemo", "com.haley.demoapi"],
}
  • 注意: 要在 jar 模块的 Android.bp 中需要添加 apex_available 字段,否则会包 "com.haley.demo" requires "JarDemo" that is not available for the APEX 异常。

添加一个so

首先创建要添加的 so 模块,如下所示:

image.png

修改 Android.bp 在 apex {} 中设置native_shared_libs 字段,如下所示:

apex {
     //... 省略部分代码
	  native_shared_libs: ["libdbus"],
}
  • 跟添加 jar 模块一样,需要设置apex_available 字段。

apex 版本升级

如果 apex 版本需要升级,只需要修改 apex_manifest.json 中的 version 版本号即可:

{
  "name": "com.haley.demo",
  "version": 3
}

修改之后重新打包,使用 adb install 命令安装覆盖,设备重启生效。