0. 错误 log

Android 8.1 系统。

1. E PackageManager: Failed to create app data for com.xxxx.iot.service, but trying to recover: com.android.server.pm.Installer$InstallerException: java.lang.NullPointerException: Attempt to invoke interface method 'long android.os.IInstalld.createAppData(java.lang.String, java.lang.String, int, int, int, java.lang.String, int)' on a null object reference
2. W PackageManager: com.android.server.pm.Installer$InstallerException: java.lang.NullPointerException: Attempt to invoke interface method 'void android.os.IInstalld.destroyAppData(java.lang.String, java.lang.String, int, int, long)' on a null object reference
3. D PackageManager: Recovery failed!
4. W PackageManager: Failed to migrate com.xxxx.iot.service: java.lang.NullPointerException: Attempt to invoke interface method 'void android.os.IInstalld.migrateAppData(java.lang.String, java.lang.String, int, int)' on a null object reference

1. SystemServer 连接 installd

从 log 中看出异常原因是调用 IInstalld 方法的时候抛出了空指针异常,IInstalld 其实就是 installd 在 SystemServer 中的代理,Java 层对应的 Service 是 Installer,在 systemServer 的 startBootstrapServices() 方法中连接 installd。

// frameworks/base/services/java/com/android/server/SystemServer.java
/**
 * Starts the small tangle of critical services that are needed to get
 * the system off the ground.  These services have complex mutual dependencies
 * which is why we initialize them all in one place here.  Unless your service
 * is also entwined in these dependencies, it should be initialized in one of
 * the other functions.
 */
private void startBootstrapServices() {
    ...
    Installer installer = mSystemServiceManager.startService(Installer.class);
    mActivityManagerService = mSystemServiceManager.startService(
            ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
    mActivityManagerService.setInstaller(installer);
    ...
}

Android 8.0 之前 SystemServer 与 installd 是通过 socket 连接的,在连接的时候会阻塞等待 installd 连接成功。

// frameworks/base/core/java/com/android/internal/os/InstallerConnection.java
public void waitForConnection() {
    for (;;) {
        try {
            execute("ping");
            return;
        } catch (InstallerException ignored) {}
        Slog.w(TAG, "installd not ready");
        SystemClock.sleep(1000);
}}

但是在 Android 8.0 把 installd 的连接方式改成了 binder,且不会阻塞等待 installd 连接成功,而是每隔一秒重试,直到连接成功。

// frameworks/base/services/core/java/com/android/server/pm/Installer.java
private void connect() {
    IBinder binder = ServiceManager.getService("installd");
    if (binder != null) {
        try {
            binder.linkToDeath(new DeathRecipient() {
                @Override
                public void binderDied() {
                    Slog.w(TAG, "installd died; reconnecting");
                    connect();
                }
            }, 0);
        } catch (RemoteException e) {
            binder = null;
        }
    }
    if (binder != null) {
        mInstalld = IInstalld.Stub.asInterface(binder);
    } else {
        Slog.w(TAG, "installd not found; trying again");
        // 一秒后重试
        BackgroundThread.getHandler().postDelayed(() -> {
            connect();
        }, DateUtils.SECOND_IN_MILLIS);
}}

2. 初始化 PackageManagerService

PackageManagerService(下面称为 PMS)的初始化也是在 startBootstrapServices() 方法中,且是在创建 Installer 对象之后。调用 PMS 的静态方法 main() 进行初始化,参数传入了 Installer 对象。

// frameworks/base/services/java/com/android/server/SystemServer.java
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
        mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

PMS 的初始化中会扫描 APK,并为应用创建私有目录。扫描 APK 时如果没有需要清除的 APK 则不会调用 installd,所以我们关注给应用创建私有目录的过程,它是调用 reconcileAppsDataLI() 方法。

// frameworks/base/services/core/java/com/android/server/pm/[PackageManagerService.java](<http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java>)
// Prepare storage for system user really early during boot,
// since core system apps like SettingsProvider and SystemUI
// can't wait for user to start
final int storageFlags;
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
    storageFlags = StorageManager.FLAG_STORAGE_DE;
} else {
    storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
}
List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
        UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
        true /* onlyCoreApps */);

FLAG_STORAGE_CE 指的是 /data/user/0/,即 /data/data,该目录只能在解锁屏幕后才能访问。而 FLAG_STORAGE_DE 指的是 /data/user_de/0,该目录在有屏幕锁的时候也能访问,参考 Android 官网关于 DirectBoot 的介绍。

reconcileAppsDataLI() 方法中判断 ceDirdeDir 中的应用是否是已知的(参考系统启动时 PMS 检查 APK 有效性分析)且已安装的,如果不是的话调用 installd 的 destroyAppData() 函数删除对应私有目录。

之后对于已安装的应用调用 prepareAppDataAndMigrateLIF() 方法。

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
if (ps.getInstalled(userId)) {
    prepareAppDataAndMigrateLIF(ps.pkg, userId, flags, migrateAppData);
    preparedCount++;
}