installExistingPackageAsUser方法的分析

Android应用双开实现中用到installExistingPackageAsUser方法,下面分析下该方法的流程

public int installExistingPackageAsUser(String packageName, int userId) {
        //检查安装app权限
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
                null);
        PackageSetting pkgSetting;
        final int uid = Binder.getCallingUid();
        enforceCrossUserPermission(uid, userId,
                true /* requireFullPermission */, true /* checkShell */,
                "installExistingPackage for user " + userId);
        //是否被限制安装app
        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
            return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
        }

        long callingId = Binder.clearCallingIdentity();
        try {
            boolean installed = false;

            // writer
      //写入相关xml文件
            synchronized (mPackages) {
                pkgSetting = mSettings.mPackages.get(packageName);
                if (pkgSetting == null) {
                    return PackageManager.INSTALL_FAILED_INVALID_URI;
                }
                if (!pkgSetting.getInstalled(userId)) {
                    pkgSetting.setInstalled(true, userId);
                    pkgSetting.setHidden(false, userId);
                    mSettings.writePackageRestrictionsLPr(userId);
                    installed = true;
                }
            }

            if (installed) {
                if (pkgSetting.pkg != null) {
                    synchronized (mInstallLock) {
                        // We don't need to freeze for a brand new install
                        prepareAppDataAfterInstallLIF(pkgSetting.pkg);
                    }
                }
                //发送ACTION_PACKAGE_ADDED广播
                sendPackageAddedForUser(packageName, pkgSetting, userId);
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }

        return PackageManager.INSTALL_SUCCEEDED;
    }
由于app已经安装,所以该方法实际上是创建app在新user目录下的相关数据

  private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) {
        final PackageSetting ps;
    //KernelMapping对应的文件夹/config/sdcardfs,sdcardfs是用于app文件访问的文件系统
    //sdcardfs详细可见https://www.zhihu.com/question/24112546
        synchronized (mPackages) {
            ps = mSettings.mPackages.get(pkg.packageName);
            mSettings.writeKernelMappingLPr(ps);
        }

        final UserManager um = mContext.getSystemService(UserManager.class);
        UserManagerInternal umInternal = getUserManagerInternal();
        for (UserInfo user : um.getUsers()) {
            ...
                prepareAppDataLIF(pkg, user.id, flags);
            ...
        }
    }
两个工作:一是处理sdcardfs,二是prepareAppDataLIF

    private void prepareAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
        if (pkg == null) {
            Slog.wtf(TAG, "Package was null!", new Throwable());
            return;
        }
        prepareAppDataLeafLIF(pkg, userId, flags);
        //childPackages是AndroidManifest中package属性指定的,不过sdk文档中并无package属性的说明。
    //在PackageParser.java代码中还可以看到一些sdk中没有说明的属性。
        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
        for (int i = 0; i < childCount; i++) {
            prepareAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
        }
    }
继续看prepareAppDataLeafLIF

private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
        ...

        try {
      //createAppData之前已详细分析过
            mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                    appId, app.seinfo, app.targetSdkVersion);
        } catch (InstallerException e) {
           ...
        }

        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
      //获取app data inode然后存储,代码省略
           ...
        }

        prepareAppDataContentsLeafLIF(pkg, userId, flags);
    }
主要工作还是通过installd创建app相关目录,最后一行prepareAppDataContentsLeafLIF,从名字看不出是啥作用

    private void prepareAppDataContentsLeafLIF(PackageParser.Package pkg, int userId, int flags) {
        ...

        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
            // Create a native library symlink only if we have native libraries
            // and if the native libraries are 32 bit libraries. We do not provide
            // this symlink for 64 bit libraries.
            //创建32位native库的连接,向前兼容
            if (app.primaryCpuAbi != null && !VMRuntime.is64BitAbi(app.primaryCpuAbi)) {
                final String nativeLibPath = app.nativeLibraryDir;
                try {
                    mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
                            nativeLibPath, userId);
                } catch (InstallerException e) {
                    Slog.e(TAG, "Failed to link native for " + packageName + ": " + e);
                }
            }
        }
    }
代码就一个功能,处理64位机器对32位库的兼容性问题。linkNativeLibraryDirectory实际调用了installd的linklib方法
 int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId)
{
   ...

    if (symlink(asecLibDir, libsymlink) < 0) {
        ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir,
                strerror(errno));
        rc = -errno;
        goto out;
    }

    ...
}
使用了linux C库标准方法symlink建立符号连接

ps:createAppData在创建user,开启user和安装app的时候都会调用:创建user的时候只是针对system app,因为对于所有user来说system app是必须的;安装app的时候是针对要安装的app,这个很好理解;最后开启user的时候从代码看是会有一个特殊的合并system app data目录的操作(system app对所有用户来说是一样的,所以没必要搞多个目录占地方)。





猜你喜欢

转载自blog.csdn.net/firedancer0089/article/details/78354738
今日推荐