前情提要
关于预装APK相关知识可参考
遇到问题
当我按照 Android O、P、Q 版本如何预装 APK 一文将 PMS 中 patch 回退,并在 Android.mk 中指定
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) 输出到 data/app 目录下,编译后apk也确实位于
data/app/中,而且 userdata.img 大小也明显变大了,但是当烧写完成后,开机并不能正常启动,而是
自动进入了 recovery 界面,且界面显示
Can’t load Android system. You’r data may be corrupt.If you continue to get this message, you may
need to perform a factory data reset and erase all user data stored on this devices.
按照提示我进行了恢复出厂操作,恢复出厂后能正常开机。但是预装的app没了,很明显预装app会没了,因为我们
预装app就在 data 分区下,恢复出厂将 data 分区格式化了,所以 app 没了,而且系统能正常开机了。那么看来问题
就在 data 分区上了,难道 AndroidQ 不允许将apk预装到 data 分区了吗?
解决办法
跳过 data 分区下 app 目录加密策略读取和设置即可
system/extras/libfscrypt/fscrypt_init_extensions.cpp
@@ -88,7 +88,7 @@ int fscrypt_set_directory_policy(const char* dir) "media", "data", "user", "user_de", "apex", "preloads", "app-staging", - "gsi", + "gsi","app", }; std::string prefix = "/data/"; for (const auto& d: directories_to_exclude) { if ((prefix + d) == dir) { LOG(INFO) << "Not setting policy on " << dir; return 0; } } return set_system_de_policy_on(dir);
分析过程
由上面的问题现象开始定位到 data 分区,问了朋友后说可能和分区格式有关系,userdata 改成f2sf才能内置东西,
于是照着 Android 如何将 data 分区格式由 ext4 转为 f2fs 一文修改后重新编译,烧写后发现问题依旧,还是提示
一样的问题,回厂进入系统后 adb shell 查看 data 分区格式确实已经变成 f2fs 了,这么说和 ext4、f2fs 没关系,但
如果需要过 GMS 的话,确实需要修改 data 格式为 f2fs。
好吧,看来这条路走不通了。那就只能启用终极奥义,看串口Log了,乘着硬件小哥飞串口线的时间,我们
先来把系统的串口 Log 打开。
kernel-4.9/kernel/printk/printk.c
@@ -75,7 +75,7 @@ module_param_named(disable_uart, printk_disable_uart, int, 0644); bool mt_get_uartlog_status(void) { if (printk_disable_uart == 1) - return false; + return true;//false; else if ((printk_disable_uart == 0) || (printk_disable_uart == 2)) return true; return true; @@ -94,7 +94,7 @@ void mt_disable_uart(void) { /* uart print not always enable */ if ((mt_need_uart_console != 1) && (printk_disable_uart != 2)) - printk_disable_uart = 1; + printk_disable_uart = 0; } void mt_enable_uart(void) {
vendor/mediatek/proprietary/bootable/bootloader/lk/app/mt_boot/mt_boot.c
@@ -1233,7 +1233,7 @@ int boot_linux_fdt(void *kernel, unsigned *tags, #endif cmdline_append("printk.disable_uart=0"); else - cmdline_append("printk.disable_uart=1"); + cmdline_append("printk.disable_uart=0"); break; case BUILD_TYPE_USERDEBUG: @@ -1243,13 +1243,13 @@ int boot_linux_fdt(void *kernel, unsigned *tags, #else (is_meta_log_disable() == 1)) #endif - cmdline_append("printk.disable_uart=1 slub_debug=O"); + cmdline_append("printk.disable_uart=0 slub_debug=O"); #ifdef LOG_STORE_SUPPORT else if (boot_ftrace && g_boot_arg->log_dynamic_switch == 0) #else else if (boot_ftrace) #endif - cmdline_append("printk.disable_uart=1 slub_debug=-"); + cmdline_append("printk.disable_uart=0 slub_debug=-"); else cmdline_append("printk.disable_uart=0"); break; @@ -1257,7 +1257,7 @@ int boot_linux_fdt(void *kernel, unsigned *tags, case BUILD_TYPE_ENG: if ((g_boot_mode == META_BOOT) && is_meta_log_disable && (is_meta_log_disable() == 1)) - cmdline_append("printk.disable_uart=1 slub_debug=O"); + cmdline_append("printk.disable_uart=0 slub_debug=O"); else cmdline_append("printk.disable_uart=0 ddebug_query=\"file *mediatek* +p ; file *gpu* =_\""); break;
重新编译烧写开机得到如下串口日志
[ 21.147710] <2>.(2)[1:init]init 15: Setting policy on /data/vendor [ 21.148630] <2>.(2)[1:init]init 15: Policy for /data/vendor set to 579af1fc730f2e34 modes 1/4 [ 21.151349] <2>.(2)[1:init]init 19: ReapLogF PropSet [apexd.status]=[ready]21.147147 Done [ 21.152406] <2>.(2)[1:init]init 15: Not setting policy on /data/vendor_ce [ 21.154164] <2>.(2)[1:init]init 15: Not setting policy on /data/vendor_de [ 21.159953] <2>.(2)[1:init]init 3: Not setting policy on /data/data [ 21.162020] <2>.(2)[1:init]init 3: Setting policy on /data/app-private [ 21.163054] <2>.(2)[1:init]init 3: Policy for /data/app-private set to 579af1fc730f2e34 modes 1/4 [ 21.165331] <2>.(2)[1:init]init 3: Setting policy on /data/app-ephemeral [ 21.166299] <2>.(2)[1:init]init 3: Policy for /data/app-ephemeral set to 579af1fc730f2e34 modes 1/4 [ 21.169022] <2>.(2)[1:init]init 3: Setting policy on /data/app-asec [ 21.169960] <2>.(2)[1:init]init 3: Policy for /data/app-asec set to 579af1fc730f2e34 modes 1/4 [ 21.172106] <2>.(2)[1:init]init 3: Setting policy on /data/app-lib [ 21.173001] <2>.(2)[1:init]init 3: Policy for /data/app-lib set to 579af1fc730f2e34 modes 1/4 [ 21.176232] <2>.(2)[1:init]init 15: Setting policy on /data/app [ 21.177618] <2>.(2)[1:init]init 15: Failed to get encryption policy for /data/app: No data available [ 21.202980] <3>.(3)[384:logd.auditd]type=1400 audit(1262304028.716:5): avc: denied { associate } for comm="init" name="boot_boost" scontext=u:object_r:proc_perfmgr:s0 tcontext=u:object_r:proc:s0 tclass=filesystem permissive=1 [ 21.205523] <3>.(3)[384:logd.auditd]type=1400 audit(1262304029.180:6): avc: denied { dac_override } for comm="ls" capability=1 scontext=u:r:toolbox:s0 tcontext=u:r:toolbox:s0 tclass=capability permissive=1 [ 21.205797] <3>.(2)[1:init]init 15: Setting 579af1fc policy on /data/app failed! [ 21.205825] <3>.(2)[1:init]init 15: Rebooting into recovery [ 21.209288] <3>.(2)[1:init]init 15: Received sys.powerctl='reboot,recovery' from pid: 1 (/system/bin/init) [ 21.209678] <3>.(2)[1:init]init 3: ReapLogF PropSet [sys.powerctl]=[reboot,recovery]21.206530 Done [ 21.209689] <3>.(2)[1:init]init 15: Clear action queue and start shutdown trigger [ 21.209819] <3>.(2)[1:init]init 15: processing action (shutdown_done) from (<Builtin Action>:0) [ 21.209851] <3>.(2)[1:init]init 15: Reboot start, reason: reboot,recovery, rebootTarget: recovery [ 21.215330] <3>.(3)[384:logd.auditd]type=1400 audit(1262304029.180:6): avc: denied { dac_override } for comm="ls" capability=1 scontext=u:r:toolbox:s0 tcontext=u:r:toolbox:s0 tclass=capability permissive=1 [ 21.217674] <3>.(3)[384:logd.auditd]type=1400 audit(1262304029.180:7): avc: denied { read } for comm="ls" name="app" dev="mmcblk0p36" ino=6 scontext=u:r:toolbox:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=1 [ 21.220472] <3>.(3)[384:logd.auditd]type=1400 audit(1262304029.180:7): avc: denied { read } for comm="ls" name="app" dev="mmcblk0p36" ino=6 scontext=u:r:toolbox:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=1 [ 21.223103] <3>.(3)[384:logd.auditd]type=1400 audit(1262304029.180:8): avc: denied { open } for comm="ls" path="/data/app" dev="mmcblk0p36" ino=6 scontext=u:r:toolbox:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=1 [ 21.244124] <2>.(2)[1:init]init 3: ReapLogF PropSet [persist.sys.boot.reason]=[recovery]21.207330 Done [ 21.245320] <2>.(2)[1:init]init 15: Shutdown timeout: 6000 ms [ 21.246820] <2>.(2)[1:init]init 19: starting service 'blank_screen'... [ 21.251021] <2>.(2)[1:init]init 19: starting service 'light-hal-2-0'... [ 21.254702] <2>.(2)[1:init]init 19: terminating init services
找到其中关键日志 Rebooting into recovery,往上看到 Setting 579af1fc policy on /data/app failed!
嗯,和 data 相关的东西出现了。Failed to get encryption policy for /data/app: No data available
通过搜索 Failed to get encryption policy 找到文件 system/extras/libfscrypt/fscrypt.cpp
static bool fscrypt_policy_get(const char *directory, char *policy, size_t policy_length, int contents_encryption_mode, int filenames_encryption_mode) { .... fscrypt_policy fp; memset(&fp, 0, sizeof(fscrypt_policy)); if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &fp) != 0) { PLOG(ERROR) << "Failed to get encryption policy for " << directory; close(fd); log_ls(directory); return false; } close(fd); if ((fp.version != 0) || (fp.contents_encryption_mode != contents_encryption_mode) || (fp.filenames_encryption_mode != filenames_encryption_mode) || (fp.flags != fscrypt_get_policy_flags(filenames_encryption_mode))) { LOG(ERROR) << "Failed to find matching encryption policy for " << directory; return false; } memcpy(policy, fp.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE); return true; }
应该就是在这里打印的,简单看了下源码,和设置文件加密解密策略有关,开始看到了日志中
Rebooting into recovery 附近一堆 avc: denied,以为和 SELinux 权限有关系,就先挨个把权限都给
补上了,再次编译后开机,呵呵,还是一样的问题,不过日志中已经没有 avc: denied 相关字眼。再仔细品一品
串口日志,既然问题出在读取 /data/app encryption policy 上,那么是不是跳过 /data/app 就行了呢。
回到源码中看下调用流程
fscrypt_policy_get( fscrypt_policy_check() fscrypt_policy_get()
然鹅 fscrypt_policy_get() 的调用在 system/extras/libfscrypt/fscrypt_init_extensions.cpp 中
看到 fscrypt_set_directory_policy() 调用了 fscrypt_policy_get() ,在之前进行了 for 循环判断,如果
传递的文件目录在 vector 中则跳过读取检查,这正是我们所需要的,直接在 vector 中添加 app 目录。
int fscrypt_set_directory_policy(const char* dir) { if (!dir || strncmp(dir, "/data/", 6)) { return 0; } // Special-case /data/media/obb per b/64566063 if (strcmp(dir, "/data/media/obb") == 0) { // Try to set policy on this directory, but if it is non-empty this may fail. set_system_de_policy_on(dir); return 0; } // Only set policy on first level /data directories // To make this less restrictive, consider using a policy file. // However this is overkill for as long as the policy is simply // to apply a global policy to all /data folders created via makedir if (strchr(dir + 6, '/')) { return 0; } // Special case various directories that must not be encrypted, // often because their subdirectories must be encrypted. // This isn't a nice way to do this, see b/26641735 std::vector<std::string> directories_to_exclude = { "lost+found", "system_ce", "system_de", "misc_ce", "misc_de", "vendor_ce", "vendor_de", "media", "data", "user", "user_de", "apex", "preloads", "app-staging", "gsi","app", }; std::string prefix = "/data/"; for (const auto& d: directories_to_exclude) { if ((prefix + d) == dir) { LOG(INFO) << "Not setting policy on " << dir; return 0; } } return set_system_de_policy_on(dir); } static int set_system_de_policy_on(char const* dir) { std::string ref_filename = std::string("/data") + fscrypt_key_ref; std::string policy; if (!android::base::ReadFileToString(ref_filename, &policy)) { LOG(ERROR) << "Unable to read system policy to set on " << dir; return -1; } auto type_filename = std::string("/data") + fscrypt_key_mode; std::string modestring; if (!android::base::ReadFileToString(type_filename, &modestring)) { LOG(ERROR) << "Cannot read mode"; } std::vector<std::string> modes = android::base::Split(modestring, ":"); if (modes.size() < 1 || modes.size() > 2) { LOG(ERROR) << "Invalid encryption mode string: " << modestring; return -1; } LOG(INFO) << "Setting policy on " << dir; int result = fscrypt_policy_ensure(dir, policy.c_str(), policy.length(), modes[0].c_str(), modes.size() >= 2 ? modes[1].c_str() : "aes-256-cts"); if (result) { LOG(ERROR) << android::base::StringPrintf( "Setting %02x%02x%02x%02x policy on %s failed!", policy[0], policy[1], policy[2], policy[3], dir); return -1; } return 0; }
重新编译烧写后成功开机的log,可以看到打印了跳过 /data/app/ 相关
[ 7.101127] <0>.(0)[1:init]init 19: Not setting policy on /data/vendor_de [ 7.106596] <0>.(0)[1:init]init 19: Not setting policy on /data/data [ 7.109088] <0>.(0)[1:init]init 19: Setting policy on /data/app-private [ 7.110129] <0>.(0)[1:init]init 19: Policy for /data/app-private set to e2d0272afa66497a modes 1/4 [ 7.112834] <0>.(0)[1:init]init 19: Setting policy on /data/app-ephemeral [ 7.124062] <0>.(0)[1:init]init 3: Not setting policy on /data/app [ 7.126336] <0>.(0)[1:init]init 3: Setting policy on /data/property [ 7.127449] <0>.(0)[1:init]init 3: Found policy e2d0272afa66497a at /data/property which matches expected valu
可能还存在的问题
我在 userdebug 版本下测试,烧写完成后第一次开机启动正常,app 都存在,再次重启后发现 data 下的 app 被卸载了,抓到对应的日志
2020-06-24 16:23:22.767 1144-1144/system_process V/PackageManager: reconcileAppsData for null u0 0x1 migrateAppData=true 2020-06-24 16:23:22.771 1144-1144/system_process W/PackageManager: Destroying /data/user_de/0/cn.cpe.contacts due to: com.android.server.pm.PackageManagerException: Package cn.cpe.contact is unknown 2020-06-24 16:23:22.780 1144-1144/system_process W/PackageManager: Destroying /data/user_de/0/cn.cpe.broswer due to: com.android.server.pm.PackageManagerException: Package cn.cpe.brosweris unknown 2020-06-24 16:23:22.944 1144-1144/system_process V/PackageManager: reconcileAppsData finished 31 packages 2020-06-24 16:23:23.068 1144-1144/system_process E/PackageManager: There should probably be a verifier, but, none were found 2020-06-24 16:23:25.260 1144-1144/system_process W/PackageManager: Destroying orphaned/data/app/CeContact 2020-06-24 16:23:25.270 1144-1144/system_process W/PackageManager: Destroying orphaned/data/app/CeBroswer PackageManager: System package cn.cpe.contact no longer exists; it's data will be wiped 2020-06-30 15:23:16.505 1126-1126/system_process W/PackageManager: System package cn.cpe.broswer no longer exists; it's data will be wiped
PackageManager 中重启后进行了再次检查
我在 user 版本下验证并未出现这个现象,所以就不再深究了,感兴趣的可以去找 PackageManagerService 对应位置分析