Android 安装过程五 MSG_INSTALL消息的处理 安装

现在马上进入正式的安装流程。

从前面文章 Android 安装过程四 MSG_INSTALL消息的处理 安装之前的验证知道,在验证之后没有什么问题的情况下,会回调onVerificationComplete()方法,它位于PackageInstallerSession类中。

java 复制代码
    private void onVerificationComplete() {
        // Staged sessions will be installed later during boot
        if (isStaged()) {
            // TODO(b/136257624): Remove this once all verification logic has been transferred out
            //  of StagingManager.
            mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
            // TODO(b/136257624): We also need to destroy internals for verified staged session,
            //  otherwise file descriptors are never closed for verified staged session until reboot
            return;
        }

        install();
    }
    ......
    private void install() {
        try {
            installNonStaged();
        } catch (PackageManagerException e) {
            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
            onSessionInstallationFailure(e.error, completeMsg);
        }
    }

看到不是在缓存下来进行安装的情况下,会继续调用installNonStaged()方法。

java 复制代码
    private void installNonStaged()
            throws PackageManagerException {
        final PackageManagerService.InstallParams installingSession = makeInstallParams();
        if (installingSession == null) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                    "Session should contain at least one apk session for installation");
        }
        if (isMultiPackage()) {
            final List<PackageInstallerSession> childSessions;
            synchronized (mLock) {
                childSessions = getChildSessionsLocked();
            }
            List<PackageManagerService.InstallParams> installingChildSessions =
                    new ArrayList<>(childSessions.size());
            boolean success = true;
            PackageManagerException failure = null;
            for (int i = 0; i < childSessions.size(); ++i) {
                final PackageInstallerSession session = childSessions.get(i);
                try {
                    final PackageManagerService.InstallParams installingChildSession =
                            session.makeInstallParams();
                    if (installingChildSession != null) {
                        installingChildSessions.add(installingChildSession);
                    }
                } catch (PackageManagerException e) {
                    failure = e;
                    success = false;
                }
            }
            if (!success) {
                final IntentSender statusReceiver;
                synchronized (mLock) {
                    statusReceiver = mRemoteStatusReceiver;
                }
                sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                        isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
                        failure.error, failure.getLocalizedMessage(), null);
                return;
            }
            mPm.installStage(installingSession, installingChildSessions);
        } else {
            mPm.installStage(installingSession);
        }
    }

构造安装参数InstallParams对象

和验证那块的代码有些像,先调用makeInstallParams()构造安装参数InstallParams对象installingSession。暂时忽略多包安装的情况,接着就是到PackageManagerService对象中调用installStage(installingSession)方法,执行安装。

先看一下安装参数的构成方法makeInstallParams():

java 复制代码
    @Nullable
    private PackageManagerService.InstallParams makeInstallParams()
            throws PackageManagerException {
        synchronized (mLock) {
            if (mDestroyed) {
                throw new PackageManagerException(
                        INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
            }
            if (!mSealed) {
                throw new PackageManagerException(
                        INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
            }
        }

        // Do not try to install staged apex session. Parent session will have at least one apk
        // session.
        if (!isMultiPackage() && isApexSession() && params.isStaged) {
            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,
                    "Apex package should have been installed by apexd", null);
            return null;
        }

        final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
            @Override
            public void onUserActionRequired(Intent intent) {
                throw new IllegalStateException();
            }

            @Override
            public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                    Bundle extras) {
                if (isStaged()) {
                    sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
                } else {
                    // We've reached point of no return; call into PMS to install the stage.
                    // Regardless of success or failure we always destroy session.
                    destroyInternal();
                    dispatchSessionFinished(returnCode, msg, extras);
                }
            }
        };

        final UserHandle user;
        if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
            user = UserHandle.ALL;
        } else {
            user = new UserHandle(userId);
        }

        if (params.isStaged) {
            params.installFlags |= INSTALL_STAGED;
        }

        if (!isMultiPackage() && !isApexSession()) {
            synchronized (mLock) {
                // This shouldn't be null, but have this code path just in case.
                if (mPackageLite == null) {
                    Slog.wtf(TAG, "Session: " + sessionId + ". Don't have a valid PackageLite.");
                }
                mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
            }
        }

        synchronized (mLock) {
            return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,
                    mSigningDetails, mInstallerUid, mPackageLite);
        }
    }

首先检查Session对象的状态,如果是mDestroyed或!mSealed,都会抛出PackageManagerException。

接着会生成一个回调对象localObserver。等待后面安装完毕之后,会调用它的onPackageInstalled回调接口。

如果不是多包安装和不是apex安装的情况下,如果成员变量为null,会调用getOrParsePackageLiteLocked(stageDir, /* flags */ 0)再次生成它。

最后就是调用InstallParams 的构造函数来生成InstallParams 对象了。

进入PackageManagerService中执行安装

PackageManagerService类中的installStage(InstallParams params)如下:

java 复制代码
    void installStage(InstallParams params) {
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        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);
    }

看下INIT_COPY消息的处理,

java 复制代码
        void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    if (params != null) {
                        if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
                        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                System.identityHashCode(params));
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                        params.startCopy();
                        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                    }
                    break;
                }
                ............

msg.obj就是前面构造的InstallParams 对象,这里调用它的startCopy()方法。InstallParams 是继承自HandlerParams类,它的startCopy(),就是调用handleStartCopy()和handleReturnCode()。这俩方法是实现在InstallParams 类中的。

InstallParams 类的handleStartCopy()

先看InstallParams 类的handleStartCopy():

java 复制代码
        public void handleStartCopy() {
            if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
                mRet = INSTALL_SUCCEEDED;
                return;
            }
            PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                    mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);

            // For staged session, there is a delay between its verification and install. Device
            // state can change within this delay and hence we need to re-verify certain conditions.
            boolean isStaged = (installFlags & INSTALL_STAGED) != 0;
            if (isStaged) {
                mRet = verifyReplacingVersionCode(
                        pkgLite, requiredInstalledVersionCode, installFlags);
                if (mRet != INSTALL_SUCCEEDED) {
                    return;
                }
            }

            mRet = overrideInstallLocation(pkgLite);
        }

通过PackageManagerServiceUtils.getMinimalPackageInfo()得到包信息对象pkgLite。接着就调用overrideInstallLocation(pkgLite)重写安装位置,主要是可能改变安装标识。

这两个方法咱们主要关注一下关于安装位置方面的东西。

先看下PackageManagerServiceUtils.getMinimalPackageInfo()方法:

