• Android中应用安装分析


    #1 安装方式

    • 1 安装系统APK和预制APK时,通过PMS的构造函数中安装,即第一次开机时安装应用,没有安装界面。
    • 2 网络下载安装,通过应用商店等,即调用PackageManager.installPackages(),有安装界面。
    • 3 通过adb工具安装,没有安装界面,它通过启动pm脚本的形式,然后调用com.android.commands.pm.Pm类,之后调用到PMS.installStage()完成安装。
    • 4 安装本地apk,有安装界面,由PackageInstaller系统应用安装。
      上述几种方式均通过PackageInstallObserver来监听安装是否成功。

    #2 安装流程分析

    2.1 首次安装

    首次安装即系统第一次开机时安装应用,包括系统应用和预制应用,其最主要过程在PMS构造函数中,

    整个过程关键步骤大致为上述15步,与应用安装相关实际上就是扫描和安装两步。方法调用时序图如图1所示。
    [图1 PMS安装应用时序图]

    • 1 向动态设置中添加系统默认的共享ID(system、phone、log、nfc、bluetooth、shell、se等)。
    • 2 初始化成员变量,如Installer、PackageDexOptimizer、DexManager、ArtManagerService、MoveCallbacks、OnPermissionChangeListeners等,并获取系统配置。
    • 3 启动一个服务类线程。
    • 4 初始化用户管理服务
    • 5 将权限配置传入包管理器
    • 6 清除代码路径不存在的孤立包
    • 7 将系统应用权限从安装态升级为运行时
    • 8 在扫描应用前,手机供应商的覆盖安装包(/overlay)
    • 9 扫描应用目录,依次为特权系统目录 priv-app、普通目录 app、供应商系统目录 vendor/app等
    • 10 解析存储管理器
    • 11 如果是第一次开机,需要初始化用户的默认偏好应用
    • 12 在启动时,为用户准备好存储空间,因为SystemUI等启动不能等待用户
    • 13 安装应用,完成后检查webview,默认浏览器等。
    • 14 启动PackageInstallerService
    • 15 向系统组件暴露私有服务
      下面我们结合代码做详细分析
    1. 判断应用包是否已安装,如果包名存在于uninstalled_deapp.xml中或者已安装,则直接返回null。

    2.2 下载安装

    下载安装可分为两部分:拷贝应用和安装应用。拷贝过程的函数调用时序图如图2所示。
    【图2 下载安装应用程序时序图】

    frameworks层的入口函数为PackageManager.installPackage,由应用市场APP调用,然后调用PMS.installPackageAsUser,然后发送消息INIT_COPY、MCS_BOUND开始复制,调用HandlerParams.startCopy。这个方法主要分两部分,一部分是拷贝应用的执行程序,另一部分是创建应用的数据目录,拷贝部分由handleStartCopy完成。之后调用handlerReturnCode来处理创建数据目录。拷贝部分会调用DefaultContainerService来完成,该服务为那些可能位于可删除空间上的文件提供检查和拷贝功能。当底层设置被移除时,这样设计可以防止系统进程保留打开的文件时,不被内核杀死。

    handleStartcopy实现在PMS内部类InstallParams中,它的功能是调用远程方法获取包信息和安装位置,如有必要则给与默认车辆覆盖安装位置,然后基于安装位置创建安装参数。下面我们结合关键代码做进一步分析。

    首先是拷贝应用过程

    • 1 PMS.installPackageAsUser的功能主要是:根据uid确定installFlags,并校验权限,并构造InstallParam,然后发送INIT_COPY消息。
    @Override
    public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, int userId) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
    
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId,
                true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser");
    
        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
            try {
                if (observer != null) {
                    observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
                }
            } catch (RemoteException re) {
            }
            return;
        }
    
        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
            installFlags |= PackageManager.INSTALL_FROM_ADB;
    
        } else {
            // Caller holds INSTALL_PACKAGES permission, so we're less strict
            // about installerPackageName.
    
            installFlags &= ~PackageManager.INSTALL_FROM_ADB;
            installFlags &= ~PackageManager.INSTALL_ALL_USERS;
        }
    
        UserHandle user;
        if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
            user = UserHandle.ALL;
        } else {
            user = new UserHandle(userId);
        }
    
        // Only system components can circumvent runtime permissions when installing.
        if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
                && mContext.checkCallingOrSelfPermission(Manifest.permission
                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
            throw new SecurityException("You need the "
                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
        }
    
        if ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
                || (installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
            throw new IllegalArgumentException(
                    "New installs into ASEC containers no longer supported");
        }
    
        final File originFile = new File(originPath);
        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
    
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final VerificationInfo verificationInfo = new VerificationInfo(
                null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
        final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
                installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
                null /*packageAbiOverride*/, null /*grantedPermissions*/,
                null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);
        params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
        msg.obj = params;
    
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",
                System.identityHashCode(msg.obj));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(msg.obj));
    
        mHandler.sendMessage(msg);
    }
    
    
    • 2 之后根据Handler.doHandleMessage调用到InstallParams.handleStartCopy方法,首先检查文件和cid是否已生成,如生成则设置installFlags。
    // [InstallParams.handleStartCopy]
    if (origin.staged) {
        if (origin.file != null) {
            installFlags |= PackageManager.INSTALL_INTERNAL;
            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
        } else if (origin.cid != null) {
            installFlags |= PackageManager.INSTALL_EXTERNAL;
            installFlags &= ~PackageManager.INSTALL_INTERNAL;
        } else {
            throw new IllegalStateException("Invalid stage location");
        }
    }
    
    • 3 然后检查空间大小,如果空间不够则释放无用空间。
    // [InstallParams.handleStartCopy]
    if (!origin.staged && pkgLite.recommendedInstallLocation
                            == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
        // TODO: focus freeing disk space on the target device
        final StorageManager storage = StorageManager.from(mContext);
        final long lowThreshold = storage.getStorageLowBytes(
                Environment.getDataDirectory());
    
        final long sizeBytes = mContainerService.calculateInstalledSize(
                origin.resolvedPath, isForwardLocked(), packageAbiOverride);
    
        try {
            mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
            pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                    installFlags, packageAbiOverride);
        } catch (InstallerException e) {
            Slog.w(TAG, "Failed to free cache", e);
        }
    
    
        if (pkgLite.recommendedInstallLocation
                == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
            pkgLite.recommendedInstallLocation
                = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
        }
    }
    
    • 4 覆盖原有安装位置的文件,并根据返回结果来确定函数的返回值,并设置installFlags。
    // [InstallParams.handleStartCopy]
    // Override with defaults if needed.
    loc = installLocationPolicy(pkgLite);
    if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
        ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
    } else if (!onSd && !onInt) {
        // Override install location with flags
        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
            // Set the flag to install on external media.
            installFlags |= PackageManager.INSTALL_EXTERNAL;
            installFlags &= ~PackageManager.INSTALL_INTERNAL;
        } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
            if (DEBUG_EPHEMERAL) {
                Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
            }
            installFlags |= PackageManager.INSTALL_INSTANT_APP;
            installFlags &= ~(PackageManager.INSTALL_EXTERNAL
                    |PackageManager.INSTALL_INTERNAL);
        } else {
            // Make sure the flag for installing on external
            // media is unset
            installFlags |= PackageManager.INSTALL_INTERNAL;
            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
        }
    }
    
    • 5 确定是否有任何已安装的包验证器,如有,则延迟检测。主要分三步:首先新建一个验证Intent,然后设置相关的信息,之后获取验证器列表,最后向每个验证器发送验证Intent。
    // [InstallParams.handleStartCopy]
    final Intent verification = new Intent( //构造验证Intent
                                Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
        // ......
    
        final PackageVerificationState verificationState = new PackageVerificationState(
                requiredUid, args);
    
        mPendingVerification.append(verificationId, verificationState);
        // 获取验证器列表
        final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
                receivers, verificationState);
    
        DeviceIdleController.LocalService idleController = getDeviceIdleController();
        final long idleDuration = getVerificationTimeout();
    
        /*
         * If any sufficient verifiers were listed in the package
         * manifest, attempt to ask them.
         */
        if (sufficientVerifiers != null) {
            final int N = sufficientVerifiers.size();
            if (N == 0) {
                Slog.i(TAG, "Additional verifiers required, but none installed.");
                ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
            } else {
                for (int i = 0; i < N; i++) {
                    final ComponentName verifierComponent = sufficientVerifiers.get(i);
                    idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
                            verifierComponent.getPackageName(), idleDuration,
                            verifierUser.getIdentifier(), false, "package verifier");
                    // 向每个验证器发送验证Intent
                    final Intent sufficientIntent = new Intent(verification);
                    sufficientIntent.setComponent(verifierComponent);
                    mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
                }
            }
        }
    
    • 6 向验证器客户端发送intent,只有当验证成功之后才会开启copy工作。如果没有任何验证器则直接拷贝。

    下面为安装过程入口是PMS.processPendingInstall方法,调用时序图如图3
    【图3 下载安装-安装过程图】

    • 1 首先启动一个新线程,然后设置安装信息,处理安装参数,开始安装,并发送关于安装状态的广播,然后处理安装完的事情,比如打印错误信息,清除临时文件等。
    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        // Queue up an async operation since the package installation may take a little while.
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
                 // Result object to be returned
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.setReturnCode(currentStatus);
                res.uid = -1;
                res.pkg = null;
                res.removedInfo = null;
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    args.doPreInstall(res.returnCode);
                    synchronized (mInstallLock) {
                        installPackageTracedLI(args, res);
                    }
                    args.doPostInstall(res.returnCode, res.uid);
        //......
    }
    
    • 2 installPackageTracedLI是安装过程的核心方法,然后调用installPackageLI.首先检查安装包的完整性并解析安装包。
    //[PMS.installPackageLI]
    // 完整性校验
    if (instantApp && (forwardLocked || onExternal)) {
        Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
                + " external=" + onExternal);
        res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
        return;
    }
    
    // 检索包设置,并解析应用
    final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
            | PackageParser.PARSE_ENFORCE_CODE
            | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
            | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
            | (instantApp ? PackageParser.PARSE_IS_EPHEMERAL : 0)
            | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
    PackageParser pp = new PackageParser();
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setDisplayMetrics(mMetrics);
    pp.setCallback(mPackageParserCallback);
    
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final PackageParser.Package pkg;
    try {
        //解析安装包
        pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        DexMetadataHelper.validatePackageDexMetadata(pkg);
    } catch (PackageParserException e) {
        res.setError("Failed parse during installPackageLI", e);
        return;
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
    
    • 3 检查SDK版本和沙箱版本,同时检查是否有静态共享库,如有则需要放在内部存储中。
    //[PMS.installPackageLI]
    //检查SDK版本和沙箱版本
    if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
        Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target O");
        res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
                "Instant app package must target O");
        return;
    }
    if (instantApp && pkg.applicationInfo.targetSandboxVersion != 2) {
        Slog.w(TAG, "Instant app package " + pkg.packageName
                + " does not target targetSandboxVersion 2");
        res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
                "Instant app package must use targetSanboxVersion 2");
        return;
    }
    //检查是否有静态共享库
    if (pkg.applicationInfo.isStaticSharedLibrary()) {
        // Static shared libraries have synthetic package names
        renameStaticSharedLibraryPackage(pkg);
    
        // No static shared libs on external storage
        if (onExternal) {
            Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
            res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                    "Packages declaring static-shared libs cannot be updated");
            return;
        }
    }
    
    • 4 检查是否有子安装包,如有则子安装包也需要检测。
    //[PMS.installPackageLI]
    // If we are installing a clustered package add results for the children
    if (pkg.childPackages != null) {
        synchronized (mPackages) {
            final int childCount = pkg.childPackages.size();
            for (int i = 0; i < childCount; i++) {
                PackageParser.Package childPkg = pkg.childPackages.get(i);
                PackageInstalledInfo childRes = new PackageInstalledInfo();
                childRes.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
                childRes.pkg = childPkg;
                childRes.name = childPkg.packageName;
                PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
                if (childPs != null) {
                    childRes.origUsers = childPs.queryInstalledUsers(
                            sUserManager.getUserIds(), true);
                }
                if ((mPackages.containsKey(childPkg.packageName))) {
                    childRes.removedInfo = new PackageRemovedInfo(this);
                    childRes.removedInfo.removedPackage = childPkg.packageName;
                    childRes.removedInfo.installerPackageName = childPs.installerPackageName;
                }
                if (res.addedChildPackages == null) {
                    res.addedChildPackages = new ArrayMap<>();
                }
                res.addedChildPackages.put(childPkg.packageName, childRes);
            }
        }
    }
    
    • 5 检查安装包是否已存在,如已存在则需要检查旧的父包、沙箱、sdk等是否已为空,否则会报错。
    • 6 校验安装包签名
    //[PMS.installPackageLI]
    PackageSetting signatureCheckPs = ps;
    if (pkg.applicationInfo.isStaticSharedLibrary()) {
        SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
        if (libraryEntry != null) {
            signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
        }
    }
    
    // Quick sanity check that we're signed correctly if updating;
    // we'll check this again later when scanning, but we want to
    // bail early here before tripping over redefined permissions.
    if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
        if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
            res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                    + pkg.packageName + " upgrade keys do not match the "
                    + "previously installed version");
            return;
        }
    } else {
        try {
            verifySignaturesLP(signatureCheckPs, pkg);
        } catch (PackageManagerException e) {
            res.setError(e.error, e.getMessage());
            return;
        }
    }
    
    • 7 设置相关的全向,包括生成权限、移植权限等
    • 8 如果这是一个系统应用,则检查是否在外部存储上或是是否被其他应用替换等
    //[PMS.installPackageLI]
    if (systemApp) {
        if (onExternal) {
            // Abort update; system app can't be replaced with app on sdcard
            res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                    "Cannot install updates to system apps on sdcard");
            return;
        } else if (instantApp) {
            // Abort update; system app can't be replaced with an instant app
            res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
                    "Cannot update a system app with an instant app");
            return;
        }
    }
    
    • 9 生成安装包Abi(Application binary interface,应用二进制接口,描述应用程序和操作系统之间或其他应用程序的低级接口)
    //[PMS.installPackageLI]
    try {
        String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
            args.abiOverride : pkg.cpuAbiOverride);
        final boolean extractNativeLibs = !pkg.isLibrary();
        derivePackageAbi(pkg, new File(pkg.codePath), abiOverride,
                extractNativeLibs, mAppLib32InstallDir);
    } catch (PackageManagerException pme) {
        Slog.e(TAG, "Error deriving application ABI", pme);
        res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
        return;
    }
    
    • 10更新共享库
    //[PMS.installPackageLI]
    synchronized (mPackages) {
        try {
            updateSharedLibrariesLPr(pkg, null);
        } catch (PackageManagerException e) {
            Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
        }
    }
    
    • 11如有必要,优化dex文件
    //[PMS.installPackageLI]
    final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED)
        && !forwardLocked
        && !pkg.applicationInfo.isExternalAsec()
        && (!instantApp || Global.getInt(mContext.getContentResolver(),
        Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
        && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
    
    if (performDexopt) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
    // Do not run PackageDexOptimizer through the local performDexOpt
    // method because `pkg` may not be in `mPackages` yet.
    //
    // Also, don't fail application installs if the dexopt step fails.
    DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
            REASON_INSTALL,
            DexoptOptions.DEXOPT_BOOT_COMPLETE |
            DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
    mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
            null /* instructionSets */,
            getOrCreateCompilerPackageStats(pkg),
            mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
            dexoptOptions);
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
    
    • 12替换安装,则直接安装新包,这里应用时生成应用数据目录。ps:替换安装:其主要过程为更新设置,清除原有的某些APP数据,重新生成相关的app数据目录等步骤,同事要区分系统应用替换和非系统应用替换。而安装新包:则直接更新设置,生成APP数据即可。
    try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                    "installPackageLI")) {
        if (replace) {
            if (pkg.applicationInfo.isStaticSharedLibrary()) {
                // Static libs have a synthetic package name containing the version
                // and cannot be updated as an update would get a new package name,
                // unless this is the exact same version code which is useful for
                // development.
                PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
                if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) {
                    res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
                            + "static-shared libs cannot be updated");
                    return;
                }
            }
            replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                    installerPackageName, res, args.installReason);
        } else {
            installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                    args.user, installerPackageName, volumeUuid, res, args.installReason);
        }
    }
    
    • 13 如果是安装一个不存在的包,则调用PMS.installNewPackageLIF方法。首先会检查是否有重复的包名,并更新设置,然后根据安装的结果,如果安装失败则删除安装过程中产生的文件。
    private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
            int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
            PackageInstalledInfo res, int installReason) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
    
        // Remember this for later, in case we need to rollback this install
        String pkgName = pkg.packageName;
    
        if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
    
        synchronized(mPackages) {
            final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
            if (renamedPackage != null) {
                // 如果已有相同包名的应用,则报错
                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
                        + " without first uninstalling package running as "
                        + renamedPackage);
                return;
            }
            if (mPackages.containsKey(pkgName)) {
                // Don't allow installation over an existing package with the same name.
                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
                        + " without first uninstalling.");
                return;
            }
        }
    
        try {
            PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags, System.currentTimeMillis(), user);
    
            updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
    
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                prepareAppDataAfterInstallLIF(newPackage);
    
            } else {
                // Remove package from internal structures, but keep around any
                // data that might have already existed
                deletePackageLIF(pkgName, UserHandle.ALL, false, null,
                        PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
            }
        } catch (PackageManagerException e) {
            res.setError("Package couldn't be installed in " + pkg.codePath, e);
        }
    
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
    
    • 14 然后为已安装的应用准备数据目录,其依次的顺序是
      • PMS.prepareAppDataAfterInstallLIF
      • PMS.prepareAppDataLIF
      • PMS.prepareAppDataLeafLIF
      • Installer.createAppData

    这个方法是PMS与Installer交互的接口函数,这里的数据目录是CE类型。

    private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
        if (DEBUG_APP_DATA) {
            Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
                    + Integer.toHexString(flags));
        }
    
        final String volumeUuid = pkg.volumeUuid;
        final String packageName = pkg.packageName;
        final ApplicationInfo app = pkg.applicationInfo;
        final int appId = UserHandle.getAppId(app.uid);
    
        Preconditions.checkNotNull(app.seInfo);
    
        long ceDataInode = -1;
        try {
            // 调用Installd守护进程的入口
            ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                    appId, app.seInfo, app.targetSdkVersion);
        } catch (InstallerException e) {
            //......
        }
        // Prepare the application profiles.
        mArtManagerService.prepareAppProfiles(pkg, userId);
    
        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
            // TODO: mark this structure as dirty so we persist it!
            synchronized (mPackages) {
                final PackageSetting ps = mSettings.mPackages.get(packageName);
                if (ps != null) {
                    ps.setCeDataInode(ceDataInode, userId);
                }
            }
        }
    
        prepareAppDataContentsLeafLIF(pkg, userId, flags);
    }
    
    
    • 15 如果是替换应用,一般情况是应用更新,或者是重新安装。它的主要过程包括:验证签名,如是系统更新则还需要校验hash值,检查共享ID的更改情况,不允许完整更新,更新已被删除数据,最后根据应用是否是系统应用来判断接下去的操作。
    private void replacePackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags,
            UserHandle user, String installerPackageName, PackageInstalledInfo res,
            int installReason) {
        final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
    
        final PackageParser.Package oldPackage;
        final PackageSetting ps;
        final String pkgName = pkg.packageName;
        final int[] allUsers;
        final int[] installedUsers;
    
        // ......
    
        boolean sysPkg = (isSystemApp(oldPackage));
        if (sysPkg) {
            // Set the system/privileged flags as needed
            final boolean privileged =
                    (oldPackage.applicationInfo.privateFlags
                            & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
            final int systemPolicyFlags = policyFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | (privileged ? PackageParser.PARSE_IS_PRIVILEGED : 0);
    
            replaceSystemPackageLIF(oldPackage, pkg, systemPolicyFlags, scanFlags,
                    user, allUsers, installerPackageName, res, installReason);
        } else {
            replaceNonSystemPackageLIF(oldPackage, pkg, policyFlags, scanFlags,
                    user, allUsers, installerPackageName, res, installReason);
        }
    }
    
    • 16 最后这两个方法均会调用到PMS.prepareAppDataLeafLIF。
    • 17 安装完成后,更新设置,更新安装锁等。

    2.3 adb安装

    关于adb安装,其copy过程与下载安装不同,但安装过程却与下载过程是相同的,这里不做重复分析,需要注意的是adb安装是不能替换安装的,具体原因?

    拷贝过程
    其调用时序图如图4 所示。
    【图4 adb安装-copy过程时序图】

    • 1 adb的入口在com.android.commands.pm.Pm类,那么这是如何调用到这个类的呢,这是adb命令通过adbd守护进程调用到/system/bin/pm这个脚本,其脚本源码如下:
    base=/system
    export CLASSPATh-$base/framework/pm.jar
    exec app_process $base/bin.com.android.commands.pm.Pm "$@"
    
    • 2 Pm类通过脚本启动,执行顺序是main->run->runInstall,然后提交session。
    public static void main(String[] args) {
        int exitCode = 1;
        try {
            exitCode = new Pm().run(args);
        } catch (Exception e) {
            Log.e(TAG, "Error", e);
            System.err.println("Error: " + e);
            if (e instanceof RemoteException) {
                System.err.println(PM_NOT_RUNNING_ERR);
            }
        }
        System.exit(exitCode);
    }
    public int run(String[] args) throws RemoteException {
        boolean validCommand = false;
        if (args.length < 1) {
            return showUsage();
        }
        mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
        mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    
        if (mPm == null) {
            System.err.println(PM_NOT_RUNNING_ERR);
            return 1;
        }
        mInstaller = mPm.getPackageInstaller();
    
        mArgs = args;
        String op = args[0];
        mNextArg = 1;
        //......
        if ("install".equals(op)) {
            return runInstall();
        }
        //......
    }
    
    • 3 Pm.runInstall中首先是创建session,然后提交session,代码如下。
        private int runInstall() throws RemoteException {
            long startedTime = SystemClock.elapsedRealtime();
            final InstallParams params = makeInstallParams();
            final String inPath = nextArg();
            if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) {
                File file = new File(inPath);
                if (file.isFile()) {
                    try {
                        ApkLite baseApk = PackageParser.parseApkLite(file, 0);
                        PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
                                null, null);
                        params.sessionParams.setSize(
                                PackageHelper.calculateInstalledSize(pkgLite, false,
                                params.sessionParams.abiOverride));
                    } catch (PackageParserException | IOException e) {
                        System.err.println("Error: Failed to parse APK file: " + e);
                        return 1;
                    }
                } else {
                    System.err.println("Error: Can't open non-file: " + inPath);
                    return 1;
                }
            }
    
            final int sessionId = doCreateSession(params.sessionParams,
                    params.installerPackageName, params.userId);
    
            try {
                if (inPath == null && params.sessionParams.sizeBytes == -1) {
                    System.err.println("Error: must either specify a package size or an APK file");
                    return 1;
                }
                if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                        false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                    return 1;
                }
                Pair<String, Integer> status = doCommitSession(sessionId, false /*logSuccess*/);
                if (status.second != PackageInstaller.STATUS_SUCCESS) {
                    return 1;
                }
                Log.i(TAG, "Package " + status.first + " installed in " + (SystemClock.elapsedRealtime()
                        - startedTime) + " ms");
                System.out.println("Success");
                return 0;
            } finally {
                try {
                    mInstaller.abandonSession(sessionId);
                } catch (Exception ignore) {
                }
            }
        }
    
    • 4 这里Pm相当于客户端,接受session的服务端在PackageInstallerSession中,这里利用AIDL来完成传输,其调用过程为:
      • Pm.doCommitSession
      • PackageInstaller.Session.commit
      • IPackageInstallerSession.commit
      • PackageInstallerSession.commit
      • Handler.Callback.handleMessage
      • PackageInstallerSession.commitLock
      • PMS.installStage

    以上关于session传递过程暂不分析,下面我们来详细看下installStage方法。

    • 5 installStage方法主要功能就是构造InstallParam对象,并发送INIT_COPY。
    void installStage(String packageName, File stagedDir, String stagedCid,
            IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
            String installerPackageName, int installerUid, UserHandle user,
            Certificate[][] certificates) {
        if (DEBUG_EPHEMERAL) {
            if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
                Slog.d(TAG, "Ephemeral install of " + packageName);
            }
        }
        final VerificationInfo verificationInfo = new VerificationInfo(
                sessionParams.originatingUri, sessionParams.referrerUri,
                sessionParams.originatingUid, installerUid);
    
        final OriginInfo origin;
        if (stagedDir != null) {
            origin = OriginInfo.fromStagedFile(stagedDir);
        } else {
            origin = OriginInfo.fromStagedContainer(stagedCid);
        }
    
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final int installReason = fixUpInstallReason(installerPackageName, installerUid,
                sessionParams.installReason);
        final InstallParams params = new InstallParams(origin, null, observer,
                sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                verificationInfo, user, sessionParams.abiOverride,
                sessionParams.grantedRuntimePermissions, certificates, installReason);
        params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
        msg.obj = params;
    
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
                System.identityHashCode(msg.obj));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(msg.obj));
    
        mHandler.sendMessage(msg);
    }
    
    
    • 6 发送完Handler消息后就与下载安装过程相同了。

    2.4 本地安装

    本地安装参与对象包括PackageInstaller应用,PMS两部分。下面我们就来分析下PackageInstaller是如何调用到PMS中的。函数调用时序图如图5所示。
    【图5 本地安装前提调用时序图】

    • 1 点击文件管理器中的apk文件时,会调用到FolderFragment类的openFile方法,然后调用startActivitySafety方法启动PackageInstallerActivity。
    private void openFile(File f) {  
        final Uri fileUri = Uri.fromFile(f);  
        final Intent intent = new Intent();  
        intent.setAction(android.content.Intent.ACTION_VIEW);  
        intent.putExtra(Intent.EXTRA_TITLE, f.getName());  
        intent.putExtra(EXTRA_ALL_VIDEO_FOLDER, true);  
        Uri contentUri = null;  
        String type = getMIMEType(f);  
        //......  
            if (contentUri != null) {  
                intent.setDataAndType(contentUri, type);  
            } else {  
                intent.setDataAndType(fileUri, type);  
            }  
            try {  
                startActivitySafely(intent);  
            }   
        //......  
    }
    
    • 2 如下为PackageInstallerActivity.onCreate方法源码,其主要过程初始化各个服务的成员变量如PMS,校验session,并加载UI界面,然用户确定是否安装。
    //[PackageInstallerActivity.java]
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
    
        if (icicle != null) {
            mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
        }
    	//初始化各个关键参数
        mPm = getPackageManager();
        mIpm = AppGlobals.getPackageManager();
        mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
        mInstaller = mPm.getPackageInstaller();
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
    
        final Intent intent = getIntent();
    
        mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
        mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
        mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                PackageInstaller.SessionParams.UID_UNKNOWN);
        mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
                ? getPackageNameForUid(mOriginatingUid) : null;
    
    
        final Uri packageUri;
        //校验session
        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
                finish();
                return;
            }
    
            mSessionId = sessionId;
            packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
            mOriginatingURI = null;
            mReferrerURI = null;
        } else {
            mSessionId = -1;
            packageUri = intent.getData();
            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
            mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
        }
    
        // if there's nothing to do, quietly slip into the ether
        if (packageUri == null) {
            Log.w(TAG, "Unspecified source");
            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
            finish();
            return;
        }
    
        if (DeviceUtils.isWear(this)) {
            showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
            return;
        }
    
        boolean wasSetUp = processPackageUri(packageUri);
        if (!wasSetUp) {
            return;
        }
    
        // 加载UI界面
        bindUi(R.layout.install_confirm, false);
        checkIfAllowedAndInitiateInstall();
    }
    
    • 3 当用户点击安装按钮时,响应函数为PackageInstallerActivity.onClick方法,
    //[PackageInstallerActivity.java]
    public void onClick(View v) {
        if (v == mOk) {
            if (mOk.isEnabled()) {
                if (mOkCanInstall || mScrollView == null) {
                    if (mSessionId != -1) {
                        mInstaller.setPermissionsResult(mSessionId, true);
                        finish();
                    } else {
                        startInstall();
                    }
                } else {
                    mScrollView.pageScroll(View.FOCUS_DOWN);
                }
            }
        } else if (v == mCancel) {
            // Cancel and finish
            setResult(RESULT_CANCELED);
            if (mSessionId != -1) {
                mInstaller.setPermissionsResult(mSessionId, false);
            }
            finish();
        }
    }
    
    • 4 之后调用 PackageInstallerActivity.startInstall方法,构造Intent,然后启动InstallInstalling,并销毁PackageInstallerActivity。
    private void startInstall() {
        // Start subactivity to actually install the application
        Intent newIntent = new Intent();
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                mPkgInfo.applicationInfo);
        newIntent.setData(mPackageURI);
        newIntent.setClass(this, InstallInstalling.class);
        String installerPackageName = getIntent().getStringExtra(
                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
        if (mOriginatingURI != null) {
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
        }
        if (mReferrerURI != null) {
            newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
        }
        if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
        }
        if (installerPackageName != null) {
            newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                    installerPackageName);
        }
        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
            newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        }
        if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
        startActivity(newIntent);
        finish();
    }
    
    • 5 之后启动InstallInstalling,因为Activity中的默认成员方法的执行顺序是onCreate->onStart->onResume...其中onCreate的方法中主要过程包括:
      • 1 获取待安装应用信息
      • 2 根据应用安装与否决定如何调用方法
      • 3 如果已存在,则直接调用PackageManager.installExistingPackage
      • 4 如果不存在则构造session
      • 5 之后则为安装事件广播添加一个监测
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setContentView(R.layout.install_installing);
        // 获取待安装应用信息
        ApplicationInfo appInfo = getIntent()
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        mPackageURI = getIntent().getData();
        // 如果应用已存在,则使用这条路径安装
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                getPackageManager().installExistingPackage(appInfo.packageName);
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else { //否则使用session提交安装应用
            final File sourceFile = new File(mPackageURI.getPath());
            PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
                    sourceFile), R.id.app_snippet);
            // 如果session已存在,则获取sessionId等数据
            if (savedInstanceState != null) {
                mSessionId = savedInstanceState.getInt(SESSION_ID);
                mInstallId = savedInstanceState.getInt(INSTALL_ID);
    
                // Reregister for result; might instantly call back if result was delivered while
                // activity was destroyed
                try {
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else { // 否则创建session
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                // ......
                try {
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
                //创建session
                try {
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
            }
            //......
            mSessionCallback = new InstallSessionCallback();
        }
    }
    
    • 6 InstallInstalling.onStart中注册回调函数,然后onResume中执行AsyncTask。
    @Override
    protected void onResume() {
        super.onResume();
    
        // This is the first onResume in a single life of the activity
        if (mInstallingTask == null) {
            PackageInstaller installer = getPackageManager().getPackageInstaller();
            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
            //如果session非空,则执行AsyncTask
            if (sessionInfo != null && !sessionInfo.isActive()) {
                mInstallingTask = new InstallingAsyncTask();
                mInstallingTask.execute();
            } else {
                // we will receive a broadcast when the install is finished
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            }
        }
    }
    
    • 7 AsyncTask是Android提供的一种轻量级的异步类,执行过程可以表示为5个阶段。
      • 1 准备执行,onPreExecute()
      • 2 正在后台执行,doInBackgroud()
      • 3 进度更新,onProcessUpdate()
      • 4 完成后台任务,onPostExecute()
      • 5 取消任务,onCacelled()

    此处重写了方法onPostExecute方法,源码如下。

    @Override
    protected void onPostExecute(PackageInstaller.Session session) {
        if (session != null) {
            Intent broadcastIntent = new Intent(BROADCAST_ACTION);
            broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
            broadcastIntent.setPackage(
                    getPackageManager().getPermissionControllerPackageName());
            broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
    
            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    InstallInstalling.this,
                    mInstallId,
                    broadcastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            //提交session
            session.commit(pendingIntent.getIntentSender());
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        } else {
            getPackageManager().getPackageInstaller().abandonSession(mSessionId);
    
            if (!isCancelled()) {
                launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
            }
        }
    }
    
    • 8 session对象传输顺序为:
      • 1 PackageInstaller.Session.commit
      • 2 IPackageInstallerSession.commit
      • 3 PackageInstallerSession.commit
      • 4 Handler.Callback.handleMessage
      • 5 PackageInstallerSession.commitLock
      • 6 PMS.installStage
        这里是不是似曾相识,这一步跟Adb安装的第4步几乎相同,之后就调用installStage方法完成安装。

    #3 总结

    安装应用的场景就是上述所示的PMS构造函数安装、adb安装、网络下载安装、本地安装。其最终的入口为PMS.prepareAppDataLeafLIF,然后调用Installer类完成安装,这里涉及到System_server到Installd守护进程的转移。

  • 相关阅读:
    Linux之lsof命令
    lnmp一键安装的卸载
    MySQL密码忘了怎么办?MySQL重置root密码方法
    LNMP状态管理命令
    perl5 第一章 概述
    行政级别详解
    http://dl.fedoraproject.org/pub/epel/7/x86_64/,开源软件清单list
    Linux Centos 系统上安装BT客户端 Transmission
    在VPS上安裝BT軟體Transmission
    What is a good EPUB reader on Linux
  • 原文地址:https://www.cnblogs.com/z1987/p/8974719.html
Copyright © 2020-2023  润新知