1. 程式人生 > >[RK3288][Android6.0] 除錯筆記 --- 系統自帶預置第三方APK方法

[RK3288][Android6.0] 除錯筆記 --- 系統自帶預置第三方APK方法

Platform: RK3288
OS: Android 6.0
Kernel: 3.10.92

Rockchip預設提供了機制來預置第三方APK, 方法很簡單:
1. 在device/rockchip/rk3288建立preinstall目錄(如果要可解除安裝,那就建立preinstall_del目錄)
2. 將你要預安裝的APK放進此目錄即可

下面看下實現原理過程:
device/rockchip/common/device.mk中有:

ifneq ($(strip $(TARGET_DEVICE)), )
    TARGET_DEVICE_DIR=$(shell test -d device && find device -maxdepth 4
-path '*/$(TARGET_DEVICE)/BoardConfig.mk') TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(TARGET_DEVICE_DIR))) $(shell python device/rockchip/common/auto_generator.py $(TARGET_DEVICE_DIR) system_app) $(shell python device/rockchip/common/auto_generator.py $(TARGET_DEVICE_DIR) preinstall) $(shell python device/rockchip/common/auto_generator.py $(
TARGET_DEVICE_DIR) preinstall_del) -include $(TARGET_DEVICE_DIR)/system_app/preinstall.mk -include $(TARGET_DEVICE_DIR)/preinstall/preinstall.mk -include $(TARGET_DEVICE_DIR)/preinstall_del/preinstall.mk endif

auto_generator.py是個python指令碼,用於生成Android.mk和preinstall.mk檔案,

def main(argv):
    preinstall_dir = os.path.join(argv[1
] + '/' + argv[2]) if os.path.exists(preinstall_dir): #Use to define modules for install makefile_path = preinstall_dir + '/Android.mk' #Use to include modules include_path = preinstall_dir + '/preinstall.mk' if os.path.exists(makefile_path): os.remove(makefile_path) if os.path.exists(include_path): os.remove(include_path) makefile = file(makefile_path, 'w') includefile = file(include_path, 'w') makefile.write("LOCAL_PATH := $(my-dir)\n\n") for root, dirs, files in os.walk(preinstall_dir): for file_name in files: p = re.compile(r'\S*(?=.apk\b)') found = p.search(file_name) if found: makefile.write(templet %(found.group(), argv[2])) includefile.write('PRODUCT_PACKAGES += %s\n' %found.group()) makefile.close() includefile.close()

Android.mk用於制定編譯規則,如我在preinstall目錄下放了個AVSourceTester.apk,那麼生成的檔案內容是

LOCAL_PATH := $(my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := AVSourceTester
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_PATH := $(TARGET_OUT)/preinstall
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)

preinstall.mk內容如下:
PRODUCT_PACKAGES += AVSourceTester

編譯系統之後,生成路徑是
out/target/product/rk3288/system/preinstall/AVSourceTester/AVSourceTester.apk

系統開機之後會呼叫copyDirectory()@PackageManagerService.java

  File preinstallAppDir = new File(Environment.getRootDirectory(), "preinstall");
            File preinstallAppDelDir = new File(Environment.getRootDirectory(),
                    "preinstall_del");
            if ((!SystemProperties.getBoolean("persist.sys.preinstalled", false)) &&
                (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt", null)))) {
                // mPreInstallObserver = new AppDirObserver(
                // mPreinstallAppDir.getPath(), OBSERVER_EVENTS, false);
                // mPreInstallObserver.startWatching();

                if (preinstallAppDir.exists()) {
                    // scanDirLI(mPreinstallAppDir, 0, scanMode, 0);
                    copyPackagesToAppInstallDir(preinstallAppDir);
                }

                // mPreInstallDelObserver = new AppDirObserver(
                // mPreinstallAppDelDir.getPath(), OBSERVER_EVENTS, false);
                // mPreInstallDelObserver.startWatching();
                if (preinstallAppDelDir.exists()) {
                    copyPackagesToAppInstallDir(preinstallAppDelDir);
                    deletePreinstallDir(preinstallAppDelDir);
                }
                SystemProperties.set("persist.sys.preinstalled", "1");
            }//$_rockchip_$_modify_end

關鍵函式是copyPackagesToAppInstallDir(),它會把preinstall目錄下的安裝檔案copy到安裝目錄。
這樣安裝就成功了。

安裝preinstall和preinstall_del的區別在於後者在安裝完之後會刪除系統目錄下的apk,因此要是做了恢復出廠設定或者解除安裝動作,那就不能恢復了。

刪除函式是deletePreinstallDir(),通過init中的ctl命令實現。

    private void deletePreinstallDir(File dir) {
        String[] files = dir.list();
        if (files != null) {
            Slog.d(TAG, "Ready to cleanup preinstall");
            SystemProperties.set("ctl.start", "preinst_clr");
        }
    }

不過在source code中並沒有找到preinst_clr這個service,可以在init.rc中自己新增下,
參考的是 Nu3001/device_rockchip_rksdk

service preinst_clr /system/bin/preinstall_cleanup.sh
    disabled
    oneshot

preinstall_cleanup.sh這個檔案預設是有的,本質是直接刪除apk。

#!/system/bin/sh
log -t PackageManager "Start to clean up /system/preinstall_del/"
mount -o rw,remount -t ext4 /system
rm system/preinstall_del/*.*
mount -o ro,remount -t ext4 /system