Android中apk安装过程源码解析

本文中使用的Android源码基于Android 14

1 三方应用安装apk调用方法

java 复制代码
public void installApk() {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    /*
	 * 自Android N开始,是通过FileProvider共享相关文件,但是Android Q对公
	 * 有目录 File API进行了限制,只能通过Uri来操作
	 */
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
        // filePath是通过ContentResolver得到的
        intent.setDataAndType(Uri.parse(filePath),
                              "application/vnd.android.package-archive");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Uri contentUri = FileProvider.getUriForFile(mContext,
                                "com.test.file.fileProvider", file);
        intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
    } else {
        intent.setDataAndType(Uri.fromFile(file), 
                              "application/vnd.android.package-archive");
    }
    startActivity(intent);
}

然后再AndroidManifest.xml中声明权限

xml 复制代码
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> 

2 PackageInstaller流程

2.1弹出安装对话框

进入PackageInstaller后,首先会拉起PackageInstallerActivity。

java 复制代码
//framework/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@Override
protected void onResume() {
    super.onResume();

    if (mLocalLOGV) Log.i(TAG, "onResume(): mAppSnippet=" + mAppSnippet);

    if (mAppSnippet != null) {
        // load dummy layout with OK button disabled until we override this layout in
        // startInstallConfirm
        bindUi();
        checkIfAllowedAndInitiateInstall();
    }

    if (mOk != null) {
        mOk.setEnabled(mEnableOk);
    }
}

PackageInstallerActivity首先创建Ui,然后检查权限。

java 复制代码
private void bindUi() {
    mAlert.setIcon(mAppSnippet.icon);
    mAlert.setTitle(mAppSnippet.label);
    mAlert.setView(R.layout.install_content_view);
    mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
                     (ignored, ignored2) -> {
                         if (mOk.isEnabled()) {
                             if (mSessionId != -1) {
                                 setActivityResult(RESULT_OK);
                                 finish();
                             } else {
                                 startInstall();
                             }
                         }
                     }, null);
    mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
                     (ignored, ignored2) -> {
                         // Cancel and finish
                         setActivityResult(RESULT_CANCELED);
                         finish();
                     }, null);
    setupAlert();

    mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
    mOk.setEnabled(false);

    if (!mOk.isInTouchMode()) {
        mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
    }
}

/**
 * Check if it is allowed to install the package and initiate install if allowed.
 */
private void checkIfAllowedAndInitiateInstall() {
    if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
        if (mLocalLOGV) Log.i(TAG, "install allowed");
        initiateInstall();
    } else {
        handleUnknownSources();
    }
}

在checkIfAllowedAndInitiateInstall里面,狐妖是检查是否有安装来自未知来源的权限,如果有权限,那么在initiateInstall中就设置确定按钮为enable和visible。

在bindUi里面,当点击确认时,开始安装流程。

java 复制代码
private void startInstall() {
    String installerPackageName = getIntent().getStringExtra(
        Intent.EXTRA_INSTALLER_PACKAGE_NAME);
    int stagedSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);

    // 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);
    if (mOriginatingURI != null) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
    }
    if (mReferrerURI != null) {
        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
    }
    if (mOriginatingUid != Process.INVALID_UID) {
        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);
    }
    if (stagedSessionId > 0) {
        newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
    }
    newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
    startActivity(newIntent);
    finish();
}

这里startActivity启动的是InstallInstalling。

2.2 安装进度界面显示InstallInstalling

继续看InstallInstalling的启动。首先我们看下InstallInstalling在perfetto中的流程。

对应代码为:

java 复制代码
//framework/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    if (savedInstanceState != null) {
        ...
    } else {
        try {
            mInstallId = InstallEventReceiver
                .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                             this::launchFinishBasedOnResult);
        } catch (EventResultPersister.OutOfIdsException e) {
            launchFailure(PackageInstaller.STATUS_FAILURE,
                          PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
        }
}

@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);

        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);
        }
    }
}

首先在onCreate中会创建安装中的Ui界面,然后注册InstallEventReceiver来监听安装结果,并在launchFinishBasedOnResult中将接收到的安装结果进行处理。然后在onResume中开启安装session。

java 复制代码
/**
 * Send the package to the package installer and then register a event result observer that
 * will call {@link #launchFinishBasedOnResult(int, int, String, int)}
 */
private final class InstallingAsyncTask extends AsyncTask<Void, 
	Void, PackageInstaller.Session> {
    volatile boolean isDone;

    @Override
    protected PackageInstaller.Session doInBackground(Void... params) {
        try {
            return getPackageManager().getPackageInstaller().openSession(mSessionId);
        } catch (IOException e) {
            return null;
        } finally {
            synchronized (this) {
                isDone = true;
                notifyAll();
            }
        }
    }

    @Override
    protected void onPostExecute(PackageInstaller.Session session) {
        if (session != null) {
            Intent broadcastIntent = new Intent(BROADCAST_ACTION);
            broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
            broadcastIntent.setPackage(getPackageName());
            broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                InstallInstalling.this,
                mInstallId,
                broadcastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);

            session.commit(pendingIntent.getIntentSender());
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        } else {
            getPackageManager().getPackageInstaller().abandonSession(mSessionId);

            if (!isCancelled()) {
                launchFailure(PackageInstaller.STATUS_FAILURE,
                              PackageManager.INSTALL_FAILED_INVALID_APK, null);
            }
        }
    }
}

3 建立安装的Session

3.1 建立Session

在doInBackground中进行PackageInstaller#openSession,然后在onPostExecute中提交会话结果,来看PackageInstaller#openSession。

java 复制代码
//framework/base/core/java/android/content/pm/PackageInstaller.java
/**
 * Open an existing session to actively perform work. To succeed, the caller
 * must be the owner of the install session.
 *
 * @throws IOException if parameters were unsatisfiable, such as lack of
 *             disk space or unavailable media.
 * @throws SecurityException when the caller does not own the session, or
 *             the session is invalid.
 */
public @NonNull Session openSession(int sessionId) throws IOException {
    try {
        try {
            return new Session(mInstaller.openSession(sessionId));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (RuntimeException e) {
        ExceptionUtils.maybeUnwrapIOException(e);
        throw e;
    }
}

这里调用了mInstaller.openSession(sessionId),并new了一个Session。

java 复制代码
public static class Session implements Closeable {}

mInstaller是IPackageInstaller,也就是PackageInstaller的服务端PackageInstallerService。

java 复制代码
//framework/base/services/core/java/com/android/server/pm/PackageInstallerService.java
@Override
public IPackageInstallerSession openSession(int sessionId) {
    try {
        return openSessionInternal(sessionId);
    } catch (IOException e) {
        throw ExceptionUtils.wrap(e);
    }
}

private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
    synchronized (mSessions) {
        final PackageInstallerSession session = mSessions.get(sessionId);
        if (!checkOpenSessionAccess(session)) {
            throw new SecurityException("Caller has no access to session " + sessionId);
        }
        session.open();
        return session;
    }
}

checkOpenSessionAccess进行简单校验之后,直接调用PackageInstallerSession的open()。

java 复制代码
//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void open() throws IOException {
    activate();
    boolean wasPrepared;
    synchronized (mLock) {
        wasPrepared = mPrepared;
        if (!mPrepared) {
            if (stageDir != null) {
                prepareStageDir(stageDir);
            } else if (params.isMultiPackage) {
                // it's all ok
            } else {
                throw new IllegalArgumentException("stageDir must be set");
            }

            mPrepared = true;
        }
    }

    if (!wasPrepared) {
        mCallback.onSessionPrepared(this);
    }
}

这里也很简单,在prepareStageDir里面进行目录创建和selinux相关配置。

这里有一个疑问,stageDir是什么?

java 复制代码
/** Staging location where client data is written. */
final File stageDir;

它是一个file,那它的路径是哪里呢?继续找

java 复制代码
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
            Context context, PackageManagerService pm,
            PackageSessionProvider sessionProvider,
            SilentUpdatePolicy silentUpdatePolicy, Looper looper, StagingManager stagingManager,
            int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
            SessionParams params, long createdMillis, long committedMillis,
            File stageDir, String stageCid, InstallationFile[] files,
            ArrayMap<String, PerFileChecksum> checksums,
            boolean prepared, boolean committed, boolean destroyed, boolean sealed,
            @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
            boolean isFailed, boolean isApplied, int sessionErrorCode,
            String sessionErrorMessage) {
    
}

public static PackageInstallerSession readFromXml(@NonNull TypedXmlPullParser in,
            @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
            @NonNull PackageManagerService pm, Looper installerThread,
            @NonNull StagingManager stagingManager, @NonNull File sessionsDir,
            @NonNull PackageSessionProvider sessionProvider,
            @NonNull SilentUpdatePolicy silentUpdatePolicy)
            throws IOException, XmlPullParserException {
    final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
    final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
    
    InstallSource installSource = InstallSource.create(installInitiatingPackageName,
           	installOriginatingPackageName, installerPackageName, installPackageUid,
            updateOwnerPackageName, installerAttributionTag, params.packageSource);
    return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 silentUpdatePolicy, installerThread, stagingManager, sessionId,
                 userId,installerUid, installSource, params, createdMillis, 
                 committedMillis, stageDir,stageCid, fileArray, checksumsMap,
                 prepared, committed, destroyed, sealed,childSessionIdsArray,
                 parentSessionId, isReady, isFailed, isApplied,
                 sessionErrorCode, sessionErrorMessage);
}

private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
java 复制代码
//framework/base/services/core/java/com/android/server/pm/PackageInstallerService.java
private void readSessionsLocked() {
    while ((type = in.next()) != END_DOCUMENT) {
        if (type == START_TAG) {
            final String tag = in.getName();
            if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
                final PackageInstallerSession session;
                try {
                    session = PackageInstallerSession.readFromXml(in,
                               mInternalCallback, mContext, mPm,
                               mInstallThread.getLooper(), mStagingManager,
                               mSessionsDir, this, mSilentUpdatePolicy);
                } catch (Exception e) {
                    Slog.e(TAG, "Could not read session", e);
                    continue;
                }
                mSessions.put(session.sessionId, session);
                mAllocatedSessions.put(session.sessionId, true);
            }
        }
    }
    // After reboot housekeeping.
    for (int i = 0; i < mSessions.size(); ++i) {
        PackageInstallerSession session = mSessions.valueAt(i);
        session.onAfterSessionRead(mSessions);
    }
}

public PackageInstallerService(Context context, PackageManagerService pm,
            Supplier<PackageParser2> apexParserSupplier) {
    ...
    mSessionsFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), 
                 "install_sessions.xml"), "package-session");
    ...
}

public void systemReady() {
    synchronized (mSessions) {
        readSessionsLocked();
        ...
    }
}

//framework/base/services/core/java/com/android/server/pm/PackageManagerService.java
public void systemReady() {
    mInstallerService.systemReady();
}

终于理清楚了,首先在PKMS启动之后的systemReady中,通知PackageInstallerService的systemReady,然后systemReady中读取保存的Sessions,读取的地方是/data/system/install_sessions.xml中的package-session的tag。然后在该tag下读取sessionStageDir即是stageDir。

3.2 预检验apk

在readSessionsLocked最后,会走session.onAfterSessionRead(mSessions)。

java 复制代码
//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@AnyThread
void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
    synchronized (mLock) {
        ...
        if (root != null && !root.isStagedAndInTerminalState()) {
            if (isApexSession()) {
                validateApexInstallLocked();
            } else {
                validateApkInstallLocked();
            }
        }
        ...
    }
}

/**
 * Validate install by confirming that all application packages are have
 * consistent package name, version code, and signing certificates.
 * <p>
 * Clears and populates {@link #mResolvedBaseFile},
 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
 * <p>
 * Renames package files in stage to match split names defined inside.
 * <p>
 * Note that upgrade compatibility is still performed by
 * {@link PackageManagerService}.
 * @return a {@link PackageLite} representation of the validated APK(s).
 */
@GuardedBy("mLock")
private PackageLite validateApkInstallLocked() throws PackageManagerException {
    for (File addedFile : addedFiles) {
            final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(
                input.reset(), addedFile,
                ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
    }
}

parseApkLite中传入的flag是PARSE_COLLECT_CERTIFICATES。

java 复制代码
//framework/base/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
/**
 * Utility method that retrieves lightweight details about a single APK
 * file, including package name, split name, and install location.
 *
 * @param apkFile path to a single APK
 * @param flags optional parse flags, such as
 *            {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
 */
public static ParseResult<ApkLite> parseApkLite(ParseInput input, File apkFile,
                                                int flags) {
    return parseApkLiteInner(input, apkFile, null, null, flags);
}

private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
                File apkFile, FileDescriptor fd, String debugPathName, int flags) {
    final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();

    XmlResourceParser parser = null;
    ApkAssets apkAssets = null;
    try {
        try {
            apkAssets = fd != null
                ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
                : ApkAssets.loadFromPath(apkPath);
        } catch (IOException e) {
            return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
                               "Failed to parse " + apkPath, e);
        }

        parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);

        final SigningDetails signingDetails;
        if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
            final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
            try {
                final ParseResult<SigningDetails> result =
                    FrameworkParsingPackageUtils.getSigningDetails(input,
                                                                   apkFile.getAbsolutePath(),
                                                                   skipVerify, /* isStaticSharedLibrary */ false,
                                                                   SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
                if (result.isError()) {
                    return input.error(result);
                }
                signingDetails = result.getResult();
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
        } else {
            signingDetails = SigningDetails.UNKNOWN;
        }

        return parseApkLite(input, apkPath, parser, signingDetails, flags);
    } catch (XmlPullParserException | IOException | RuntimeException e) {
        Slog.w(TAG, "Failed to parse " + apkPath, e);
        return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                           "Failed to parse " + apkPath, e);
    } finally {
        IoUtils.closeQuietly(parser);
        if (apkAssets != null) {
            try {
                apkAssets.close();
            } catch (Throwable ignored) {
            }
        }
        // TODO(b/72056911): Implement AutoCloseable on ApkAssets.
    }
}

parseApkLiteInner中首先会加载ApkAssets,对应perfetto中加载ApkAssets的load阶段。

java 复制代码
private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
                  File apkFile, FileDescriptor fd, String debugPathName, int flags) {
    parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);

    final SigningDetails signingDetails;
    if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
        final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
        try {
            final ParseResult<SigningDetails> result =
                FrameworkParsingPackageUtils.getSigningDetails(input,                                          apkFile.getAbsolutePath(),skipVerify
                         , /*isStaticSharedLibrary */ false,                      
                         SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
            if (result.isError()) {
                return input.error(result);
            }
            signingDetails = result.getResult();
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    } else {
        signingDetails = SigningDetails.UNKNOWN;
    }
    return parseApkLite(input, apkPath, parser, signingDetails, flags);
}

FrameworkParsingPackageUtils.getSigningDetails中主要进行签名校验,在perfetto中的表现:

最后parseApkLite会返回在AndroidManifest.xml中解析的信息。

继续看前面InstallingAsyncTask在onPostExecute中session.commit(pendingIntent.getIntentSender())

java 复制代码
//framework/base/core/java/android/content/pm/PackageInstaller.java
public void commit(@NonNull IntentSender statusReceiver) {
    try {
        mSession.commit(statusReceiver, false);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    assertNotChild("commit");

    if (!markAsSealed(statusReceiver, forTransfer)) {
        return;
    }
    if (isMultiPackage()) {
        synchronized (mLock) {
            boolean sealFailed = false;
            for (int i = mChildSessions.size() - 1; i >= 0; --i) {
                // seal all children, regardless if any of them fail; we'll throw/return
                // as appropriate once all children have been processed
                if (!mChildSessions.valueAt(i).markAsSealed(null, forTransfer)) {
                    sealFailed = true;
                }
            }
            if (sealFailed) {
                return;
            }
        }
    }

    File appMetadataFile = getStagedAppMetadataFile();
    if (appMetadataFile != null) {
        long sizeLimit = getAppMetadataSizeLimit();
        if (appMetadataFile.length() > sizeLimit) {
            appMetadataFile.delete();
            throw new IllegalArgumentException(
                "App metadata size exceeds the maximum allowed limit of " + sizeLimit);
        }
        if (isIncrementalInstallation()) {
            // Incremental requires stageDir to be empty so move the app metadata file to a
            // temporary location and move back after commit.
            appMetadataFile.renameTo(getTmpAppMetadataFile());
        }
    }

    dispatchSessionSealed();
}

private void dispatchSessionSealed() {
    mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}

private void handleSessionSealed() {
        assertSealed("dispatchSessionSealed");
        // Persist the fact that we've sealed ourselves to prevent
        // mutations of any hard links we create.
        mCallback.onSessionSealedBlocking(this);
        dispatchStreamValidateAndCommit();
    }

private void dispatchStreamValidateAndCommit() {
    mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}

@WorkerThread
private void handleStreamValidateAndCommit() {
    try {
        // This will track whether the session and any children were validated and are ready to
        // progress to the next phase of install
        boolean allSessionsReady = true;
        for (PackageInstallerSession child : getChildSessions()) {
            allSessionsReady &= child.streamValidateAndCommit();
        }
        if (allSessionsReady && streamValidateAndCommit()) {
            mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
        }
    } catch (PackageManagerException e) {
        destroy();
        String msg = ExceptionUtils.getCompleteMessage(e);
        dispatchSessionFinished(e.error, msg, null);
        maybeFinishChildSessions(e.error, msg);
    }
}

@WorkerThread
private void handleInstall() {
    if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
        DevicePolicyEventLogger
            .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
            .setAdmin(getInstallSource().mInstallerPackageName)
            .write();
    }

    /**
         * Stops the installation of the whole session set if one session needs user action
         * in its belong session set. When the user answers the yes,
         * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
         * handled to come back here to check again.
         *
         * {@code mUserActionRequired} is used to track when user action is required for an
         * install. Since control may come back here more than 1 time, we must ensure that it's
         * value is not overwritten.
         */
    boolean wasUserActionIntentSent = sendPendingUserActionIntentIfNeeded();
    if (mUserActionRequired == null) {
        mUserActionRequired = wasUserActionIntentSent;
    }
    if (wasUserActionIntentSent) {
        // Commit was keeping session marked as active until now; release
        // that extra refcount so session appears idle.
        deactivate();
        return;
    } else if (mUserActionRequired) {
        // If user action is required, control comes back here when the user allows
        // the installation. At this point, the session is marked active once again,
        // since installation is in progress.
        activate();
    }

    if (mVerificationInProgress) {
        Slog.w(TAG, "Verification is already in progress for session " + sessionId);
        return;
    }
    mVerificationInProgress = true;

    if (params.isStaged) {
        mStagedSession.verifySession();
    } else {
        verify();
    }
}

private void verify() {
    try {
        List<PackageInstallerSession> children = getChildSessions();
        if (isMultiPackage()) {
            for (PackageInstallerSession child : children) {
                child.prepareInheritedFiles();
                child.parseApkAndExtractNativeLibraries();
            }
        } else {
            prepareInheritedFiles();
            parseApkAndExtractNativeLibraries();
        }
        verifyNonStaged();
    } catch (PackageManagerException e) {
        final String completeMsg = ExceptionUtils.getCompleteMessage(e);
        final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
        setSessionFailed(e.error, errorMsg);
        onSessionVerificationFailure(e.error, errorMsg);
    }
}

verify中会调用prepareInheritedFiles()在/data/app/的app安装目录下创建oat的目录和/lib/arm64,最后调用verifyNonStaged。

java 复制代码
private void verifyNonStaged()
    throws PackageManagerException {
    synchronized (mLock) {
        markStageDirInUseLocked();
    }
    mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
        mHandler.post(() -> {
            if (dispatchPendingAbandonCallback()) {
                // No need to continue if abandoned
                return;
            }
            if (error == INSTALL_SUCCEEDED) {
                onVerificationComplete();
            } else {
                onSessionVerificationFailure(error, msg);
            }
        });
    });
}

mSessionProvider.getSessionVerifier().verify调用的是PackageSessionVerifier的verify。

java 复制代码
//framework/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
/**
 * Runs verifications that are common to both staged and non-staged sessions.
 */
public void verify(PackageInstallerSession session, Callback callback) {
    mHandler.post(() -> {
        try {
            storeSession(session.mStagedSession);
            if (session.isMultiPackage()) {
                for (PackageInstallerSession child : session.getChildSessions()) {
                    checkApexUpdateAllowed(child);
                    checkRebootlessApex(child);
                    checkApexSignature(child);
                }
            } else {
                checkApexUpdateAllowed(session);
                checkRebootlessApex(session);
                checkApexSignature(session);
            }
            verifyAPK(session, callback);
        } catch (PackageManagerException e) {
            String errorMessage = PackageManager.installStatusToString(e.error, e.getMessage());
            session.setSessionFailed(e.error, errorMessage);
            callback.onResult(e.error, e.getMessage());
        }
    });
}

/**
 * Runs verifications particular to APK. This includes APEX sessions since an APEX can also
 * be treated as APK.
 */
private void verifyAPK(PackageInstallerSession session, Callback callback)
    throws PackageManagerException {
    final IPackageInstallObserver2 observer = 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 (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
                // Continue verification for staged sessions
                verifyStaged(session.mStagedSession, callback);
                return;
            }
            if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
                String errorMessage = PackageManager.installStatusToString(returnCode, msg);
                session.setSessionFailed(returnCode, errorMessage);
                callback.onResult(returnCode, msg);
            } else {
                session.setSessionReady();
                callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
            }
        }
    };
    final VerifyingSession verifyingSession = createVerifyingSession(session, observer);
    if (session.isMultiPackage()) {
        final List<PackageInstallerSession> childSessions = session.getChildSessions();
        List<VerifyingSession> verifyingChildSessions = new ArrayList<>(childSessions.size());
        for (PackageInstallerSession child : childSessions) {
            verifyingChildSessions.add(createVerifyingSession(child, null));
        }
        verifyingSession.verifyStage(verifyingChildSessions);
    } else {
        verifyingSession.verifyStage();
    }
}

private VerifyingSession createVerifyingSession(
    PackageInstallerSession session, IPackageInstallObserver2 observer) {
    final UserHandle user;
    if ((session.params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
        user = UserHandle.ALL;
    } else {
        user = new UserHandle(session.userId);
    }
    return new VerifyingSession(user, session.stageDir, observer, session.params,
                  session.getInstallSource(), session.getInstallerUid(),
                  session.getSigningDetails(),session.sessionId,
                  session.getPackageLite(), session.getUserActionRequired(), mPm);
}

verify调用verifyAPK,verifyAPK中会创建一个observer来接收安装进度结果,然后把observer传入VerifyingSession中,最后调动VerifyingSession的verifyStage()。

java 复制代码
//framework/base/services/core/java/com/android/server/pm/VerifyingSession.java
public void verifyStage() {
    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueVerify",
                          System.identityHashCode(this));
    mPm.mHandler.post(this::start);
}

对应perfetto中的部分如下:

queueVerify结束之后,就开始start流程,对应perfetto中如下部分:

java 复制代码
private void start() {
    if (DEBUG_INSTALL) Slog.i(TAG, "start " + mUser + ": " + this);
    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueVerify",
                        System.identityHashCode(this));
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "start");
    handleStartVerify();
    handleReturnCode();
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

public void handleStartVerify() {
    PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(
        mPm.mContext, mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
        mPackageAbiOverride);

    Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
        pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
    setReturnCode(ret.first, ret.second);
    if (mRet != INSTALL_SUCCEEDED) {
        return;
    }

    // Perform package verification and enable rollback (unless we are simply moving the
    // package).
    if (!mOriginInfo.mExisting) {
        if (!isApex()) {
            // TODO(b/182426975): treat APEX as APK when APK verification is concerned
            sendApkVerificationRequest(pkgLite);
        }
        if ((mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
            sendEnableRollbackRequest();
        }
    }
}

void handleReturnCode() {
    if (mWaitForVerificationToComplete || mWaitForIntegrityVerificationToComplete
        || mWaitForEnableRollbackToComplete) {
        return;
    }
    sendVerificationCompleteNotification();
    if (mRet != INSTALL_SUCCEEDED) {
        PackageMetrics.onVerificationFailed(this);
    }
}

private void sendVerificationCompleteNotification() {
    if (mParentVerifyingSession != null) {
        mParentVerifyingSession.trySendVerificationCompleteNotification(this);
    } else {
        try {
            mObserver.onPackageInstalled(null, mRet, mErrorMessage,
                                         new Bundle());
        } catch (RemoteException e) {
            Slog.i(TAG, "Observer no longer exists.");
        }
    }
}

首先在handleStartVerify中进行一些校验,然后通知mObserver进行onPackageInstalled。

PackageSessionVerifier中收到onPackageInstalled后,做了下面两件事。

java 复制代码
session.setSessionReady();
callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);

setSessionReady中,主要进行一些变量的赋值,

java 复制代码
void setSessionReady() {
    synchronized (mLock) {
        // Do not allow destroyed/failed session to change state
        if (mDestroyed || mSessionFailed) return;
        mSessionReady = true;
        mSessionApplied = false;
        mSessionFailed = false;
        mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
        mSessionErrorMessage = "";
    }
    mCallback.onSessionChanged(this);
}

而callback.onResult(PackageManager.INSTALL_SUCCEEDED, null)中主要是调用onVerificationComplete()。

4 apk的install流程

java 复制代码
@WorkerThread
private void onVerificationComplete() {
    if (isStaged()) {
        mStagingManager.commitSession(mStagedSession);
        sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
        return;
    }
    install();
}

/**
 * Stages installs and do cleanup accordingly depending on whether the installation is
 * successful or not.
 *
 * @return a future that will be completed when the whole process is completed.
 */
private CompletableFuture<Void> install() {
    List<CompletableFuture<InstallResult>> futures = installNonStaged();
    CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
    return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
        if (t == null) {
            setSessionApplied();
            for (CompletableFuture<InstallResult> f : futures) {
                InstallResult result = f.join();
                result.session.dispatchSessionFinished(
                    INSTALL_SUCCEEDED, "Session installed", result.extras);
            }
        } else {
            PackageManagerException e = (PackageManagerException) t.getCause();
            setSessionFailed(e.error,
                             PackageManager.installStatusToString(e.error, e.getMessage()));
            dispatchSessionFinished(e.error, e.getMessage(), null);
            maybeFinishChildSessions(e.error, e.getMessage());
        }
    });
}

/**
 * Stages sessions (including child sessions if any) for install.
 *
 * @return a list of futures to indicate the install results of each session.
 */
private List<CompletableFuture<InstallResult>> installNonStaged() {
	final InstallingSession installingSession = createInstallingSession(future);
    installingSession.installStage();
    return futures;
}

public void installStage() {
        setTraceMethod("installStage").setTraceCookie(System.identityHashCode(this));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
                System.identityHashCode(this));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(this));
        mPm.mHandler.post(this::start);
    }

install最后走到了installStage,对应的perfetto如下:

java 复制代码
private void start() {
    if (DEBUG_INSTALL) Slog.i(TAG, "start " + mUser + ": " + this);
    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                        System.identityHashCode(this));
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startInstall");
    InstallRequest installRequest = new InstallRequest(this);
    handleStartCopy(installRequest);
    handleReturnCode(installRequest);
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

installStage会走到mPm.mHandler.post(this::start), 对应的perfetto如下:

handleStartCopy中主要是获取安装目录信息。再来看handleReturnCode

java 复制代码
private void handleReturnCode(InstallRequest installRequest) {
    processPendingInstall(installRequest);
}

private void processPendingInstall(InstallRequest installRequest) {
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        mRet = copyApk(installRequest);
    }
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        F2fsUtils.releaseCompressedBlocks(
            mPm.mContext.getContentResolver(), new File(installRequest.getCodePath()));
    }
    installRequest.setReturnCode(mRet);
    if (mParentInstallingSession != null) {
        mParentInstallingSession.tryProcessInstallRequest(installRequest);
    } else {
        // Queue up an async operation since the package installation may take a little while.
        mPm.mHandler.post(() -> processInstallRequests(
            mRet == PackageManager.INSTALL_SUCCEEDED /* success */,
            Collections.singletonList(installRequest)));
    }
}

4.1 apk和二进制拷贝

首先拷贝apk。

java 复制代码
private int copyApk(InstallRequest request) {
    if (mMoveInfo == null) {
        return copyApkForFileInstall(request);
    } else {
        return copyApkForMoveInstall(request);
    }
}

private int copyApkForFileInstall(InstallRequest request) {
    ret = PackageManagerServiceUtils.copyPackage(
        mOriginInfo.mFile.getAbsolutePath(), request.getCodeFile());
    ...
    final boolean isIncremental = isIncrementalPath(
        request.getCodeFile().getAbsolutePath());
    final File libraryRoot = new File(request.getCodeFile(), LIB_DIR_NAME);
    NativeLibraryHelper.Handle handle = null;
    try {
        handle = NativeLibraryHelper.Handle.create(request.getCodeFile());
        ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                                                                 request.getAbiOverride(), isIncremental);
        if (ret != PackageManager.INSTALL_SUCCEEDED) {
            final String errorMessage = "Failed to copy native libraries";
            request.setError(ret, errorMessage);
        }
    }
}

copyApkForFileInstall主要做了两件事:拷贝apk、常见nativelib目录。

先来看拷贝apk

java 复制代码
/**
 * Copy package to the target location.
 *
 * @param packagePath absolute path to the package to be copied. Can be
 *                    a single monolithic APK file or a cluster directory
 *                    containing one or more APKs.
 * @return returns status code according to those in
 *         {@link PackageManager}
 */
public static int copyPackage(String packagePath, File targetDir) {
	copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
}

private static void copyFile(String sourcePath, File targetDir, String targetName)
    throws ErrnoException, IOException {
    if (!FileUtils.isValidExtFilename(targetName)) {
        throw new IllegalArgumentException("Invalid filename: " + targetName);
    }
    Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);

    final File targetFile = new File(targetDir, targetName);
    final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
                                            O_RDWR | O_CREAT, 0644);
    Os.chmod(targetFile.getAbsolutePath(), 0644);
    FileInputStream source = null;
    try {
        source = new FileInputStream(sourcePath);
        FileUtils.copy(source.getFD(), targetFd);
    } finally {
        IoUtils.closeQuietly(source);
    }
}

copyPackage把apk拷贝到apk的安装目录,可以看到就是我们看到的/data/app下面的那个base.apk。

然后handle = NativeLibraryHelper.Handle.create(request.getCodeFile());创建目录,然后拷贝NativeBinaries,也就是/data/app下面的lib64。

4.2 installPackages流程

继续看processPendingInstall,之后会走到processInstallRequests。

java 复制代码
private void processInstallRequests(boolean success, List<InstallRequest> installRequests) {
    processApkInstallRequests(success, installRequests);
}

private void processApkInstallRequests(boolean success, List<InstallRequest> installRequests) {
    mInstallPackageHelper.installPackagesTraced(installRequests);

    for (InstallRequest request : installRequests) {
        request.onInstallCompleted();
        doPostInstall(request);
    }
    for (InstallRequest request : installRequests) {
        mInstallPackageHelper.restoreAndPostInstall(request);
    }
}

void installPackagesTraced(List<InstallRequest> requests) {
    synchronized (mPm.mInstallLock) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
            installPackagesLI(requests);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
}

processInstallRequests最后会开启installPackagesLI流程

5 installPackagesLI

先看perfetto中的流程。

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("mPm.mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {
	Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
    //1.preparePackageLI
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
    request.onPrepareStarted();
    preparePackageLI(request);
    //2、scanPackageTracedLI
    final ScanResult scanResult = scanPackageTracedLI(request.getParsedPackage(),
                            request.getParseFlags(), request.getScanFlags(),
                            System.currentTimeMillis(), request.getUser(),
                            request.getAbiOverride());
    //3、reconcilePackages
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                    reconciledPackages = ReconcilePackageUtils.reconcilePackages(
                            requests, Collections.unmodifiableMap(mPm.mPackages),
                            versionInfos, mSharedLibraries,
                        mPm.mSettings.getKeySetManagerService(),
                            mPm.mSettings);
    //4、commitPackagesLocked
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
    commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());
    
    //5、executePostCommitStepsLIF
    executePostCommitStepsLIF(reconciledPackages);
    
    //6、broadcastPackageVerified
    if (success) {
        VerificationUtils.broadcastPackageVerified(verificationId, originUri,
                            PackageManager.VERIFICATION_ALLOW, rootHashString,
                            request.getDataLoaderType(), request.getUser(),
                            mContext);
    }
}

从代码看,installPackagesLI主要进行了6步操作,下面一步步的看。

5.1 preparePackageLI-准备阶段

java 复制代码
@GuardedBy("mPm.mInstallLock")
private void preparePackageLI(InstallRequest request) throws PrepareFailure {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final ParsedPackage parsedPackage;
    try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
        parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
        AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
    } catch (PackageManagerException e) {
        throw new PrepareFailure("Failed parse during installPackageLI", e);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

@GuardedBy("mPm.mInstallLock")
private void preparePackageLI(InstallRequest request) throws PrepareFailure {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final ParsedPackage parsedPackage;
    try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
        parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
        AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
    }
    ...
    final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
    final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
        input, parsedPackage, false /*skipVerify*/);
    parsedPackage.setSigningDetails(result.getResult());
    ...
    final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
    derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage, systemApp,
                        isUpdatedSystemAppFromExistingSetting ||
                        isUpdatedSystemAppInferred,
                        abiOverride, ScanPackageUtils.getAppLib32InstallDir());
    ...
    doRenameLI(request, parsedPackage);
}

Prepare准备:分析任何当前安装状态,分析包并对其进行初始验证。

在这一阶段首先是将apk文件解析出来,解析它的AndroidManifest.xml文件,将结果记录起来。我们平时在清单文件中声明的Activity等组件就是在这一步被记录到Framework中的,后续才能通过startActivity等方式启动来,然后是对签名信息进行验证。

总结:分析当前安装包的状态,解析安装包并对其做初始化验证

5.2 scanPackageTracedLI-扫描阶段

java 复制代码
@GuardedBy("mPm.mInstallLock")
private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
                   final @ParsingPackageUtils.ParseFlags int parseFlags,
                   @PackageManagerService.ScanFlags int scanFlags, long currentTime,
                   @Nullable UserHandle user, String cpuAbiOverride) 
    throws PackageManagerException {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
    try {
        return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime,
                                user, cpuAbiOverride);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

@GuardedBy("mPm.mInstallLock")
private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
                     final @ParsingPackageUtils.ParseFlags int parseFlags,
                     @PackageManagerService.ScanFlags int scanFlags, 
                     long currentTime, @Nullable UserHandle user, 
                     String cpuAbiOverride) throws PackageManagerException {
    synchronized (mPm.mLock) {
        assertPackageIsValid(parsedPackage, parseFlags, newScanFlags);
        final ScanRequest request = new ScanRequest(parsedPackage,
                 initialScanRequest.mOldSharedUserSetting,
                 initialScanRequest.mOldPkg, installedPkgSetting,
                 initialScanRequest.mSharedUserSetting, disabledPkgSetting,
                 initialScanRequest.mOriginalPkgSetting,
                 initialScanRequest.mRealPkgName,parseFlags, scanFlags, 
                 initialScanRequest.mIsPlatformPackage, user, cpuAbiOverride);
        return ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector,
                                  mPm.mFactoryTest, currentTime);
    }
}

/**
 * Just scans the package without any side effects.
 *
 * @param injector injector for acquiring dependencies
 * @param request Information about the package to be scanned
 * @param isUnderFactoryTest Whether or not the device is under factory test
 * @param currentTime The current time, in millis
 * @return The results of the scan
 */
@GuardedBy("mPm.mInstallLock")
@VisibleForTesting
@NonNull
public static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
                PackageManagerServiceInjector injector, boolean isUnderFactoryTest,
                long currentTime) throws PackageManagerException {
    ...
    final boolean createNewPackage = (pkgSetting == null);
    if (createNewPackage) {
        pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
                       originalPkgSetting, disabledPkgSetting, realPkgName,
                       sharedUserSetting, destCodeFile,
                       parsedPackage.getNativeLibraryRootDir(),
                       AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),                              AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
                       parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags,
                       user,true /*allowInstall*/, instantApp, virtualPreload, 
                       sStoppedSystemApp, UserManagerService.getInstance(),
                       usesSdkLibraries,
                       parsedPackage.getUsesSdkLibrariesVersionsMajor(),
                       usesStaticLibraries,
                       parsedPackage.getUsesStaticLibrariesVersions(),
                       parsedPackage.getMimeGroups(), newDomainSetId);
    }
  
    pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils
        .getRawPrimaryCpuAbi(parsedPackage))
        .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
        .setCpuAbiOverride(cpuAbiOverride);
	return new ScanResult(request, pkgSetting, changedAbiCodePath,
                !createNewPackage /* existingSettingCopied */,
                Process.INVALID_UID /* previousAppId */ , sdkLibraryInfo,
                staticSharedLibraryInfo, dynamicSharedLibraryInfos);
}

scanPackageTracedLI主要工作是把第一步preparePackageLI中解析的apk信息保存到一个PackageSetting对象中

总结:根据prepare阶段中收集的安装包状态信息去扫描解析出来的包

5.3 reconcilePackages-协调阶段

java 复制代码
public static List<ReconciledPackage> reconcilePackages(
            List<InstallRequest> installRequests,
            Map<String, AndroidPackage> allPackages,
            Map<String, Settings.VersionInfo> versionInfos,
            SharedLibrariesImpl sharedLibraries,
            KeySetManagerService ksms, Settings settings)
            throws ReconcileFailure {
    final List<ReconciledPackage> result = new ArrayList<>(installRequests.size());

    // make a copy of the existing set of packages so we can combine them with incoming packages
    final ArrayMap<String, AndroidPackage> combinedPackages =
        new ArrayMap<>(allPackages.size() + installRequests.size());

    combinedPackages.putAll(allPackages);

    final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
        new ArrayMap<>();
}

这里combinedPackages是当前安装包大小加上要安装包的大小,接着它把当前的安装包对象都放到combinedPackages中。allPackages.allPackages中是当前的安装包对象。combinedPackages是当前安装包大小加上要安装包的大小,接着它把当前的安装包对象都放到combinedPackages中。

incomingSharedLibraries对象是安装包对象中声明的即将安装的库。

先把每个安装包的解析包对象放入combinedPackages中。因为它是ArrayMap类型,所以它也可能覆盖之前的解析包对象。下面是处理共享库信息。

java 复制代码
final Settings.VersionInfo versionInfo = versionInfos.get(installPackageName);
final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
final boolean isRollback = installRequest.isRollback();
final boolean compatMatch =
    PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
                                                sharedUserSetting, disabledPkgSetting,
                                                signingDetails, compareCompat,
                                                compareRecover, isRollback);

该段代码主要是用来验证安装包的签名。

java 复制代码
final ReconciledPackage reconciledPackage = new ReconciledPackage(installRequests, 
                               allPackages, installRequest,deletePackageAction,
                               allowedSharedLibInfos, signingDetails,         
                               sharedUserSignaturesChanged, removeAppKeySetData);

如果之前的都没什么问题,会将相关信息封装到ReconciledPackage对象中,然后添加到结果result中。封装到ReconciledPackage对象的包括ReconcileRequest对象request、安装参数installArgs、新生成的PackageSettingscanResult.pkgSetting、安装信息res、准备结果对象、浏览结果对象、待删除包行动兑对象deletePackageAction、允许添加的包中的共享库信息、解析包签名信息对象、共享用户签名是否改变、是否删除签名信息removeAppKeySetData。

这段代码是得到每个安装包的共享库信息,在系统启动或者是系统解析包的情况下,是不进行该项操作的。

java 复制代码
reconciledPackage.mCollectedSharedLibraryInfos =
    sharedLibraries.collectSharedLibraryInfos(
    installRequest.getParsedPackage(), combinedPackages,
    incomingSharedLibraries);

主要是调用collectSharedLibraryInfos()方法来得到。collectSharedLibraryInfos主要是对解析包对象的getUsesLibraries()、getUsesStaticLibraries()、getUsesOptionalLibraries()、getUsesNativeLibraries()、getUsesOptionalNativeLibraries()进行处理,它们分别是从Manifest文件中配置的"uses-library"、"uses-static-library"、"uses-library"、"uses-native-library"、"uses-native-library"标签中解析出来的。其中getUsesLibraries()与getUsesOptionalLibraries()是根据标签中的属性"required"来区分。getUsesNativeLibraries()、getUsesOptionalNativeLibraries()也是根据标签中的属性"required"来区分。

它们又都调用了另一个同名collectSharedLibraryInfos()方法来实现。同名collectSharedLibraryInfos()方法的实现具体来说就是根据existingLibraries(存在的库), newLibraries(新加的库),如果新加的库不在它们两个中,根据参数required,如果它为false,可以跳过,如果它为true,则会抛出PackageManagerException异常。如果需要判断版本和摘要(根据参数),传递的参数摘要和对应的签名信息经过计算得出的相符,则通过。

这样最后,将结果result返回。

总结: 验证scan阶段扫描到的Package信息以及当前系统状态,确保apk的正确安装。

​ 对普通APP替换安装的生成一个删除包行为对象。

​ 对新生成的PackageSetting对象,验证签名。验证通过之后,将相关信息封装成ReconciledPackage对象,放到返回结果中。

​ 对每一个安装应用包,收集共享库信息。

5.4 commitPackagesLocked

首先来看下perfetto中的commitPackages

java 复制代码
 commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());
@GuardedBy("mPm.mLock")
private void commitPackagesLocked(List<ReconciledPackage> reconciledPackages,
                                  @NonNull int[] allUsers) {
	AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, allUsers);
    	updateSettingsLI(pkg, allUsers, installRequest);
}

@GuardedBy("mPm.mLock")
public AndroidPackage commitReconciledScanResultLocked(
    @NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
	mPm.mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
    commitPackageSettings(pkg, pkgSetting, oldPkgSetting, reconciledPkg);
}

这里面也是安装应用和扫描应用同时都会调用的方法,先只看扫描的情况

  1. 处理PackageSetting,这部分逻辑是和安装应用是混在一起的,主要包括

    1. SharedUserSetting, 这个一般只有覆盖安装的时候如果sharedUserId变了,要重新赋值
    2. 重命名包名的逻辑,安装重命名包名的时候会更新packages.xml文件
  2. 将PackageSetting写入package-restrictions.xml,里面主要存储的是disabled和enabled四大组件

  3. 最后调用commitPackageSettings()进行最后一步处理.

java 复制代码
/**
 * Adds a scanned package to the system. When this method is finished, the package will
 * be available for query, resolution, etc...
 */
private void commitPackageSettings(@NonNull AndroidPackage pkg,
                                   @NonNull PackageSetting pkgSetting, 
                                   @Nullable PackageSetting oldPkgSetting,
                                   ReconciledPackage reconciledPkg) {
    // writer
    ArrayList<AndroidPackage> clientLibPkgs =
        mSharedLibraries.commitSharedLibraryChanges(pkg, pkgSetting,
        reconciledPkg.mAllowedSharedLibraryInfos,
        reconciledPkg.getCombinedAvailablePackages(), scanFlags);
    // writer
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
    synchronized (mPm.mLock) {
        // We don't expect installation to fail beyond this point
        // Add the new setting to mSettings
        mPm.mSettings.insertPackageSettingLPw(pkgSetting, pkg);
        // Add the new setting to mPackages
        mPm.mPackages.put(pkg.getPackageName(), pkg);
        if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
            mApexManager.registerApkInApex(pkg);
        }
        final Computer snapshot = mPm.snapshotComputer();
        mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage, snapshot);
        mPm.mAppsFilter.addPackage(snapshot, pkgSetting, isReplace,
                                   (scanFlags & SCAN_DONT_KILL_APP) != 0 /* retainImplicitGrantOnReplace */);
        mPm.addAllPackageProperties(pkg);
        mPm.mPermissionManager.onPackageAdded(pkgSetting,
                    (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
    }
}
  1. 调用mSettings.insertPackageSettingLPw(),这里并非是更新packages.xml的地方,只是将pkgSetting,放到Settings.mPackages中
  2. 将AndroidPackage放到mPackages中
  3. 更新KeySetManagerService的应用信息,这是一个管理签名的服务
  4. 添加自定义的Permission和PermissionGroup到PermissionManagerService中
java 复制代码
private void updateSettingsLI(AndroidPackage newPackage,
            int[] allUsers, InstallRequest installRequest) {
    updateSettingsInternalLI(newPackage, allUsers, installRequest);
}

private void updateSettingsInternalLI(AndroidPackage pkg,
            int[] allUsers, InstallRequest installRequest) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
    mPm.mSettings.addInstallerPackageNames(ps.getInstallSource());
    ...
    mPm.mSettings.writeKernelMappingLPr(ps);
    ...
    installRequest.setName(pkgName);
            installRequest.setAppId(pkg.getUid());
            installRequest.setPkg(pkg);
            installRequest.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
            //to update install status
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
            mPm.writeSettingsLPrTEMP();
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

1、addInstallerPackageNames向mInstallerPackages中加入ps.getInstallSource()

2、writeKernelMappingLPr来写/config/sdcardfs/,如向该目录下appid写入appid file

3、将settings写入packages.xml

java 复制代码
@SuppressWarnings("GuardedBy")
void writeSettingsLPrTEMP(boolean sync) {
snapshotComputer(false);
mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
mSettings.writeLPr(mLiveComputer, sync);
}

总结:提交所有扫描的包并更新系统状态。这是唯一可以在安装流程和所有可预测错误中修改系统状态的地方.

5.5 executePostCommitStepsLIF

java 复制代码
/**
 * On successful install, executes remaining steps after commit completes and the package lock
 * is released. These are typically more expensive or require calls to installd, which often
 * locks on {@link com.android.server.pm.PackageManagerService.mLock}.
 */
@GuardedBy("mPm.mInstallLock")
private void executePostCommitStepsLIF(List<ReconciledPackage> reconciledPackages) {
    final boolean performDexopt =
        (!instantApp || android.provider.Settings.Global.getInt(
            mContext.getContentResolver(),
            android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
        && !pkg.isDebuggable()
        && (!onIncremental)
        && dexoptOptions.isCompilationEnabled()
        && !isApex;
    if (performDexopt) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
        if (useArtService()) {
            PackageManagerLocal packageManagerLocal =
                LocalManagerRegistry.getManager(PackageManagerLocal.class);
            try (PackageManagerLocal.FilteredSnapshot snapshot =
                 packageManagerLocal.withFilteredSnapshot()) {
                DexoptParams params =
                    dexoptOptions.convertToDexoptParams(0 /* extraFlags */);
                DexoptResult dexOptResult = DexOptHelper.getArtManagerLocal().dexoptPackage(
                    snapshot, packageName, params);
                installRequest.onDexoptFinished(dexOptResult);
            }
        }
    }
    PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
        incrementalStorages);
}

调用了executePostCommitStepsLIF完成apk的安装,执行dex优化等操作。

检测是否需要进行dex优化:同时满足下面几种情况:

​ 1.不是一个即时应用app或者instant_app_dexopt_enabled属性不是0

​ 2.debuggable为false

​ 3.不在增量文件系统上

4. 不是Apex
5. 编译选项没有skip

最后放出perfetto

5.6 broadcastPackageVerified

java 复制代码
public static void broadcastPackageVerified(int verificationId, Uri packageUri,
            int verificationCode, @Nullable String rootHashString, 
            int dataLoaderType, UserHandle user, Context context) {
    final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
    intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
    intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
    if (rootHashString != null) {
        intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
    }
    intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);

    context.sendBroadcastAsUser(intent, user,
                                android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
}

发送PACKAGE_VERIFIED广播。

6 返回结果

最后通知VerifyingSession#handleVerificationFinished

java 复制代码
if (verifyingSession != null) {
    verifyingSession.handleVerificationFinished();
}

void handleVerificationFinished() {
    mWaitForVerificationToComplete = false;
    handleReturnCode();
}

private void sendVerificationCompleteNotification() {
    if (mParentVerifyingSession != null) {
        mParentVerifyingSession.trySendVerificationCompleteNotification(this);
    } else {
        try {
            mObserver.onPackageInstalled(null, mRet, mErrorMessage,
                                         new Bundle());
        } catch (RemoteException e) {
            Slog.i(TAG, "Observer no longer exists.");
        }
    }
}

这里是mObserver.onPackageInstalled

java 复制代码
//framework/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
    @Override
    public void onPackageInstalled(String basePackageName, int returnCode, 
                           String msg, Bundle extras) {
        if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
            // Continue verification for staged sessions
            verifyStaged(session.mStagedSession, callback);
            return;
        }
        if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
            String errorMessage = PackageManager.installStatusToString(
                returnCode, msg);
            session.setSessionFailed(returnCode, errorMessage);
            callback.onResult(returnCode, msg);
        } else {
            session.setSessionReady();
            callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
        }
    }
}

//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private void verifyNonStaged() throws PackageManagerException {
    mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
        mHandler.post(() -> {
            if (dispatchPendingAbandonCallback()) {
                // No need to continue if abandoned
                return;
            }
            if (error == INSTALL_SUCCEEDED) {
                onVerificationComplete();
            } else {
                onSessionVerificationFailure(error, msg);
            }
        });
    });
}

@WorkerThread
private void onVerificationComplete() {
    if (isStaged()) {
        mStagingManager.commitSession(mStagedSession);
        sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
        return;
    }
    install();
}

这里走isStaged

java 复制代码
private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
    final IntentSender statusReceiver = getRemoteStatusReceiver();
    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 = getPackageName();
        args.arg2 = msg;
        args.arg3 = extras;
        args.arg4 = statusReceiver;
        args.argi1 = returnCode;
        args.argi2 = isPreapprovalRequested() && !isCommitted() ? 1 : 0;
        mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
    }
}

case MSG_ON_PACKAGE_INSTALLED:
final SomeArgs args = (SomeArgs) msg.obj;
final String packageName = (String) args.arg1;
final String message = (String) args.arg2;
final Bundle extras = (Bundle) args.arg3;
final IntentSender statusReceiver = (IntentSender) args.arg4;
final int returnCode = args.argi1;
final boolean isPreapproval = args.argi2 == 1;
args.recycle();

sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                       isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
                       packageName, returnCode, isPreapproval, message, extras);

break;

sendOnPackageInstalled中先发送安装成功的通知,然后向IntentSender发送sendIntent。

从第2节中可知道IntentSender是在PackageInstaller的apk传入的。

java 复制代码
PendingIntent pendingIntent = PendingIntent.getBroadcast(
                InstallInstalling.this,
                mInstallId,
                broadcastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);

全文完。

相关推荐
我又来搬代码了18 分钟前
【Android】Android Studio 底部快捷按钮没了去哪找
android·ide·android studio
码点1 小时前
Android 提高第一次开机速度(取消系统默认手机加密)
android
YueTann2 小时前
大模型入门3:理解LLAMA
android·llama
&岁月不待人&2 小时前
Android 通过相机和系统相册获取图片,压缩,结果回调
android·数码相机
前端小菜鸟也有人起3 小时前
ElementUI大坑Notification修改样式
android·前端·elementui
嵌入式成长家6 小时前
[rk3399 android11]收到广播执行相应的adb命令
android·adb·广播
电手6 小时前
Chrome远程桌面安卓版怎么使用?
android·远程连接·远程控制
我命由我123456 小时前
Kotlin 极简小抄 P2(插值表达式、运算符、选择结构赋值)
android·java·开发语言·后端·kotlin·安卓
宝杰X76 小时前
Compose Multiplatform+kotlin Multiplatfrom第三弹
android·开发语言·kotlin
大猫熊猫16 小时前
Android Studio 加载多个FLutter项目
android·ide·android studio