AOSP 預置 APP

吳小龍同學發表於2019-08-14

Android 系統預置 APP 是做 Framework 應用開發經常經常會遇到的工作,預置 APP 分為兩種,一種是直接預置 APK,一種是預置帶有原始碼的 APP。

預置 apk

示例說明

.apk 示例,在 AOSP/packages/apps 新建名為 的檔案,放入 ***.apk,再新建 Android.mk,內容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE := < your app folder name >

# 簽名
LOCAL_CERTIFICATE := < desired key >

# 指定 src 目錄 
LOCAL_SRC_FILES := < app apk filename >

LOCAL_MODULE_CLASS := APPS

# 該模組的字尾,不用定義
#LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)

include $(BUILD_PREBUILT)

解釋:

  • LOCAL_PATH := $(call my-dir)

每個 Android.mk 檔案必須以定義 LOCAL_PATH 為開始,它用於在開發 tree 中查詢原始檔。

  • include $(CLEAR_VARS)

CLEAR_VARS 變數由 Build System 提供,並指向一個指定的 GNU Makefile,由它負責清理很多 LOCAL_xxx。

例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES 等等,但不清理 LOCAL_PATH。

  • LOCAL_MODULE_TAGS := user eng tests optional

可選定義,表示在什麼版本情況下編譯該版本,預設 optional

user: 指該模組只在 user 版本下才編譯
eng: 指該模組只在 eng 版本下才編譯
tests: 指該模組只在 tests 版本下才編譯
optional:指該模組在所有版本下都編譯
  • LOCAL_MODULE

模組名,可不用定義,預設 = $(LOCAL_PACKAGE_NAME),不能和既有模組相同,如果該變數未設定,則使用 LOCAL_PACKAGE_NAME,如果再沒有,就會編譯失敗。

  • LOCAL_CERTIFICATE

在什麼情況下簽名。

testkey:普通 APK,預設情況下使用。

platform:該 APK 完成一些系統的核心功能。經過對系統中存在的資料夾的訪問測試,
這種方式編譯出來的 APK 所在程式的 UID 為 system,可以參見 Settings。

shared:該 APK 需要和 home/contacts 程式共享資料,可以參見 Launcher。

media:該 APK 是 media/download 系統中的一環,可以參見 Gallery。

  • LOCAL_MODULE_CLASS

指定模組的型別,可不用定義。

# 編譯 apk 檔案
LOCAL_MODULE_CLASS := APPS

# 編譯 jar 包
LOCAL_MODULE_CLASS := JAVA_LIBRAYIES

# 定義動態庫檔案
LOCAL_MODULE_CLASS := SHARED_LIBRAYIES

# 編譯可執行檔案
LOCAL_MODULE_CLASS := EXECUTABLES
  • include $(BUILD_PACKAGE)

表示生成一個 apk,它可以是多種型別

BUILD_PACKAGE(既可以編apk,也可以編資源包檔案,但是需要指定LOCAL_EXPORT_PACKAGE_RESOURCES:=true)

BUILD_JAVA_LIBRARY(java共享庫)

BUILD_STATIC_JAVA_LIBRARY(java靜態庫)

BUILD_EXECUTABLE(執行檔案)

BUILD_SHARED_LIBRARY(native共享庫)

BUILD_STATIC_LIBRARY(native靜態庫)

完整示例

***.apk 對應如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE := ***

# 系統簽名
LOCAL_CERTIFICATE := PRESIGNED

LOCAL_SRC_FILES := $(LOCAL_MODULE).apk

LOCAL_MODULE_CLASS := APPS

#LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)

include $(BUILD_PREBUILT)

更改 device.mk 檔案

AOSP/build/target/board/lunch的版本/device.mk 檔案,我編的是 aosp_x86-eng,所以增加或者更新 AOSP/build/target/board/generic_x86/device.mk:

PRODUCT_PACKAGES += \
        *** \

使用 mmm 命令來編譯指定的模組:

