当前位置: 首页 > news >正文

Android中apk安装过程源码解析

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

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

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中声明权限

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> 

2 PackageInstaller流程

2.1弹出安装对话框

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

//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// startInstallConfirmbindUi();checkIfAllowedAndInitiateInstall();}if (mOk != null) {mOk.setEnabled(mEnableOk);}
}

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

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 finishsetActivityResult(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里面,当点击确认时,开始安装流程。

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 applicationIntent 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中的流程。
在这里插入图片描述

对应代码为:

//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 activityif (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 finishedmCancelButton.setEnabled(false);setFinishOnTouchOutside(false);}}
}

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

/*** 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;@Overrideprotected PackageInstaller.Session doInBackground(Void... params) {try {return getPackageManager().getPackageInstaller().openSession(mSessionId);} catch (IOException e) {return null;} finally {synchronized (this) {isDone = true;notifyAll();}}}@Overrideprotected 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。

//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。

public static class Session implements Closeable {}

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

//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()。

//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是什么?

/** Staging location where client data is written. */
final File stageDir;

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

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";
//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)。

//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。

//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阶段。
在这里插入图片描述

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

//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 processedif (!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 installboolean 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。

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

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

//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() {@Overridepublic void onUserActionRequired(Intent intent) {throw new IllegalStateException();}@Overridepublic void onPackageInstalled(String basePackageName, int returnCode, String msg,Bundle extras) {if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {// Continue verification for staged sessionsverifyStaged(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()。

//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中如下部分:

在这里插入图片描述

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 concernedsendApkVerificationRequest(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后,做了下面两件事。

session.setSessionReady();
callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);

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

void setSessionReady() {synchronized (mLock) {// Do not allow destroyed/failed session to change stateif (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流程

@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如下:

在这里插入图片描述

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

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。

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

/*** 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。

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中的流程。

在这里插入图片描述

 /*** 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.preparePackageLITrace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");request.onPrepareStarted();preparePackageLI(request);//2、scanPackageTracedLIfinal ScanResult scanResult = scanPackageTracedLI(request.getParsedPackage(),request.getParseFlags(), request.getScanFlags(),System.currentTimeMillis(), request.getUser(),request.getAbiOverride());//3、reconcilePackagesTrace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");reconciledPackages = ReconcilePackageUtils.reconcilePackages(requests, Collections.unmodifiableMap(mPm.mPackages),versionInfos, mSharedLibraries,mPm.mSettings.getKeySetManagerService(),mPm.mSettings);//4、commitPackagesLockedTrace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());//5、executePostCommitStepsLIFexecutePostCommitStepsLIF(reconciledPackages);//6、broadcastPackageVerifiedif (success) {VerificationUtils.broadcastPackageVerified(verificationId, originUri,PackageManager.VERIFICATION_ALLOW, rootHashString,request.getDataLoaderType(), request.getUser(),mContext);}
}

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

5.1 preparePackageLI-准备阶段

@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-扫描阶段

@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-协调阶段

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 packagesfinal 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类型,所以它也可能覆盖之前的解析包对象。下面是处理共享库信息。

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

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

final ReconciledPackage reconciledPackage = new ReconciledPackage(installRequests, allPackages, installRequest,deletePackageAction,allowedSharedLibInfos, signingDetails,         sharedUserSignaturesChanged, removeAppKeySetData);

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

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

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

在这里插入图片描述

 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()进行最后一步处理.

/*** 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) {// writerArrayList<AndroidPackage> clientLibPkgs =mSharedLibraries.commitSharedLibraryChanges(pkg, pkgSetting,reconciledPkg.mAllowedSharedLibraryInfos,reconciledPkg.getCombinedAvailablePackages(), scanFlags);// writerTrace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");synchronized (mPm.mLock) {// We don't expect installation to fail beyond this point// Add the new setting to mSettingsmPm.mSettings.insertPackageSettingLPw(pkgSetting, pkg);// Add the new setting to mPackagesmPm.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中
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 statusTrace.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

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

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

5.5 executePostCommitStepsLIF

/*** 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

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

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

//framework/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {@Overridepublic void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) {if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {// Continue verification for staged sessionsverifyStaged(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 abandonedreturn;}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

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 sessionfinal 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传入的。

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

全文完。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 以小搏大:Salesforce 十亿参数模型表现超过ChatGPT
  • 计算机的内存不足
  • 北峰370MHz应急窄带无线通信解决方案
  • (一)、软硬件全开源智能手表,与手机互联,标配多表盘,功能丰富(ZSWatch-Zephyr)
  • 酒店智能触摸开关的原理与应用
  • Linux 系统调优 2
  • 【PyTorch][chapter 27][李宏毅深度学习][transformer-1]
  • Qt与Python
  • 反爬虫策略收录集
  • android交叉编译报错no input files的解决方法
  • 视觉检索(以图搜图)技术分享
  • C#从入门到精通(20)—C#目录类Directory用法总结
  • 苹果秋季发布会前瞻:iPhone 16领衔新品盛宴
  • Redis | 非关系型数据库Redis的初步认识
  • HTML粒子爱心
  • angular组件开发
  • CentOS6 编译安装 redis-3.2.3
  • JAVA并发编程--1.基础概念
  • leetcode-27. Remove Element
  • linux安装openssl、swoole等扩展的具体步骤
  • Making An Indicator With Pure CSS
  • markdown编辑器简评
  • Node 版本管理
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • Python学习笔记 字符串拼接
  • 关于springcloud Gateway中的限流
  • 基于 Babel 的 npm 包最小化设置
  • 开发基于以太坊智能合约的DApp
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 用Canvas画一棵二叉树
  • 云大使推广中的常见热门问题
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • #etcd#安装时出错
  • #单片机(TB6600驱动42步进电机)
  • #每日一题合集#牛客JZ23-JZ33
  • (7) cmake 编译C++程序(二)
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (初研) Sentence-embedding fine-tune notebook
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (贪心) LeetCode 45. 跳跃游戏 II
  • (一)SvelteKit教程:hello world
  • (转)编辑寄语:因为爱心,所以美丽
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • *1 计算机基础和操作系统基础及几大协议
  • *算法训练(leetcode)第四十天 | 647. 回文子串、516. 最长回文子序列
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .FileZilla的使用和主动模式被动模式介绍
  • .net core 管理用户机密
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .Net FrameWork总结
  • .Net 知识杂记
  • .net6+aspose.words导出word并转pdf