java 复制代码
    public static PackageInfoLite getMinimalPackageInfo(Context context, PackageLite pkg,
            String packagePath, int flags, String abiOverride) {
        final PackageInfoLite ret = new PackageInfoLite();
        if (packagePath == null || pkg == null) {
            Slog.i(TAG, "Invalid package file " + packagePath);
            ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
            return ret;
        }

        final File packageFile = new File(packagePath);
        final long sizeBytes;
        try {
            sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
        } catch (IOException e) {
            if (!packageFile.exists()) {
                ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
            } else {
                ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
            }

            return ret;
        }

        final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
                pkg.getPackageName(), pkg.getInstallLocation(), sizeBytes, flags);

        ret.packageName = pkg.getPackageName();
        ret.splitNames = pkg.getSplitNames();
        ret.versionCode = pkg.getVersionCode();
        ret.versionCodeMajor = pkg.getVersionCodeMajor();
        ret.baseRevisionCode = pkg.getBaseRevisionCode();
        ret.splitRevisionCodes = pkg.getSplitRevisionCodes();
        ret.installLocation = pkg.getInstallLocation();
        ret.verifiers = pkg.getVerifiers();
        ret.recommendedInstallLocation = recommendedInstallLocation;
        ret.multiArch = pkg.isMultiArch();
        ret.debuggable = pkg.isDebuggable();

        return ret;
    }

这里需要关注的是PackageInfoLite对象的recommendedInstallLocation和installLocation成员变量。

这里看到recommendedInstallLocation的意思是推荐的安装位置,如果结果如果是以PackageHelper.RECOMMEND_FAILED 开头的,代表失败,出问题了。

像这里计算安装大小sizeBytes时,出现异常时,RECOMMEND_FAILED_INVALID_URI代表文件不存在,RECOMMEND_FAILED_INVALID_APK代表不是有效的APK文件。

installLocation是来自安装包的解析属性,它是配置在Manifest中的"installLocation"属性,如果没有配置,默认为PackageInfo.INSTALL_LOCATION_UNSPECIFIED。

推荐安装位置,主要是通过PackageHelper.resolveInstallLocation()方法来得到。再看一下它:

java 复制代码
    public static int resolveInstallLocation(Context context, SessionParams params)
            throws IOException {
        ApplicationInfo existingInfo = null;
        try {
            existingInfo = context.getPackageManager().getApplicationInfo(params.appPackageName,
                    PackageManager.MATCH_ANY_USER);
        } catch (NameNotFoundException ignored) {
        }

        final int prefer;
        final boolean checkBoth;
        boolean ephemeral = false;
        if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            ephemeral = true;
            checkBoth = false;
        } else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
            prefer = RECOMMEND_INSTALL_EXTERNAL;
            checkBoth = true;
        } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
            // When app is already installed, prefer same medium
            if (existingInfo != null) {
                // TODO: distinguish if this is external ASEC
                if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
                    prefer = RECOMMEND_INSTALL_EXTERNAL;
                } else {
                    prefer = RECOMMEND_INSTALL_INTERNAL;
                }
            } else {
                prefer = RECOMMEND_INSTALL_INTERNAL;
            }
            checkBoth = true;
        } else {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        }

        boolean fitsOnInternal = false;
        if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
            fitsOnInternal = fitsOnInternal(context, params);
        }

        boolean fitsOnExternal = false;
        if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {
            fitsOnExternal = fitsOnExternal(context, params);
        }

        if (prefer == RECOMMEND_INSTALL_INTERNAL) {
            // The ephemeral case will either fit and return EPHEMERAL, or will not fit
            // and will fall through to return INSUFFICIENT_STORAGE
            if (fitsOnInternal) {
                return (ephemeral)
                        ? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL
                        : PackageHelper.RECOMMEND_INSTALL_INTERNAL;
            }
        } else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {
            if (fitsOnExternal) {
                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
            }
        }

        if (checkBoth) {
            if (fitsOnInternal) {
                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
            } else if (fitsOnExternal) {
                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
            }
        }

        return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
    }

从这里可以看到推荐安装的值。

这里定义变量prefer是比较推荐安装的地方。checkBoth是检查内部存储和外部存储空间是否满足。

优先判断的是标识installFlags。如果它不满足,会去判断installLocation中的值。

如果明确推荐位置,checkBoth为false,它之后不会内部和外部存储空间两个都做判断。

像满足安装位置为INSTALL_LOCATION_PREFER_EXTERNAL或PackageInfo.INSTALL_LOCATION_AUTO的情况下,将checkBoth = true。像INSTALL_LOCATION_INTERNAL_ONLY明确就在内部安装,就将checkBoth = false。

fitsOnInternal()和fitsOnExternal()两个方法就是去判断内部空间和外部空间是否满足安装大小。如果满足,返回true。

如果推荐内部安装,并且内部大小充足,就返回RECOMMEND_INSTALL_INTERNAL(Instant安装返回RECOMMEND_INSTALL_EPHEMERAL)。推荐外部安装,外部空间满足,就返回RECOMMEND_INSTALL_EXTERNAL。

如果前面两个都不是,并且满足需要检查两个空间的情况,如果空间足够,优先内部安装,返回RECOMMEND_INSTALL_INTERNAL,再外部安装。

如果以上都不满足,返回RECOMMEND_FAILED_INSUFFICIENT_STORAGE,代表空间不足。

handleStartCopy()接下来执行overrideInstallLocation(pkgLite),看一下它:

java 复制代码
        private int overrideInstallLocation(PackageInfoLite pkgLite) {
            final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
            if (DEBUG_INSTANT && ephemeral) {
                Slog.v(TAG, "pkgLite for install: " + pkgLite);
            }

            if (origin.staged) {
                // If we're already staged, we've firmly committed to an install location
                if (origin.file != null) {
                    installFlags |= PackageManager.INSTALL_INTERNAL;
                } else {
                    throw new IllegalStateException("Invalid stage location");
                }
            } else if (pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                /*
                 * If we are not staged and have too little free space, try to free cache
                 * before giving up.
                 */
                // 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 = PackageManagerServiceUtils.calculateInstalledSize(
                        origin.resolvedPath, packageAbiOverride);
                if (sizeBytes >= 0) {
                    try {
                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
                        pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                                mPackageLite, origin.resolvedPath, installFlags,
                                packageAbiOverride);
                    } catch (InstallerException e) {
                        Slog.w(TAG, "Failed to free cache", e);
                    }
                }

                /*
                 * The cache free must have deleted the file we downloaded to install.
                 *
                 * TODO: fix the "freeCache" call to not delete the file we care about.
                 */
                if (pkgLite.recommendedInstallLocation
                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    pkgLite.recommendedInstallLocation
                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                }
            }

            int ret = INSTALL_SUCCEEDED;
            int loc = pkgLite.recommendedInstallLocation;
            if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
                ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
                ret = PackageManager.INSTALL_FAILED_INVALID_APK;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                ret = PackageManager.INSTALL_FAILED_INVALID_URI;
            } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
                ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
            } else {
                // Override with defaults if needed.
                loc = installLocationPolicy(pkgLite);

                final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;

                if (!onInt) {
                    // Override install location with flags
                    if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                        // Set the flag to install on external media.
                        installFlags &= ~PackageManager.INSTALL_INTERNAL;
                    } else {
                        // Make sure the flag for installing on external
                        // media is unset
                        installFlags |= PackageManager.INSTALL_INTERNAL;
                    }
                }
            }
            return ret;
        }