mmm packages/apps/***

編譯好模組後,還要重新打包一下 system.img 檔案:

make snod 

完成後就可以燒錄了。

問題

1、如何將 APK 預置到 system/priv-app 裡?

加入 priv-app 方法:在 Android.mk 中增加 LOCAL_PRIVILEGED_MODULE := true

預置有原始碼 APP

預置有原始碼 APP 比預置 APK 要麻煩很多,可能會涉及 jar 包和 so 庫等。現在基本都是基於 Android Studio 的專案 MyTestProject1,我們先在 AOSP/packages/apps 新建名為 MyTestProject2 的資料夾,在新建 MyTestProject2/libs、MyTestProject2/res、MyTestProject2/src,分別將 MyTestProject1 下 jar 包和 so 庫拷到 MyTestProject2/libs 和 MyTestProject2/libs/armeabi,將 MyTestProject1/app/src/main/res 拷到 MyTestProject2/res,將 MyTestProject1/app/src/main/java 下檔案拷到 MyTestProject2/src 下。

引用第三方 jar 包

假設,我們當前目錄下的 libs 有 AndroidUtil.jar包,我們想引用它,需要做兩個步驟:

第一步、 宣告我們 jar 包所在的目錄

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := AndroidUtil:libs/AndroidUtil.jar

這行程式碼的意思大概可以理解成這樣,宣告一個變數 AndroidUtil,它的 value 是 libs/AndroidUtil.jar

include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := AndroidUtil:libs/AndroidUtil.jar
include $(BUILD_MULTI_PREBUILT)

第二步、 引用我們宣告 jar 包的變數

include $(CLEAR_VARS)
# 省略其他
LOCAL_STATIC_JAVA_LIBRARIES := \
        AndroidUtil
# 省略其他
include $(BUILD_PACKAGE)

引用 so 庫

假設,我們當前目錄下的 libs/armeabi 有 libBaiduMapSDK1.so、libBaiduMapSDK1.so,libs/arm64-v8a 有 libBaiduMapSDK1.so、libBaiduMapSDK1.so,我們想引用它,有兩種方法,可以在根目錄 Android.mk 引用 so 庫,也可以在 libs 下再建個 Android.mk 配置好 so 庫,然後 include,推薦第二種方式。

libs/Android.mk

#====================================================
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libBaiduMapSDK1
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_SRC_FILES_arm :=libs/armeabi/$(LOCAL_MODULE).so
LOCAL_SRC_FILES_arm64 :=libs/arm64-v8a/$(LOCAL_MODULE).so
LOCAL_MODULE_TARGET_ARCHS:= arm arm64
LOCAL_MULTILIB := both
include $(BUILD_PREBUILT)
#====================================================

#====================================================
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libBaiduMapSDK2
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_SRC_FILES_arm :=libs/armeabi/$(LOCAL_MODULE).so
LOCAL_SRC_FILES_arm64 :=libs/arm64-v8a/$(LOCAL_MODULE).so
LOCAL_MODULE_TARGET_ARCHS:= arm arm64
LOCAL_MULTILIB := both
include $(BUILD_PREBUILT)

引用 so 庫

include $(CLEAR_VARS)
# 省略其他
LOCAL_JNI_SHARED_LIBRARIES :=  \
        libBaiduMapSDK1 \
        libBaiduMapSDK2
# 省略其他
include $(BUILD_PACKAGE)

##########引用第三方 so 庫##########
include $(LOCAL_PATH)/libs/Android.mk

完整示例

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_PACKAGE_NAME := TestName

LOCAL_CERTIFICATE := platform

# 引入系統資原始檔
LOCAL_USE_AAPT2 := true

# Java檔案
LOCAL_SRC_FILES := $(call all-java-files-under, src)

# 資原始檔,可選定義,推薦不定義
#LOCAL_RESOURCE_DIR = \
#        $(LOCAL_PATH)/res \
#        frameworks/support/v7/appcompat/res \
#        frameworks/support/design/res

# 可以使用系統 hide api
LOCAL_PRIVATE_PLATFORM_APIS := true

# 匯入系統依賴
LOCAL_STATIC_ANDROID_LIBRARIES := \
        android-support-design \
        android-support-v4 \
        android-support-v7-appcompat \
        android-support-v7-recyclerview 

LOCAL_STATIC_JAVA_LIBRARIES := \
        AndroidUtil

LOCAL_JNI_SHARED_LIBRARIES :=  \
        libBaiduMapSDK1 \
        libBaiduMapSDK2

# R資源生成別名,--extra-packages 是為資原始檔設定別名:意思是通過該應用包名+R,com.android.test1.R 和 com.android.test2.R 都可以訪問到資源
LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages android.support.v4
LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
LOCAL_AAPT_FLAGS += --extra-packages android.support.design
LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.recyclerview

# 制定編譯的工程,不要使用程式碼混淆的工具進行程式碼混淆
LOCAL_PROGUARD_ENABLED := disabled
# 指定不需要混淆的native方法與變數的proguard.flags檔案
LOCAL_PROGUARD_FLAG_FILES := proguard.flags

include $(BUILD_PACKAGE)


##########引用第三方 jar 包##########
include $(CLEAR_VARS)

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := AndroidUtil:libs/AndroidUtil.jar

include $(BUILD_MULTI_PREBUILT)

##########引用第三方 so 庫##########
include $(LOCAL_PATH)/libs/Android.mk

問題

1、LOCAL_PRIVATE_PLATFORM_APIS 和 LOCAL_SDK_VERSION 有什麼區別?

LOCAL_PRIVATE_PLATFORM_APIS := true
設定後,會使用 sdk 的 hide 的 api 來編譯。

LOCAL_SDK_VERSION 這個編譯配置,就會使編譯的應用不能訪問 hide 的 api,有時一些系統的 class 被 import 後編譯時說找不到這個類,就是這個原因造成的。

2、如果直接用 mmm 編譯然後 adb install -r xxx.apk 大概會出現如下錯誤:

Failed to install out/target/product/p212/system/app/xxx/xxx.apk: Failure [INSTALL_FAILED_INVALID_APK: Package couldn't be installed in /data/app/com.droidlogic.mboxlauncher-1: Package /data/app/com.droidlogic.mboxlauncher-1/base.apk code is missing]

解決方法:

在對應 app 的 Android.mk 檔案中加入

LOCAL_DEX_PREOPT := false

關閉 dex 優化來提高除錯過程,把編譯後的 APK 直接替換安裝 adb install -r XXX.apk,不然 APK 得 Push 到 system/app,重啟裝置。

3、在 Android Studio Gradle 方式中通過 implementation 方式載入的三方庫,並沒有下載 jar 檔案放到 libs 資料夾下啊,該如何整合?

其實 jar 包有被下載到專案的 External Libraries 目錄下,找到引用的 jar 包,點右鍵 Show in Files,就能得到了 jar 包的檔案地址,然後把它拷到 libs 資料夾下,就能像別的 jar 包一樣處理了。

另外在 External Libraries 目錄還能看到隱藏的 jar,比如 retrofit,其實它有引用 okhttp,okhttp 又引用了 okio,這些也是需要的,一併拷到 libs 資料夾下。

4、第三方無法定位?

第三方定位如百度、高德,申請 SDK 時會需要填寫包名和打包簽名等資訊,如何正確地預置原始碼 APP 可以定位,除了配置LOCAL_CERTIFICATE := platform使用系統的簽名,還得在專案的AndroidMainfest.xml 根節點配置android:sharedUserId="android.uid.system"

相關文章