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

全文完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/410291.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git提交本地项目到远程仓库

1、查看项目目录&#xff0c;是否存在.git文件夹&#xff08;若存在则删除&#xff09; 2、登录git并新建一个空白项目 3、idea创建本地git仓库&#xff08;选择本地项目&#xff09; 4、添加要提交的项目&#xff08;项目右键&#xff09; 5、提交代码到本地仓库 6、配置远程…

SQLserver中的游标的分类和游标的生命周期

SQLserver中的游标的分类 在 SQL Server 中&#xff0c;游标&#xff08;Cursor&#xff09;是一种数据库对象&#xff0c;用于逐行处理结果集中的数据。游标可以用于复杂的数据处理任务&#xff0c;尤其是那些不能通过简单的 SELECT 语句和 JOIN 操作完成的任务。SQL Server …

48.x86游戏实战-封包抓取进图call

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

OpenAI API: How to count tokens before API request

题意&#xff1a;“OpenAI API&#xff1a;如何在 API 请求之前计算令牌数量” 问题背景&#xff1a; I would like to count the tokens of my OpenAI API request in R before sending it (version gpt-3.5-turbo). Since the OpenAI API has rate limits, this seems impor…

OpenLayers3,地图探查功能实现

文章目录 一、前言二、代码实现三、总结 一、前言 图层探查&#xff0c;即对置于地图下方的图层进行一定范围的探查&#xff0c;以便用户查看到不易察觉的地理地况。本文基于OpenLayers3&#xff0c;实现地图探查的功能。 二、代码实现 <!DOCTYPE HTML PUBLIC "-//W…

基于Transformer架构的大模型推理硬件加速器设计

概述 当前大模型的基础架构正在向 Transformer 结构收敛1&#xff0c;Transformer架构自谷歌2017年提出后比较稳定&#xff0c;因此针对Transformer的计算设计专用的ASIC加速器很有必要。 尤其是“Attention is All you Need”》“Money is All you Need”&#xff0c;哈哈哈…

MySQL的源码安装及基本部署(基于RHEL7.9)

这里源码安装mysql的5.7.44版本 一、源码安装 1.下载并解压mysql , 进入目录: wget https://downloads.mysql.com/archives/get/p/23/file/mysql-boost-5.7.44.tar.gz tar xf mysql-boost-5.7.44.tar.gz cd mysql-5.7.44/ 2.准备好mysql编译安装依赖: yum install cmake g…

使用vueuse在组件内复用模板

1. 安装vueusae pnpm i vueuse/core2. 组件内复用模板 createReusableTemplate 是vueuse中的一个实用工具&#xff0c;用于在 Vue 3 中创建可重复使用的模板片段&#xff0c;同时保持状态的独立性。这对于需要在多个组件中重复使用相同的结构和逻辑时非常有用。 因为这些可复…

链表OJ题——使用栈实现单链表的逆序打印

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 题目描述&#xff1a;使用栈&#xff0c;实现单链表的逆序打印 二、解题思路 三、解题代码 /*** 非递归实现单链表的顶逆序打印——>通过栈来实现* param*/public void printReverseListFromStack(){Stack<…

短视频SDK解决方案,原开发团队,一对一技术支持

美摄科技&#xff0c;作为行业领先的视频技术提供商&#xff0c;凭借深厚的技术积累和敏锐的市场洞察&#xff0c;隆重推出其短视频SDK解决方案&#xff0c;旨在为全球开发者及内容创作者搭建一座通往无限创意与高效生产的桥梁。 【一站式解决方案&#xff0c;赋能创意无界】 …

【js原型和原型链】

js原型和原型链 一、构造函数和原型对象中的this二、原型对象的constructor属性三、原型链四、关系图五、普通函数和函数对象 参考文章链接: link 一、构造函数和原型对象中的this 指向实例对象 // 定义构造函数function Star(name,age){this.name name;this.age age;conso…

前端面试题 webpack的工作流程

一、流程图 二、重要概念 1.entry入口&#xff1a; Webpack 从配置的入口点开始&#xff0c;分析应用程序的依赖关系 2.output出口&#xff1a; 定义了打包后的文件如何输出&#xff0c;包括文件名和输出路径。 3.loader加载器&#xff1a; Webpack 本身只能处理 JavaScr…

Bytebase 2.22.2 - 允许在工作空间为群组分配角色

&#x1f680; 新功能 允许在工作空间给群组分配角色。 支持禁用邮箱密码登录&#xff0c;仅允许 SSO 登录的设置项。 新增 Postgres SQL 审核规则&#xff1a;禁止在列上设置会变化的默认值。 &#x1f514; 重大变更 下线项目内的变更历史页面&#xff1b;所有变更历史仍可…

uboot环境变量擦除之烧录工具擦除flash mtd0分区

有时会uboot环境变量修改了没有生效,需要擦除整个mtd分区 Erasing at 0x100000 – 100% complete. &#xff08;1M&#xff09; uboot给flash的中分区

实体书商城小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;小说分类管理&#xff0c;小说信息管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;小说信息&#xff0c;小说资讯&#xff0…

IGE-LIO:充分利用强度信息克服激光退化场景下的定位精度

更多优质内容&#xff0c;请关注公众号&#xff1a;智驾机器人技术前线 1.论文信息 论文标题&#xff1a;IGE-LIO: Intensity Gradient Enhanced Tightly-Coupled LiDAR-Inertial Odometry 作者&#xff1a;Ziyu Chen, Hui Zhu, Biao Yu, Chunmao Jiang, Chen Hua, Xuhui Fu a…

图新说-调整标绘线面的压盖顺序的两种方法

0.序 图新说作为一个三维可视化汇报工具&#xff0c;在公安消防领域常用于做态势标绘&#xff0c;应急救援方案&#xff0c;安保预案等。 如果撤离路线&#xff0c;或者行进路线【线对象】经过了水源地、危险区等【面对象】。如何确保线对象显示在面对象的上面&#xff0c;不被…

Nginx的核心!!! 负载均衡、反向代理

目录 负载均衡 1.轮询 2.最少连接数 3.IP哈希 4.加权轮询 5.最少时间 6.一致性哈希 反向代理 测试 之前讲过Nginx 的简介和正则表达式&#xff0c;那些都是Nginx较为基础的操作&#xff0c;Nginx 最重要的最核心的功能&#xff0c;当属反向代理和负载均衡了。 负载均…

统一 transformer 与 diffusion !Meta 融合新方法剑指下一代多模态王者

本文引入了 Transfusion&#xff0c;这是一种可以在离散和连续数据上训练多模态模型的方法。 来源丨机器之心 一般来说&#xff0c;多模态生成模型需要能够感知、处理和生成离散元素&#xff08;如文本或代码&#xff09;和连续元素&#xff08;如图像、音频和视频数据&#xf…

【操作系统】实验:文件系统

目录 一、实验目的 二、实验要求 三、实验步骤 四、核心代码 五、记录与处理 六、思考 七、完整报告和成果文件提取链接 一、实验目的 1、掌握文件系统的基本结构和文件系统的管理方法 2、加深对两级文件目录认识和理解 3、对文件操作的系统命令实质内容和执行过程深入…