可以看到的是,如果pkgLite的recommendedInstallLocation为RECOMMEND_FAILED_INSUFFICIENT_STORAGE时,从上面我们知道,这是由于存储空间不足了。在这种情况下,它会尝试去释放一些空间,调用的是mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0)。在释放一些空间之后,接着再去执行一遍PackageManagerServiceUtils.getMinimalPackageInfo(),看看能不能后续得到满足的结果。

接下来,判断pkgLite.recommendedInstallLocation的值,如果是以PackageManager.INSTALL_FAILED开头的结果,都直接设置返回结果。

如果不是失败的结果,会接着调用installLocationPolicy(pkgLite)。该方法是又去根据该应用之前的安装包信息来再次做判断。例如,安装包是系统应用包,则返回PackageHelper.RECOMMEND_INSTALL_INTERNAL。在配置文件没有明确指定位置的情况下,安装包在外部存储空间,则返回PackageHelper.RECOMMEND_INSTALL_EXTERNAL。如果没有之前的安装包,则返回它自己的推荐值。

如果installFlags里面有INSTALL_INTERNAL标识,则结果返回INSTALL_SUCCEEDED。如果没有INSTALL_INTERNAL标识,假如loc为不等于PackageHelper.RECOMMEND_INSTALL_EXTERNAL,则将installFlags里加上INSTALL_INTERNAL标识。

InstallParams 类的handleReturnCode()

它执行processPendingInstall()方法:

java 复制代码
        private void processPendingInstall() {
            InstallArgs args = createInstallArgs(this);
            if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                mRet = args.copyApk();
            }
            if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                F2fsUtils.releaseCompressedBlocks(
                        mContext.getContentResolver(), new File(args.getCodePath()));
            }
            if (mParentInstallParams != null) {
                mParentInstallParams.tryProcessInstallRequest(args, mRet);
            } else {
                PackageInstalledInfo res = createPackageInstalledInfo(mRet);
                processInstallRequestsAsync(
                        res.returnCode == PackageManager.INSTALL_SUCCEEDED,
                        Collections.singletonList(new InstallRequest(args, res)));
            }
        }
    }

首先通过createInstallArgs(this)生成一个InstallArgs 对象。将InstallParams 里的信息,转到InstallArgs 类对象中。在这里,对于我们的例子,它是FileInstallArgs对象。

接着mRet == PackageManager.INSTALL_SUCCEEDED的情况下,这里会执行FileInstallArgs对象的copyApk()。FileInstallArgs对象的copyApk(),如果文件还没有缓存到对应的文件夹下面,需要执行复制。如果文件已经复制过,则不需要再次复制,直接返回INSTALL_SUCCEEDED结果。像例子中的,目前已经复制过,所以直接返回INSTALL_SUCCEEDED结果。

下面mRet ==PackageManager.INSTALL_SUCCEEDED的情况下,如果文件系统是F2Fs,启动压缩情况下,需要先释放压缩的数据块,以备下面读取使用,所以调用F2fsUtils.releaseCompressedBlocks()方法。

如果mParentInstallParams不为null,它是一个MultiPackageInstallParams对象,用来在多包安装时使用,目前的InstallParams对象是多包安装中的一个。需要调用MultiPackageInstallParams类的tryProcessInstallRequest()方法。

如果是单文件安装包,则先生成一个PackageInstalledInfo对象res。然后将InstallArgs 对象和res封装到InstallRequest对象中。最后是调用processInstallRequestsAsync()方法来执行后续安装。

处理安装请求

看下processInstallRequestsAsync()代码:

java 复制代码
    // Queue up an async operation since the package installation may take a little while.
    private void processInstallRequestsAsync(boolean success,
            List<InstallRequest> installRequests) {
        mHandler.post(() -> {
            List<InstallRequest> apexInstallRequests = new ArrayList<>();
            List<InstallRequest> apkInstallRequests = new ArrayList<>();
            for (InstallRequest request : installRequests) {
                if ((request.args.installFlags & PackageManager.INSTALL_APEX) != 0) {
                    apexInstallRequests.add(request);
                } else {
                    apkInstallRequests.add(request);
                }
            }
            // Note: supporting multi package install of both APEXes and APKs might requir some
            // thinking to ensure atomicity of the install.
            if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
                // This should've been caught at the validation step, but for some reason wasn't.
                throw new IllegalStateException(
                        "Attempted to do a multi package install of both APEXes and APKs");
            }
            if (!apexInstallRequests.isEmpty()) {
                if (success) {
                    // Since installApexPackages requires talking to external service (apexd), we
                    // schedule to run it async. Once it finishes, it will resume the install.
                    Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
                            "installApexPackages");
                    t.start();
                } else {
                    // Non-staged APEX installation failed somewhere before
                    // processInstallRequestAsync. In that case just notify the observer about the
                    // failure.
                    InstallRequest request = apexInstallRequests.get(0);
                    notifyInstallObserver(request.installResult, request.args.observer);
                }
                return;
            }
            if (success) {
                for (InstallRequest request : apkInstallRequests) {
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                synchronized (mInstallLock) {
                    installPackagesTracedLI(apkInstallRequests);
                }
                for (InstallRequest request : apkInstallRequests) {
                    request.args.doPostInstall(
                            request.installResult.returnCode, request.installResult.uid);
                }
            }
            for (InstallRequest request : apkInstallRequests) {
                restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
                        new PostInstallData(request.args, request.installResult, null));
            }
        });
    }

参数installRequests是List格式,因为多包安装,也是调用的它。

显然是将操作封装成消息了,mHandler发送的消息是在叫"PackageManager"的线程中执行的。

