virtualapp安装应用流程源码分析

virtualapp,安装,应用,流程,源码,分析 · 浏览次数 : 22

小编点评

**AppManagerService.java** 该类主要负责以下操作: 1. 解析和创建 `VPackage` 对象,解析虚拟应用 apk 文件。 2. 创建新的安装目录和 so 安装目录。 3. 判断应用程序是否已经安装,如果是外部安装,则复制原应用的 lib 下的库文件到新目录中。 4. 生成新包安装的配置信息,用于持久化和后续查询。 5. 在 installPackage 方法中,创建 `PackageSetting` 对象,存储虚拟应用的信息,并保存到硬盘中。 6. 在 appInstallationCompleted 方法中,根据应用的安装状态,广播事件。 **核心逻辑:** * 解析和创建 `VPackage` 对象,这是一个内部类, responsible for parsing and extracting information from the APK package. * 创建新的安装目录和 so 安装目录。 * 判断应用程序是否已经安装,如果是外部安装,则复制原应用的 lib 下的库文件。 * 生成新包安装的配置信息。 * 在 appInstallationCompleted 方法中,根据应用的安装状态,广播事件。 **其他信息:** * `NativeLibraryHelperCompat` 是一个工具类,用于复制 Native 库文件。 * `PackageCacheManager` 是一个内部类,负责缓存 APK 包数据。 * `PackageSetting` 是一个类,存储虚拟应用的信息,包括 appPath、libPath、packageName、appId 等。

正文

1. HomeActivity 为处理的入口

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && data != null) {
            List<AppInfoLite> appList = data.getParcelableArrayListExtra(VCommends.EXTRA_APP_INFO_LIST);
            if (appList != null) {
                for (AppInfoLite info : appList) {
                    mPresenter.addApp(info);
                }
            }
        }
    }

调用了 mPresenter.addApp, 这里还是使用了一个MVP的设计模式,对应的是HomePresenterImpl.java

 @Override
    public void addApp(AppInfoLite info) {
        class AddResult {
            private PackageAppData appData;
            private int userId;
            private boolean justEnableHidden;
        }
        AddResult addResult = new AddResult();
        VUiKit.defer().when(() -> {
            InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);
            addResult.justEnableHidden = installedAppInfo != null;
            if (addResult.justEnableHidden) {
                int[] userIds = installedAppInfo.getInstalledUsers();
                int nextUserId = userIds.length;
                /*
                  Input : userIds = {0, 1, 3}
                  Output: nextUserId = 2
                 */
                for (int i = 0; i < userIds.length; i++) {
                    if (userIds[i] != i) {
                        nextUserId = i;
                        break;
                    }
                }
                addResult.userId = nextUserId;

                if (VUserManager.get().getUserInfo(nextUserId) == null) {
                    // user not exist, create it automatically.
                    String nextUserName = "Space " + (nextUserId + 1);
                    VUserInfo newUserInfo = VUserManager.get().createUser(nextUserName, VUserInfo.FLAG_ADMIN);
                    if (newUserInfo == null) {
                        throw new IllegalStateException();
                    }
                }
                boolean success = VirtualCore.get().installPackageAsUser(nextUserId, info.packageName);
                if (!success) {
                    throw new IllegalStateException();
                }
            } else {
                InstallResult res = mRepo.addVirtualApp(info);
                if (!res.isSuccess) {
                    throw new IllegalStateException();
                }
            }
        }).then((res) -> {
            addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);
        }).done(res -> {
            boolean multipleVersion = addResult.justEnableHidden && addResult.userId != 0;
            if (!multipleVersion) {
                PackageAppData data = addResult.appData;
                data.isLoading = true;
                mView.addAppToLauncher(data);
                handleOptApp(data, info.packageName, true);
            } else {
                MultiplePackageAppData data = new MultiplePackageAppData(addResult.appData, addResult.userId);
                data.isLoading = true;
                mView.addAppToLauncher(data);
                handleOptApp(data, info.packageName, false);
            }
        });
    }

这里有一行

InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);

本质是去访问自定义的VAppManagerService

    public InstalledAppInfo getInstalledAppInfo(String packageName, int flags) {
        synchronized (PackageCacheManager.class) {
            if (packageName != null) {
                PackageSetting setting = PackageCacheManager.getSetting(packageName);
                if (setting != null) {
                    return setting.getAppInfo();
                }
            }
            return null;
        }
    }

这里是从自己定义的包缓存信息中查询是否有安装过这个包,(后面再分析是怎么记录安装信息的)
显然现在还没有安装过这个包,那么会走到else里,即执行InstallResult res = mRepo.addVirtualApp(info);

AppRepository.java

    @Override
    public InstallResult addVirtualApp(AppInfoLite info) {
        int flags = InstallStrategy.COMPARE_VERSION | InstallStrategy.SKIP_DEX_OPT;
        if (info.fastOpen) {
            flags |= InstallStrategy.DEPEND_SYSTEM_IF_EXIST;
        }
        return VirtualCore.get().installPackage(info.path, flags);
    }

