前情提要
关于预装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。
![Nd5cDK.png](https://s1.ax1x.com/2020/06/24/Nd5cDK.png)
好吧,看来这条路走不通了。那就只能启用终极奥义,看串口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 value
可能还存在的问题
我在 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 对应位置分析