这里有Apex安装方式的处理。可以看到,多包装时,是不能既用Apex,又用Apk安装方式的。咱们这里主要看Apk的安装方式。

在目前没错误的情况下,会调用installPackagesTracedLI(apkInstallRequests)执行安装。在安装之前和之后,都会执行对应InstallArgs对象的doPreInstall和doPostInstall方法。从名字能看出来,是安装之前的操作和安装之后的操作。由于这里是FileInstallArgs类对象,所以执行的是FileInstallArgs类的doPreInstall和doPostInstall方法。FileInstallArgs类的doPreInstall和doPostInstall方法都是根据参数状态,如果不是INSTALL_SUCCEEDED,则执行清理操作。具体看下面。咱们这里按照还没发生错误的情况下,继续往下看。

restoreAndPostInstall()方法在不是升级包的情况下(首次安装属于这种情况),会向备份管理服务要求执行回复数据,当然是在合适的情况下(备份服务可能不会启动,得配置PackageManager.FEATURE_BACKUP Feature,才会启动),才会具体执行。如果它是替换升级的情况,它还可能会向RollbackManager请求回退数据,得配置安装参数中的PackageManager.INSTALL_ENABLE_ROLLBACK或者PackageManager.INSTALL_REQUEST_DOWNGRADE标识。如果这两个都没满足条件,它会做安装之后的工作。在这里它是向"PackageManager"线程发送POST_INSTALL消息。它的处理实现在PackageHandler的handleMessage(Message msg)方法中,在处理POST_INSTALL消息时,会调用handlePackagePostInstall()方法,详见下面

安装

接下来继续查看installPackagesTracedLI(List requests)的实现:

java 复制代码
    @GuardedBy("mInstallLock")
    private void installPackagesTracedLI(List<InstallRequest> requests) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
            installPackagesLI(requests);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

主要是调用installPackagesLI(requests),继续看它的实现:

java 复制代码
    /**
     * Installs one or more packages atomically. This operation is broken up into four phases:
     * <ul>
     *     <li><b>Prepare</b>
     *         <br/>Analyzes any current install state, parses the package and does initial
     *         validation on it.</li>
     *     <li><b>Scan</b>
     *         <br/>Interrogates the parsed packages given the context collected in prepare.</li>
     *     <li><b>Reconcile</b>
     *         <br/>Validates scanned packages in the context of each other and the current system
     *         state to ensure that the install will be successful.
     *     <li><b>Commit</b>
     *         <br/>Commits all scanned packages and updates system state. This is the only place
     *         that system state may be modified in the install flow and all predictable errors
     *         must be determined before this phase.</li>
     * </ul>
     *
     * Failure at any phase will result in a full failure to install all packages.
     */
    @GuardedBy("mInstallLock")
    private void installPackagesLI(List<InstallRequest> requests) {
        final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
        final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
        final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
        final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
        final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
        final Map<String, PackageSetting> lastStaticSharedLibSettings =
                new ArrayMap<>(requests.size());
        final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
        boolean success = false;
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
            for (InstallRequest request : requests) {
                // TODO(b/109941548): remove this once we've pulled everything from it and into
                //                    scan, reconcile or commit.
                final PrepareResult prepareResult;
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
                    prepareResult =
                            preparePackageLI(request.args, request.installResult);
                } catch (PrepareFailure prepareFailure) {
                    request.installResult.setError(prepareFailure.error,
                            prepareFailure.getMessage());
                    request.installResult.origPackage = prepareFailure.conflictingPackage;
                    request.installResult.origPermission = prepareFailure.conflictingPermission;
                    return;
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
                request.installResult.installerPackageName =
                        request.args.installSource.installerPackageName;

                final String packageName = prepareResult.packageToScan.getPackageName();
                prepareResults.put(packageName, prepareResult);
                installResults.put(packageName, request.installResult);
                installArgs.put(packageName, request.args);
                try {
                    final ScanResult result = scanPackageTracedLI(
                            prepareResult.packageToScan, prepareResult.parseFlags,
                            prepareResult.scanFlags, System.currentTimeMillis(),
                            request.args.user, request.args.abiOverride);
                    if (null != preparedScans.put(result.pkgSetting.pkg.getPackageName(), result)) {
                        request.installResult.setError(
                                PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
                                "Duplicate package " + result.pkgSetting.pkg.getPackageName()
                                        + " in multi-package install request.");
                        return;
                    }
                    createdAppId.put(packageName, optimisticallyRegisterAppId(result));
                    versionInfos.put(result.pkgSetting.pkg.getPackageName(),
                            getSettingsVersionForPackage(result.pkgSetting.pkg));
                    if (result.staticSharedLibraryInfo != null) {
                        final PackageSetting sharedLibLatestVersionSetting =
                                getSharedLibLatestVersionSetting(result);
                        if (sharedLibLatestVersionSetting != null) {
                            lastStaticSharedLibSettings.put(result.pkgSetting.pkg.getPackageName(),
                                    sharedLibLatestVersionSetting);
                        }
                    }
                } catch (PackageManagerException e) {
                    request.installResult.setError("Scanning Failed.", e);
                    return;
                }
            }
            ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
                    installResults,
                    prepareResults,
                    mSharedLibraries,
                    Collections.unmodifiableMap(mPackages), versionInfos,
                    lastStaticSharedLibSettings);
            CommitRequest commitRequest = null;
            synchronized (mLock) {
                Map<String, ReconciledPackage> reconciledPackages;
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                    reconciledPackages = reconcilePackagesLocked(
                            reconcileRequest, mSettings.getKeySetManagerService(), mInjector);
                } catch (ReconcileFailure e) {
                    for (InstallRequest request : requests) {
                        request.installResult.setError("Reconciliation failed...", e);
                    }
                    return;
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
                    commitRequest = new CommitRequest(reconciledPackages,
                            mUserManager.getUserIds());
                    commitPackagesLocked(commitRequest);
                    success = true;
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
            }
            executePostCommitSteps(commitRequest);
        } finally {
            if (success) {
                for (InstallRequest request : requests) {
                    final InstallArgs args = request.args;
                    if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
                        continue;
                    }
                    if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
                        continue;
                    }
                    // For incremental installs, we bypass the verifier prior to install. Now
                    // that we know the package is valid, send a notice to the verifier with
                    // the root hash of the base.apk.
                    final String baseCodePath = request.installResult.pkg.getBaseApkPath();
                    final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();
                    final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
                    final int verificationId = mPendingVerificationToken++;
                    final String rootHashString = PackageManagerServiceUtils
                            .buildVerificationRootHashString(baseCodePath, splitCodePaths);
                    broadcastPackageVerified(verificationId, originUri,
                            PackageManager.VERIFICATION_ALLOW, rootHashString,
                            args.mDataLoaderType, args.getUser());
                }
            } else {
                for (ScanResult result : preparedScans.values()) {
                    if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),
                            false)) {
                        cleanUpAppIdCreation(result);
                    }
                }
                // TODO(patb): create a more descriptive reason than unknown in future release
                // mark all non-failure installs as UNKNOWN so we do not treat them as success
                for (InstallRequest request : requests) {
                    if (request.installResult.freezer != null) {
                        request.installResult.freezer.close();
                    }
                    if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                        request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
                    }
                }
            }
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

