遇到问题

当我按照 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 mayneed 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。

android recovery解密data分区 安卓10解密data分区_data预装

好吧,看来这条路走不通了。那就只能启用终极奥义,看串口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 对应位置分析