installExistingPackageAsUser方法的分析

来源:互联网 发布:淘宝商家发货信息 编辑:程序博客网 时间:2024/06/08 10:41

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对所有用户来说是一样的,所以没必要搞多个目录占地方)。