参数requests是List类型,是个集合,代表它可以安装1个或者多个应用,每个应用安装对应一个InstallRequest。通过注释可以看到,将安装分成了4个阶段:准备、浏览、协调、提交。当然翻译不一定准确,这里就这样称呼它们。

开始时按照参数requests的数量生成了好几个ArrayMap对象。prepareResults里的是准备阶段返回的结果,preparedScans里面是浏览阶段返回的结果,installArgs则是安装参数,installResults是安装结果,versionInfos里面放的是和存储卷相关的版本信息。lastStaticSharedLibSettings存放的则是最新版本的静态库对应的PackageSetting对象。createdAppId则是存放的对应应用的AppId是否创建。

下面根据请求数组,进行遍历。

preparePackageLI(request.args, request.installResult)是准备阶段的实现,具体见 Android 安装应用-准备阶段。如果准备阶段抛出异常PrepareFailure,将失败信息收集到request.installResult中。然后就直接整个返回,不再向下进行。如果没有抛出异常,将安装成功PackageManager.INSTALL_SUCCEEDED设置到request.installResult的返回代码中。然后收集准备结果,安装结果,安装参数,都是以包名为key。

scanPackageTracedLI()则是浏览阶段的实现,具体见文章 Android 安装应用-浏览阶段。然后将浏览结果对象result收集到集合preparedScans中,也是以包名作为key。继续将appId,存储卷的版本信息添加到createdAppId、versionInfos中。

如果应用是静态库,则得到最新版本的PackageSetting对象sharedLibLatestVersionSetting,如果它不为空,则将它放到lastStaticSharedLibSettings中。

下面就是退出循环了。接着使用之前的结果,构造ReconcileRequest对象reconcileRequest。

下面reconcilePackagesLocked()就是协商阶段的实现,详见 Android 应用安装-协调阶段

继续构造CommitRequest对象commitRequest,调用commitPackagesLocked(commitRequest),它是提交阶段的实现,详见文章 Android 应用安装-提交阶段。并且将变量success = true,代表提交成功。

executePostCommitSteps(commitRequest)则是提交之后的操作步骤,详见 Android 安装应用-提交阶段之后剩下的操作

继续往下,如果success为true,则代表安装成功。继续遍历安装请求,如果是增量安装,则跳出循环。如果不是增量安装,发送一个广播给验证者(注册接收Intent.ACTION_PACKAGE_VERIFIED的广播)。

如果success为false,则遍历浏览结果集合,如果在之前没有创建成功AppId,则这里调用cleanUpAppIdCreation(result)清理Settings对象中维持的appId。

在success为false的情况下,它接着遍历安装请求。将该应用对应的freezer关闭,其实就是将该包从PackageManagerService对象的成员mFrozenPackages中删除。对于安装结果为PackageManager.INSTALL_SUCCEEDED的,也将返回代码设置为PackageManager.INSTALL_UNKNOWN。

安装之后的处理

看下handlePackagePostInstall()的实现代码如下:

java 复制代码
    private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,
            boolean virtualPreload, boolean launchedForRestore, String installerPackage,
            IPackageInstallObserver2 installObserver, int dataLoaderType) {
        boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
        final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
        final String packageName = res.name;
        final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;
        final boolean removedBeforeUpdate = (pkgSetting == null)
                || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(res.pkg.getPath()));
        if (succeeded && removedBeforeUpdate) {
            Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
                    + "could be executed");
            res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED;
            res.returnMsg = "Package was removed before install could complete.";

            // Remove the update failed package's older resources safely now
            InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
            if (args != null) {
                synchronized (mInstallLock) {
                    args.doPostDeleteLI(true);
                }
            }
            notifyInstallObserver(res, installObserver);
            return;
        }

        if (succeeded) {
            // Clear the uid cache after we installed a new package.
            mPerUidReadTimeoutsCache = null;

            // Send the removed broadcasts
            if (res.removedInfo != null) {
                res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
            }

            final String installerPackageName =
                    res.installerPackageName != null
                            ? res.installerPackageName
                            : res.removedInfo != null
                                    ? res.removedInfo.installerPackageName
                                    : null;

            synchronized (mLock) {
                mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
            }

            // Determine the set of users who are adding this package for
            // the first time vs. those who are seeing an update.
            int[] firstUserIds = EMPTY_INT_ARRAY;
            int[] firstInstantUserIds = EMPTY_INT_ARRAY;
            int[] updateUserIds = EMPTY_INT_ARRAY;
            int[] instantUserIds = EMPTY_INT_ARRAY;
            final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0;
            final PackageSetting ps = pkgSetting;
            for (int newUser : res.newUsers) {
                final boolean isInstantApp = ps.getInstantApp(newUser);
                if (allNewUsers) {
                    if (isInstantApp) {
                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
                    } else {
                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
                    }
                    continue;
                }
                boolean isNew = true;
                for (int origUser : res.origUsers) {
                    if (origUser == newUser) {
                        isNew = false;
                        break;
                    }
                }
                if (isNew) {
                    if (isInstantApp) {
                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
                    } else {
                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
                    }
                } else {
                    if (isInstantApp) {
                        instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
                    } else {
                        updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
                    }
                }
            }

            // Send installed broadcasts if the package is not a static shared lib.
            if (res.pkg.getStaticSharedLibName() == null) {
                mProcessLoggingHandler.invalidateBaseApkHash(res.pkg.getBaseApkPath());

                // Send added for users that see the package for the first time
                // sendPackageAddedForNewUsers also deals with system apps
                int appId = UserHandle.getAppId(res.uid);
                boolean isSystem = res.pkg.isSystem();
                sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
                        virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
                        dataLoaderType);

                // Send added for users that don't see the package for the first time
                Bundle extras = new Bundle(1);
                extras.putInt(Intent.EXTRA_UID, res.uid);
                if (update) {
                    extras.putBoolean(Intent.EXTRA_REPLACING, true);
                }
                extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
                // Send to all running apps.
                final SparseArray<int[]> newBroadcastAllowList;

                synchronized (mLock) {
                    newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
                            getPackageSettingInternal(res.name, Process.SYSTEM_UID),
                            updateUserIds, mSettings.getPackagesLocked());
                }
                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                        extras, 0 /*flags*/,
                        null /*targetPackage*/, null /*finishedReceiver*/,
                        updateUserIds, instantUserIds, newBroadcastAllowList, null);
                if (installerPackageName != null) {
                    // Send to the installer, even if it's not running.
                    sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                            extras, 0 /*flags*/,
                            installerPackageName, null /*finishedReceiver*/,
                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
                }
                // if the required verifier is defined, but, is not the installer of record
                // for the package, it gets notified
                final boolean notifyVerifier = mRequiredVerifierPackage != null
                        && !mRequiredVerifierPackage.equals(installerPackageName);
                if (notifyVerifier) {
                    sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                            extras, 0 /*flags*/,
                            mRequiredVerifierPackage, null /*finishedReceiver*/,
                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
                }
                // If package installer is defined, notify package installer about new
                // app installed
                if (mRequiredInstallerPackage != null) {
                    sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                            extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
                            mRequiredInstallerPackage, null /*finishedReceiver*/,
                            firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
                }

                // Send replaced for users that don't see the package for the first time
                if (update) {
                    sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                            packageName, extras, 0 /*flags*/,
                            null /*targetPackage*/, null /*finishedReceiver*/,
                            updateUserIds, instantUserIds, res.removedInfo.broadcastAllowList,
                            null);
                    if (installerPackageName != null) {
                        sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                                extras, 0 /*flags*/,
                                installerPackageName, null /*finishedReceiver*/,
                                updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
                    }
                    if (notifyVerifier) {
                        sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                                extras, 0 /*flags*/,
                                mRequiredVerifierPackage, null /*finishedReceiver*/,
                                updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
                    }
                    sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                            null /*package*/, null /*extras*/, 0 /*flags*/,
                            packageName /*targetPackage*/,
                            null /*finishedReceiver*/, updateUserIds, instantUserIds,
                            null /*broadcastAllowList*/,
                            getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED)
                                    .toBundle());
                } else if (launchedForRestore && !res.pkg.isSystem()) {
                    // First-install and we did a restore, so we're responsible for the
                    // first-launch broadcast.
                    if (DEBUG_BACKUP) {
                        Slog.i(TAG, "Post-restore of " + packageName
                                + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
                    }
                    sendFirstLaunchBroadcast(packageName, installerPackage,
                            firstUserIds, firstInstantUserIds);
                }

                // Send broadcast package appeared if external for all users
                if (res.pkg.isExternalStorage()) {
                    if (!update) {
                        final StorageManager storage = mInjector.getSystemService(
                                StorageManager.class);
                        VolumeInfo volume =
                                storage.findVolumeByUuid(
                                        res.pkg.getStorageUuid().toString());
                        int packageExternalStorageType =
                                getPackageExternalStorageType(volume, res.pkg.isExternalStorage());
                        // If the package was installed externally, log it.
                        if (packageExternalStorageType != StorageEnums.UNKNOWN) {
                            FrameworkStatsLog.write(
                                    FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
                                    packageExternalStorageType, packageName);
                        }
                    }
                    if (DEBUG_INSTALL) {
                        Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
                    }
                    final int[] uidArray = new int[]{res.pkg.getUid()};
                    ArrayList<String> pkgList = new ArrayList<>(1);
                    pkgList.add(packageName);
                    sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
                }
            } else if (!ArrayUtils.isEmpty(res.libraryConsumers)) { // if static shared lib
                int[] allUsers = mInjector.getUserManagerService().getUserIds();
                for (int i = 0; i < res.libraryConsumers.size(); i++) {
                    AndroidPackage pkg = res.libraryConsumers.get(i);
                    // send broadcast that all consumers of the static shared library have changed
                    sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
                            new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
                            pkg.getUid(), null);
                }
            }

            // Work that needs to happen on first install within each user
            if (firstUserIds != null && firstUserIds.length > 0) {
                for (int userId : firstUserIds) {
                    restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
                            pkgSetting.getInstallReason(userId), userId);
                }
            }

            if (allNewUsers && !update) {
                notifyPackageAdded(packageName, res.uid);
            } else {
                notifyPackageChanged(packageName, res.uid);
            }

            // Log current value of "unknown sources" setting
            EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
                    getUnknownSourcesSettings());

            // Remove the replaced package's older resources safely now
            InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
            if (args != null) {
                if (!killApp) {
                    // If we didn't kill the app, defer the deletion of code/resource files, since
                    // they may still be in use by the running application. This mitigates problems
                    // in cases where resources or code is loaded by a new Activity before
                    // ApplicationInfo changes have propagated to all application threads.
                    scheduleDeferredNoKillPostDelete(args);
                } else {
                    synchronized (mInstallLock) {
                        args.doPostDeleteLI(true);
                    }
                }
            } else {
                // Force a gc to clear up things. Ask for a background one, it's fine to go on
                // and not block here.
                VMRuntime.getRuntime().requestConcurrentGC();
            }

            // Notify DexManager that the package was installed for new users.
            // The updated users should already be indexed and the package code paths
            // should not change.
            // Don't notify the manager for ephemeral apps as they are not expected to
            // survive long enough to benefit of background optimizations.
            for (int userId : firstUserIds) {
                PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
                // There's a race currently where some install events may interleave with an
                // uninstall. This can lead to package info being null (b/36642664).
                if (info != null) {
                    mDexManager.notifyPackageInstalled(info, userId);
                }
            }
        }

        final boolean deferInstallObserver = succeeded && update && !killApp;
        if (deferInstallObserver) {
            scheduleDeferredNoKillInstallObserver(res, installObserver);
        } else {
            notifyInstallObserver(res, installObserver);
        }
    }

succeeded为true代表安装成功,update为true代表是更新升级。removedBeforeUpdate代表更新失败或者是系统应用包更新,但是PackageSetting对象的文件路径和文件安装路径不一致,代表更新之前需要删除。

如果succeeded为true并且removedBeforeUpdate也为true的情况,会将res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED,将安装成功的返回码改成安装失败。并且如果res.removedInfo.args不为null的情况下,会执行删除旧包路径及其包下面内容。接着调用notifyInstallObserver(res, installObserver)执行回调。