VirtualCore.java

    public InstallResult installPackage(String apkPath, int flags) {
        try {
            return getService().installPackage(apkPath, flags);
        } catch (RemoteException e) {
            return VirtualRuntime.crash(e);
        }
    }

那么这里在借助aidl RPC的能力 ,利用VAppManagerService来安装包
VAppManagerService.java(方法有点大,会删除非核心逻辑的代码)

public synchronized InstallResult installPackage(String path, int flags, boolean notify) {
...
        // 真实的apk文件路径
        File packageFile = new File(path);
        if (!packageFile.exists() || !packageFile.isFile()) {
            return InstallResult.makeFailure("Package File is not exist.");
        }
        VPackage pkg = null;
        try {
            // 解析apk.生成VPackage对象
            pkg = PackageParserEx.parsePackage(packageFile);
        } catch (Throwable e) {
            e.printStackTrace();
        }
...
        InstallResult res = new InstallResult();
        res.packageName = pkg.packageName;
...
        // 生成新的安装目录
        //地址大概是/data/data/io.virtualapp/virtual/data/app/应用包名/
        File appDir = VEnvironment.getDataAppPackageDirectory(pkg.packageName);
        // 生成新的so安装目录
        File libDir = new File(appDir, "lib");
...
        // 判断是不是外部安装,比如sdcard安装
        boolean dependSystem = (flags & InstallStrategy.DEPEND_SYSTEM_IF_EXIST) != 0
                && VirtualCore.get().isOutsideInstalled(pkg.packageName);
....
        // 把原应用的lib下的数据copy过来
        NativeLibraryHelperCompat.copyNativeBinaries(new File(path), libDir);

        if (!dependSystem) {
           //假设是基于手机已安装的应用安装,那么就是走的这里
            File privatePackageFile = new File(appDir, "base.apk");
            File parentFolder = privatePackageFile.getParentFile();
            if (!parentFolder.exists() && !parentFolder.mkdirs()) {
                VLog.w(TAG, "Warning: unable to create folder : " + privatePackageFile.getPath());
            } else if (privatePackageFile.exists() && !privatePackageFile.delete()) {
                VLog.w(TAG, "Warning: unable to delete file : " + privatePackageFile.getPath());
            }
            try {
                //把原package的数据copy过来
                FileUtils.copyFile(packageFile, privatePackageFile);
            } catch (IOException e) {
                privatePackageFile.delete();
                return InstallResult.makeFailure("Unable to copy the package file.");
            }
            packageFile = privatePackageFile;
        }
        if (existOne != null) {
            PackageCacheManager.remove(pkg.packageName);
        }
        //修改新路径的权限
        chmodPackageDictionary(packageFile);
       // 生成新包安装的配置信息,用于持久化和后续查询用
        PackageSetting ps;
        if (existSetting != null) {
            ps = existSetting;
        } else {
            ps = new PackageSetting();
        }
        ps.dependSystem = dependSystem;
        ps.apkPath = packageFile.getPath();
        ps.libPath = libDir.getPath();
        ps.packageName = pkg.packageName;
        //这里为这个app生成一个appId
        ps.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));
        if (res.isUpdate) {
            ps.lastUpdateTime = installTime;
        } else {
            ps.firstInstallTime = installTime;
            ps.lastUpdateTime = installTime;
            for (int userId : VUserManagerService.get().getUserIds()) {
                boolean installed = userId == 0;
                ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);
            }
        }
        //在/data/data/io.virtualapp/virtual/data/app/应用包名/下持久化一个 package.ini,用于记录VPackage的信息, 下次读取可以直接用
        PackageParserEx.savePackageCache(pkg);
        //缓存一下信息
		PackageCacheManager.put(pkg, ps);
        mPersistenceLayer.save();
        if (!dependSystem) {
            boolean runDexOpt = false;
            if (VirtualRuntime.isArt()) {
                try {
                    ArtDexOptimizer.interpretDex2Oat(ps.apkPath, VEnvironment.getOdexFile(ps.packageName).getPath());
                } catch (IOException e) {
                    e.printStackTrace();
                    runDexOpt = true;
                }
            } else {
                runDexOpt = true;
            }
            if (runDexOpt) {
                try {
                    DexFile.loadDex(ps.apkPath, VEnvironment.getOdexFile(ps.packageName).getPath(), 0).close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        BroadcastSystem.get().startApp(pkg);
        if (notify) {
            notifyAppInstalled(ps, -1);
        }
        res.isSuccess = true;
        return res;
    }
可以看到,在VA内部安装虚拟应用,VA主要做了这几件事
  1. 反射创建android.pm.PackageParser实例,解析虚拟应用apk包的四大组件以及其他信息;
  2. 把so库复制到对应包的虚拟路径下;
  3. 保存、持久化部分apk包数据到硬盘内;
  4. 把apk包复制到对应的虚拟路径下;

与virtualapp安装应用流程源码分析相似的内容: