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

全文完。

相关推荐
coder_pig3 小时前
🤡 公司Android老项目升级踩坑小记
android·flutter·gradle
死就死在补习班5 小时前
Android系统源码分析Input - InputReader读取事件
android
死就死在补习班5 小时前
Android系统源码分析Input - InputChannel通信
android
死就死在补习班5 小时前
Android系统源码分析Input - 设备添加流程
android
死就死在补习班5 小时前
Android系统源码分析Input - 启动流程
android
tom4i5 小时前
Launcher3 to Launchpad 01 布局修改
android
雨白5 小时前
OkHttpClient 核心配置详解
android·okhttp
淡淡的香烟5 小时前
Android auncher3实现简单的负一屏功能
android
RabbitYao6 小时前
Android 项目 通过 AndroidStringsTool 更新多语言词条
android·python
RabbitYao6 小时前
使用 Gemini 及 Python 更新 Android 多语言 Excel 文件
android·python