接下来succeeded如果为true,代表着安装成功的情况。在res.removedInfo不为null的情况下,将调用它的sendPackageRemovedBroadcasts(killApp, false)发送包被删除的广播。在前面 Android 安装应用-准备阶段 中,会在更新安装的情况下,初始化res.removedInfo,更新安装涉及到旧包的删除,这里会发送包被删除的广播。res.removedInfo是PackageRemovedInfo对象,所以会调用它的sendPackageRemovedBroadcasts(killApp, false)方法,它会向能看到它的普通应用发送Intent.ACTION_PACKAGE_REMOVED广播,向安装该应用的应用发送Intent.ACTION_PACKAGE_REMOVED广播。向平台包应用(包名为"android")发送Intent.ACTION_PACKAGE_REMOVED_INTERNAL广播。如果数据都被删除,并且不是系统应用包更新的情况,它会向能看到它的普通应用发送Intent.ACTION_PACKAGE_FULLY_REMOVED广播。它还会向能看到它的普通应用发送Intent.ACTION_UID_REMOVED广播。

下面的四个数组变量firstUserIds、firstInstantUserIds、updateUserIds、instantUserIds是和用户有关,前面两个是首次添加该包的用户(分普通应用安装和Instant安装),后面两个是之前就添加过该包的用户,现在更新安装依然存在的用户。res.origUsers就是之前添加过该包的用户,res.newUsers则是目前更新添加该包的用户。这块处理的逻辑就是res.origUsers不存在,res.newUsers存在的就放入firstUserIds或firstInstantUserIds中;res.origUsers存在,res.newUsers存在的就放入updateUserIds或instantUserIds中。

res.pkg.getStaticSharedLibName() == null代表安装应用不是静态分享包,在该种情况下,sendPackageAddedForNewUsers()方法会向firstUserIds或firstInstantUserIds对应的能看到的普通应用发送Intent.ACTION_PACKAGE_ADDED广播。对于这些首次添加该包的用户,如果应用是系统应用或者虚拟预加载的,如果这些用户是运行状态,它会该用户的应用发送Intent.ACTION_LOCKED_BOOT_COMPLETED广播,如果该用户没有被锁定,它还会发送Intent.ACTION_BOOT_COMPLETED广播。

下面还会接着处理应用不是静态分享包时,还会向updateUserIds或instantUserIds中的普通应用发送Intent.ACTION_PACKAGE_ADDED广播。如果安装者应用不为null,向它发送Intent.ACTION_PACKAGE_ADDED广播。如果mRequiredVerifierPackage不为null,并且它不为安装者应用,则它向mRequiredVerifierPackage发送Intent.ACTION_PACKAGE_ADDED广播。如果包安装者定义了(mRequiredInstallerPackage != null),会向它发送Intent.ACTION_PACKAGE_ADDED广播。

如果是在更新安装的情况下,它会向updateUserIds或instantUserIds应用中的普通应用发送Intent.ACTION_PACKAGE_REPLACED广播,如果安装者应用不为null,向它发送Intent.ACTION_PACKAGE_REPLACED广播,如果mRequiredVerifierPackage不为null,并且它不为安装者应用,则它向mRequiredVerifierPackage发送Intent.ACTION_PACKAGE_REPLACED广播。它还会向自己发送Intent.ACTION_MY_PACKAGE_REPLACED广播。

如果不是更新安装,是恢复数据的普通应用,它将向第一次添加该包的用户的安装它的应用包发送Intent.ACTION_PACKAGE_FIRST_LAUNCH广播。

接下来如果包是在外部存储空间中,它将发送Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE广播,将该应用发生了变化进行通知。

下面进入else if (!ArrayUtils.isEmpty(res.libraryConsumers))分支,意味着它是一个库,被别的应用引用,res.libraryConsumers就是引用它的应用。它会发送Intent.ACTION_PACKAGE_CHANGED广播,通知其他能看到引用库的应用的其他普通应用发生了变化。

在安装的过程中,如果有新增的用户,会执行恢复新增用户的运行时权限,更新对应用户的更喜欢的Activity。

在allNewUsers为true,update为false的情况下,代表第一次安装,会通过notifyPackageAdded(packageName, res.uid),调用成员变量mPackageListObservers中的onPackageAdded接口。其他情况下,会调用成员变量mPackageListObservers中的onPackageChanged接口。

如果res.removedInfo.args不为null,会调用它的doPostDeleteLI(true)执行删除资源和代码。不过如果设置了不杀死应用的情况,会调用scheduleDeferredNoKillPostDelete(args)延迟删除。

对于新增的用户,调用mDexManager.notifyPackageInstalled(info, userId)通知包安装了。

最后就是处理返回结果。不过也分是否杀死应用两种情况,如果是不杀死应用的情况,也是延迟消息处理。最后都是通过调用notifyInstallObserver(res, installObserver)来处理。

将处理结果返回给应用

notifyInstallObserver(res, installObserver)的代码如下:

java 复制代码
    private void notifyInstallObserver(PackageInstalledInfo info,
            IPackageInstallObserver2 installObserver) {
        if (installObserver != null) {
            try {
                Bundle extras = extrasForInstallResult(info);
                installObserver.onPackageInstalled(info.name, info.returnCode,
                        info.returnMsg, extras);
            } catch (RemoteException e) {
                Slog.i(TAG, "Observer no longer exists.");
            }
        }
    }

可见是通过参数IPackageInstallObserver2 类型的onPackageInstalled接口返回。这个回调接口是来自安装参数FileInstallArgs类对象的成员变量observer。它又来自InstallParams类对象的observer。InstallParams类对象的创建来自PackageInstallerSession的makeInstallParams()。

java 复制代码
    @Nullable
    private PackageManagerService.InstallParams makeInstallParams()
            throws PackageManagerException {
            ......
         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
            @Override
            public void onUserActionRequired(Intent intent) {
                throw new IllegalStateException();
            }

            @Override
            public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                    Bundle extras) {
                if (isStaged()) {
                    sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
                } else {
                    // We've reached point of no return; call into PMS to install the stage.
                    // Regardless of success or failure we always destroy session.
                    destroyInternal();
                    dispatchSessionFinished(returnCode, msg, extras);
                }
            }
        };
        ......
        synchronized (mLock) {
            return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,
                    mSigningDetails, mInstallerUid, mPackageLite);
        }
    }                   

IPackageInstallObserver2 对象是通过构造InstallParams对象时作为参数传递过去的。

isStaged()代表这个安装是需要重启才执行安装。普通应用的安装是走else分支。

destroyInternal()是用来销毁session,因为目前已经安装完毕,有结果了。

dispatchSessionFinished(returnCode, msg, extras)用来派发结果。看一下它的代码:

java 复制代码
    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
        sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);

        synchronized (mLock) {
            mFinalStatus = returnCode;
            mFinalMessage = msg;
        }

        final boolean success = (returnCode == INSTALL_SUCCEEDED);

        // Send broadcast to default launcher only if it's a new install
        // TODO(b/144270665): Secure the usage of this broadcast.
        final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
        if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
            mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId);
        }

        mCallback.onSessionFinished(this, success);
        if (isDataLoaderInstallation()) {
            logDataLoaderInstallationSession(returnCode);
        }
    }

sendUpdateToRemoteStatusReceiver(returnCode, msg, extras)是向应用端发送广播安装结果。

给mFinalStatus、mFinalMessage赋值,存放结果。

如果是新安装,就是不是更新安装,并且安装成功的情况下,mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /icon /), userId)会发送对应的广播。

mCallback.onSessionFinished(this, success)主要是Session已经完成了,所以需要将它去除。

咱们这里主要看看sendUpdateToRemoteStatusReceiver(returnCode, msg, extras),代码如下:

java 复制代码
    private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
        final IntentSender statusReceiver;
        final String packageName;
        synchronized (mLock) {
            statusReceiver = mRemoteStatusReceiver;
            packageName = mPackageName;
        }
        if (statusReceiver != null) {
            // Execute observer.onPackageInstalled on different thread as we don't want callers
            // inside the system server have to worry about catching the callbacks while they are
            // calling into the session
            final SomeArgs args = SomeArgs.obtain();
            args.arg1 = packageName;
            args.arg2 = msg;
            args.arg3 = extras;
            args.arg4 = statusReceiver;
            args.argi1 = returnCode;
            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
        }
    }

这里的mRemoteStatusReceiver就是在 Android 安装过程一 界面跳转 中InstallInstalling Activity中session.commit(pendingIntent.getIntentSender())里的pendingIntent.getIntentSender()。pendingIntent是待发送的BROADCAST_ACTION广播(详见Android 安装过程一 界面跳转),它的接收是在安装进程中的InstallEventReceiver中,它是配置在Manifest文件中。

在这里是通过消息MSG_ON_PACKAGE_INSTALLED的处理,它的处理是在sendOnPackageInstalled()中,看一下:

java 复制代码
    private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
            boolean showNotification, int userId, String basePackageName, int returnCode,
            String msg, Bundle extras) {
        if (INSTALL_SUCCEEDED == returnCode && showNotification) {
            boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
            Notification notification = PackageInstallerService.buildSuccessNotification(context,
                    context.getResources()
                            .getString(update ? R.string.package_updated_device_owner :
                                    R.string.package_installed_device_owner),
                    basePackageName,
                    userId);
            if (notification != null) {
                NotificationManager notificationManager = (NotificationManager)
                        context.getSystemService(Context.NOTIFICATION_SERVICE);
                notificationManager.notify(basePackageName,
                        SystemMessageProto.SystemMessage.NOTE_PACKAGE_STATE,
                        notification);
            }
        }
        final Intent fillIn = new Intent();
        fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
        fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
        fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
                PackageManager.installStatusToPublicStatus(returnCode));
        fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
                PackageManager.installStatusToString(returnCode, msg));
        fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
        if (extras != null) {
            final String existing = extras.getString(
                    PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
            if (!TextUtils.isEmpty(existing)) {
                fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
            }
        }
        try {
            target.sendIntent(context, 0, fillIn, null, null);
        } catch (IntentSender.SendIntentException ignored) {
        }
    }

这里就是发送了BROADCAST_ACTION广播,咱们再看看安装进程中的接收者的处理。

安装进程中的接收者的处理是在EventResultPersister中的onEventReceived()中,代码如下:

java 复制代码
    void onEventReceived(@NonNull Context context, @NonNull Intent intent) {
        int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);

        if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
            context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));

            return;
        }

        int id = intent.getIntExtra(EXTRA_ID, 0);
        String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
        int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);

        EventResultObserver observerToCall = null;
        synchronized (mLock) {
            int numObservers = mObservers.size();
            for (int i = 0; i < numObservers; i++) {
                if (mObservers.keyAt(i) == id) {
                    observerToCall = mObservers.valueAt(i);
                    mObservers.removeAt(i);

                    break;
                }
            }

            if (observerToCall != null) {
                observerToCall.onResult(status, legacyStatus, statusMessage);
            } else {
                mResults.put(id, new EventResult(status, legacyStatus, statusMessage));
                writeState();
            }
        }
    }

注册的回调在成员变量mObservers,它是SparseArray类型。接收到广播之后,通过参数id来找到对应的回调函数,就可以回调对应的函数了。

再看一下它的注册回调也是在InstallInstalling Activity中,如下:

java 复制代码
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult)

所以最后会调用launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage)方法,再显示对应的界面。(详见Android 安装过程一 界面跳转)。

总结

安装这个过程还是挺复杂的,最主要的是它将之分成4个阶段:准备、浏览、协调、提交。每个步骤中都有好多代码,并且代码中处理不是只针对普通应用的安装,它是包括其他形式安装的处理,该文章主要是有关普通应用的安装,还是结合了前面文章的例子,将所有的流程过了一遍,其中的相关细节,还没有深究,留待后续继续琢磨。

相关推荐
橘子海全栈攻城狮2 小时前
【源码+文档+调试讲解】基于Android的固定资产借用管理平台
android·java·spring boot·后端·python·美食
服装学院的IT男2 小时前
【Android 源码分析】Activity生命周期之onStop-2
android
林多4 小时前
【Android】CarWatchDog I/O监控服务
android·watchdog·监控·car·disablepackage
机器之心4 小时前
这篇论文非常火!差分Transformer竟能消除注意力噪声,犹如降噪耳机
android·人工智能
golitter.4 小时前
MySql表结构设计 - 数据操作语言
android·数据库·mysql
GEEKVIP5 小时前
如何从数码相机中恢复已删除的照片
android·数码相机·macos·ios·电脑·手机·iphone
千里马学框架6 小时前
安卓如何实现双击触摸唤醒点亮屏幕功能-Android framework实战开发
android·车载系统·wms·安卓framework开发·安卓窗口系统·双击屏幕·唤醒
修行者对6668 小时前
Android Studio实现安卓心理健康咨询
android·ide·android studio
lizi888888 小时前
MYSQL-INNODB索引构成详解
android·数据库·mysql