本文中使用的Android源码基于Android 14
1 三方应用安装apk调用方法
java
public void installApk() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/*
* 自Android N开始,是通过FileProvider共享相关文件,但是Android Q对公
* 有目录 File API进行了限制,只能通过Uri来操作
*/
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
// filePath是通过ContentResolver得到的
intent.setDataAndType(Uri.parse(filePath),
"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(mContext,
"com.test.file.fileProvider", file);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
}
startActivity(intent);
}
然后再AndroidManifest.xml中声明权限
xml
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
2 PackageInstaller流程
2.1弹出安装对话框
进入PackageInstaller后,首先会拉起PackageInstallerActivity。
java
//framework/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@Override
protected void onResume() {
super.onResume();
if (mLocalLOGV) Log.i(TAG, "onResume(): mAppSnippet=" + mAppSnippet);
if (mAppSnippet != null) {
// load dummy layout with OK button disabled until we override this layout in
// startInstallConfirm
bindUi();
checkIfAllowedAndInitiateInstall();
}
if (mOk != null) {
mOk.setEnabled(mEnableOk);
}
}
PackageInstallerActivity首先创建Ui,然后检查权限。
java
private void bindUi() {
mAlert.setIcon(mAppSnippet.icon);
mAlert.setTitle(mAppSnippet.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
setActivityResult(RESULT_OK);
finish();
} else {
startInstall();
}
}
}, null);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setActivityResult(RESULT_CANCELED);
finish();
}, null);
setupAlert();
mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
if (!mOk.isInTouchMode()) {
mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
}
}
/**
* Check if it is allowed to install the package and initiate install if allowed.
*/
private void checkIfAllowedAndInitiateInstall() {
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
if (mLocalLOGV) Log.i(TAG, "install allowed");
initiateInstall();
} else {
handleUnknownSources();
}
}
在checkIfAllowedAndInitiateInstall里面,狐妖是检查是否有安装来自未知来源的权限,如果有权限,那么在initiateInstall中就设置确定按钮为enable和visible。
在bindUi里面,当点击确认时,开始安装流程。
java
private void startInstall() {
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
int stagedSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallInstalling.class);
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
if (mOriginatingUid != Process.INVALID_UID) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
installerPackageName);
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}
if (stagedSessionId > 0) {
newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
}
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);
finish();
}
这里startActivity启动的是InstallInstalling。
2.2 安装进度界面显示InstallInstalling
继续看InstallInstalling的启动。首先我们看下InstallInstalling在perfetto中的流程。
对应代码为:
java
//framework/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
...
} else {
try {
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
}
@Override
protected void onResume() {
super.onResume();
// This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo =
installer.getSessionInfo(mSessionId);
if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// we will receive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
首先在onCreate中会创建安装中的Ui界面,然后注册InstallEventReceiver来监听安装结果,并在launchFinishBasedOnResult中将接收到的安装结果进行处理。然后在onResume中开启安装session。
java
/**
* Send the package to the package installer and then register a event result observer that
* will call {@link #launchFinishBasedOnResult(int, int, String, int)}
*/
private final class InstallingAsyncTask extends AsyncTask<Void,
Void, PackageInstaller.Session> {
volatile boolean isDone;
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
try {
return getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}
3 建立安装的Session
3.1 建立Session
在doInBackground中进行PackageInstaller#openSession,然后在onPostExecute中提交会话结果,来看PackageInstaller#openSession。
java
//framework/base/core/java/android/content/pm/PackageInstaller.java
/**
* Open an existing session to actively perform work. To succeed, the caller
* must be the owner of the install session.
*
* @throws IOException if parameters were unsatisfiable, such as lack of
* disk space or unavailable media.
* @throws SecurityException when the caller does not own the session, or
* the session is invalid.
*/
public @NonNull Session openSession(int sessionId) throws IOException {
try {
try {
return new Session(mInstaller.openSession(sessionId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
}
}
这里调用了mInstaller.openSession(sessionId),并new了一个Session。
java
public static class Session implements Closeable {}
mInstaller是IPackageInstaller,也就是PackageInstaller的服务端PackageInstallerService。
java
//framework/base/services/core/java/com/android/server/pm/PackageInstallerService.java
@Override
public IPackageInstallerSession openSession(int sessionId) {
try {
return openSessionInternal(sessionId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
if (!checkOpenSessionAccess(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
session.open();
return session;
}
}
checkOpenSessionAccess进行简单校验之后,直接调用PackageInstallerSession的open()。
java
//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void open() throws IOException {
activate();
boolean wasPrepared;
synchronized (mLock) {
wasPrepared = mPrepared;
if (!mPrepared) {
if (stageDir != null) {
prepareStageDir(stageDir);
} else if (params.isMultiPackage) {
// it's all ok
} else {
throw new IllegalArgumentException("stageDir must be set");
}
mPrepared = true;
}
}
if (!wasPrepared) {
mCallback.onSessionPrepared(this);
}
}
这里也很简单,在prepareStageDir里面进行目录创建和selinux相关配置。
这里有一个疑问,stageDir是什么?
java
/** Staging location where client data is written. */
final File stageDir;
它是一个file,那它的路径是哪里呢?继续找
java
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
Context context, PackageManagerService pm,
PackageSessionProvider sessionProvider,
SilentUpdatePolicy silentUpdatePolicy, Looper looper, StagingManager stagingManager,
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
SessionParams params, long createdMillis, long committedMillis,
File stageDir, String stageCid, InstallationFile[] files,
ArrayMap<String, PerFileChecksum> checksums,
boolean prepared, boolean committed, boolean destroyed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int sessionErrorCode,
String sessionErrorMessage) {
}
public static PackageInstallerSession readFromXml(@NonNull TypedXmlPullParser in,
@NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
@NonNull PackageManagerService pm, Looper installerThread,
@NonNull StagingManager stagingManager, @NonNull File sessionsDir,
@NonNull PackageSessionProvider sessionProvider,
@NonNull SilentUpdatePolicy silentUpdatePolicy)
throws IOException, XmlPullParserException {
final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
InstallSource installSource = InstallSource.create(installInitiatingPackageName,
installOriginatingPackageName, installerPackageName, installPackageUid,
updateOwnerPackageName, installerAttributionTag, params.packageSource);
return new PackageInstallerSession(callback, context, pm, sessionProvider,
silentUpdatePolicy, installerThread, stagingManager, sessionId,
userId,installerUid, installSource, params, createdMillis,
committedMillis, stageDir,stageCid, fileArray, checksumsMap,
prepared, committed, destroyed, sealed,childSessionIdsArray,
parentSessionId, isReady, isFailed, isApplied,
sessionErrorCode, sessionErrorMessage);
}
private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
java
//framework/base/services/core/java/com/android/server/pm/PackageInstallerService.java
private void readSessionsLocked() {
while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) {
final String tag = in.getName();
if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
final PackageInstallerSession session;
try {
session = PackageInstallerSession.readFromXml(in,
mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), mStagingManager,
mSessionsDir, this, mSilentUpdatePolicy);
} catch (Exception e) {
Slog.e(TAG, "Could not read session", e);
continue;
}
mSessions.put(session.sessionId, session);
mAllocatedSessions.put(session.sessionId, true);
}
}
}
// After reboot housekeeping.
for (int i = 0; i < mSessions.size(); ++i) {
PackageInstallerSession session = mSessions.valueAt(i);
session.onAfterSessionRead(mSessions);
}
}
public PackageInstallerService(Context context, PackageManagerService pm,
Supplier<PackageParser2> apexParserSupplier) {
...
mSessionsFile = new AtomicFile(new File(Environment.getDataSystemDirectory(),
"install_sessions.xml"), "package-session");
...
}
public void systemReady() {
synchronized (mSessions) {
readSessionsLocked();
...
}
}
//framework/base/services/core/java/com/android/server/pm/PackageManagerService.java
public void systemReady() {
mInstallerService.systemReady();
}
终于理清楚了,首先在PKMS启动之后的systemReady中,通知PackageInstallerService的systemReady,然后systemReady中读取保存的Sessions,读取的地方是/data/system/install_sessions.xml中的package-session的tag。然后在该tag下读取sessionStageDir即是stageDir。
3.2 预检验apk
在readSessionsLocked最后,会走session.onAfterSessionRead(mSessions)。
java
//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@AnyThread
void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
synchronized (mLock) {
...
if (root != null && !root.isStagedAndInTerminalState()) {
if (isApexSession()) {
validateApexInstallLocked();
} else {
validateApkInstallLocked();
}
}
...
}
}
/**
* Validate install by confirming that all application packages are have
* consistent package name, version code, and signing certificates.
* <p>
* Clears and populates {@link #mResolvedBaseFile},
* {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
* <p>
* Renames package files in stage to match split names defined inside.
* <p>
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
* @return a {@link PackageLite} representation of the validated APK(s).
*/
@GuardedBy("mLock")
private PackageLite validateApkInstallLocked() throws PackageManagerException {
for (File addedFile : addedFiles) {
final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(
input.reset(), addedFile,
ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
}
}
parseApkLite中传入的flag是PARSE_COLLECT_CERTIFICATES。
java
//framework/base/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
/**
* Utility method that retrieves lightweight details about a single APK
* file, including package name, split name, and install location.
*
* @param apkFile path to a single APK
* @param flags optional parse flags, such as
* {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
*/
public static ParseResult<ApkLite> parseApkLite(ParseInput input, File apkFile,
int flags) {
return parseApkLiteInner(input, apkFile, null, null, flags);
}
private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
File apkFile, FileDescriptor fd, String debugPathName, int flags) {
final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
XmlResourceParser parser = null;
ApkAssets apkAssets = null;
try {
try {
apkAssets = fd != null
? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
: ApkAssets.loadFromPath(apkPath);
} catch (IOException e) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath, e);
}
parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
final SigningDetails signingDetails;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
final ParseResult<SigningDetails> result =
FrameworkParsingPackageUtils.getSigningDetails(input,
apkFile.getAbsolutePath(),
skipVerify, /* isStaticSharedLibrary */ false,
SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
if (result.isError()) {
return input.error(result);
}
signingDetails = result.getResult();
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} else {
signingDetails = SigningDetails.UNKNOWN;
}
return parseApkLite(input, apkPath, parser, signingDetails, flags);
} catch (XmlPullParserException | IOException | RuntimeException e) {
Slog.w(TAG, "Failed to parse " + apkPath, e);
return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
if (apkAssets != null) {
try {
apkAssets.close();
} catch (Throwable ignored) {
}
}
// TODO(b/72056911): Implement AutoCloseable on ApkAssets.
}
}
parseApkLiteInner中首先会加载ApkAssets,对应perfetto中加载ApkAssets的load阶段。
java
private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
File apkFile, FileDescriptor fd, String debugPathName, int flags) {
parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
final SigningDetails signingDetails;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
final ParseResult<SigningDetails> result =
FrameworkParsingPackageUtils.getSigningDetails(input, apkFile.getAbsolutePath(),skipVerify
, /*isStaticSharedLibrary */ false,
SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
if (result.isError()) {
return input.error(result);
}
signingDetails = result.getResult();
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} else {
signingDetails = SigningDetails.UNKNOWN;
}
return parseApkLite(input, apkPath, parser, signingDetails, flags);
}
FrameworkParsingPackageUtils.getSigningDetails中主要进行签名校验,在perfetto中的表现:
最后parseApkLite会返回在AndroidManifest.xml中解析的信息。
继续看前面InstallingAsyncTask在onPostExecute中session.commit(pendingIntent.getIntentSender())
java
//framework/base/core/java/android/content/pm/PackageInstaller.java
public void commit(@NonNull IntentSender statusReceiver) {
try {
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
assertNotChild("commit");
if (!markAsSealed(statusReceiver, forTransfer)) {
return;
}
if (isMultiPackage()) {
synchronized (mLock) {
boolean sealFailed = false;
for (int i = mChildSessions.size() - 1; i >= 0; --i) {
// seal all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mChildSessions.valueAt(i).markAsSealed(null, forTransfer)) {
sealFailed = true;
}
}
if (sealFailed) {
return;
}
}
}
File appMetadataFile = getStagedAppMetadataFile();
if (appMetadataFile != null) {
long sizeLimit = getAppMetadataSizeLimit();
if (appMetadataFile.length() > sizeLimit) {
appMetadataFile.delete();
throw new IllegalArgumentException(
"App metadata size exceeds the maximum allowed limit of " + sizeLimit);
}
if (isIncrementalInstallation()) {
// Incremental requires stageDir to be empty so move the app metadata file to a
// temporary location and move back after commit.
appMetadataFile.renameTo(getTmpAppMetadataFile());
}
}
dispatchSessionSealed();
}
private void dispatchSessionSealed() {
mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}
private void handleSessionSealed() {
assertSealed("dispatchSessionSealed");
// Persist the fact that we've sealed ourselves to prevent
// mutations of any hard links we create.
mCallback.onSessionSealedBlocking(this);
dispatchStreamValidateAndCommit();
}
private void dispatchStreamValidateAndCommit() {
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
@WorkerThread
private void handleStreamValidateAndCommit() {
try {
// This will track whether the session and any children were validated and are ready to
// progress to the next phase of install
boolean allSessionsReady = true;
for (PackageInstallerSession child : getChildSessions()) {
allSessionsReady &= child.streamValidateAndCommit();
}
if (allSessionsReady && streamValidateAndCommit()) {
mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
} catch (PackageManagerException e) {
destroy();
String msg = ExceptionUtils.getCompleteMessage(e);
dispatchSessionFinished(e.error, msg, null);
maybeFinishChildSessions(e.error, msg);
}
}
@WorkerThread
private void handleInstall() {
if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(getInstallSource().mInstallerPackageName)
.write();
}
/**
* Stops the installation of the whole session set if one session needs user action
* in its belong session set. When the user answers the yes,
* {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
* handled to come back here to check again.
*
* {@code mUserActionRequired} is used to track when user action is required for an
* install. Since control may come back here more than 1 time, we must ensure that it's
* value is not overwritten.
*/
boolean wasUserActionIntentSent = sendPendingUserActionIntentIfNeeded();
if (mUserActionRequired == null) {
mUserActionRequired = wasUserActionIntentSent;
}
if (wasUserActionIntentSent) {
// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
deactivate();
return;
} else if (mUserActionRequired) {
// If user action is required, control comes back here when the user allows
// the installation. At this point, the session is marked active once again,
// since installation is in progress.
activate();
}
if (mVerificationInProgress) {
Slog.w(TAG, "Verification is already in progress for session " + sessionId);
return;
}
mVerificationInProgress = true;
if (params.isStaged) {
mStagedSession.verifySession();
} else {
verify();
}
}
private void verify() {
try {
List<PackageInstallerSession> children = getChildSessions();
if (isMultiPackage()) {
for (PackageInstallerSession child : children) {
child.prepareInheritedFiles();
child.parseApkAndExtractNativeLibraries();
}
} else {
prepareInheritedFiles();
parseApkAndExtractNativeLibraries();
}
verifyNonStaged();
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
setSessionFailed(e.error, errorMsg);
onSessionVerificationFailure(e.error, errorMsg);
}
}
verify中会调用prepareInheritedFiles()在/data/app/的app安装目录下创建oat的目录和/lib/arm64,最后调用verifyNonStaged。
java
private void verifyNonStaged()
throws PackageManagerException {
synchronized (mLock) {
markStageDirInUseLocked();
}
mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
mHandler.post(() -> {
if (dispatchPendingAbandonCallback()) {
// No need to continue if abandoned
return;
}
if (error == INSTALL_SUCCEEDED) {
onVerificationComplete();
} else {
onSessionVerificationFailure(error, msg);
}
});
});
}
mSessionProvider.getSessionVerifier().verify调用的是PackageSessionVerifier的verify。
java
//framework/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
/**
* Runs verifications that are common to both staged and non-staged sessions.
*/
public void verify(PackageInstallerSession session, Callback callback) {
mHandler.post(() -> {
try {
storeSession(session.mStagedSession);
if (session.isMultiPackage()) {
for (PackageInstallerSession child : session.getChildSessions()) {
checkApexUpdateAllowed(child);
checkRebootlessApex(child);
checkApexSignature(child);
}
} else {
checkApexUpdateAllowed(session);
checkRebootlessApex(session);
checkApexSignature(session);
}
verifyAPK(session, callback);
} catch (PackageManagerException e) {
String errorMessage = PackageManager.installStatusToString(e.error, e.getMessage());
session.setSessionFailed(e.error, errorMessage);
callback.onResult(e.error, e.getMessage());
}
});
}
/**
* Runs verifications particular to APK. This includes APEX sessions since an APEX can also
* be treated as APK.
*/
private void verifyAPK(PackageInstallerSession session, Callback callback)
throws PackageManagerException {
final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) {
throw new IllegalStateException();
}
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
// Continue verification for staged sessions
verifyStaged(session.mStagedSession, callback);
return;
}
if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
String errorMessage = PackageManager.installStatusToString(returnCode, msg);
session.setSessionFailed(returnCode, errorMessage);
callback.onResult(returnCode, msg);
} else {
session.setSessionReady();
callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
}
}
};
final VerifyingSession verifyingSession = createVerifyingSession(session, observer);
if (session.isMultiPackage()) {
final List<PackageInstallerSession> childSessions = session.getChildSessions();
List<VerifyingSession> verifyingChildSessions = new ArrayList<>(childSessions.size());
for (PackageInstallerSession child : childSessions) {
verifyingChildSessions.add(createVerifyingSession(child, null));
}
verifyingSession.verifyStage(verifyingChildSessions);
} else {
verifyingSession.verifyStage();
}
}
private VerifyingSession createVerifyingSession(
PackageInstallerSession session, IPackageInstallObserver2 observer) {
final UserHandle user;
if ((session.params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(session.userId);
}
return new VerifyingSession(user, session.stageDir, observer, session.params,
session.getInstallSource(), session.getInstallerUid(),
session.getSigningDetails(),session.sessionId,
session.getPackageLite(), session.getUserActionRequired(), mPm);
}
verify调用verifyAPK,verifyAPK中会创建一个observer来接收安装进度结果,然后把observer传入VerifyingSession中,最后调动VerifyingSession的verifyStage()。
java
//framework/base/services/core/java/com/android/server/pm/VerifyingSession.java
public void verifyStage() {
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueVerify",
System.identityHashCode(this));
mPm.mHandler.post(this::start);
}
对应perfetto中的部分如下:
queueVerify结束之后,就开始start流程,对应perfetto中如下部分:
java
private void start() {
if (DEBUG_INSTALL) Slog.i(TAG, "start " + mUser + ": " + this);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueVerify",
System.identityHashCode(this));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "start");
handleStartVerify();
handleReturnCode();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
public void handleStartVerify() {
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(
mPm.mContext, mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
mPackageAbiOverride);
Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
setReturnCode(ret.first, ret.second);
if (mRet != INSTALL_SUCCEEDED) {
return;
}
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (!mOriginInfo.mExisting) {
if (!isApex()) {
// TODO(b/182426975): treat APEX as APK when APK verification is concerned
sendApkVerificationRequest(pkgLite);
}
if ((mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
sendEnableRollbackRequest();
}
}
}
void handleReturnCode() {
if (mWaitForVerificationToComplete || mWaitForIntegrityVerificationToComplete
|| mWaitForEnableRollbackToComplete) {
return;
}
sendVerificationCompleteNotification();
if (mRet != INSTALL_SUCCEEDED) {
PackageMetrics.onVerificationFailed(this);
}
}
private void sendVerificationCompleteNotification() {
if (mParentVerifyingSession != null) {
mParentVerifyingSession.trySendVerificationCompleteNotification(this);
} else {
try {
mObserver.onPackageInstalled(null, mRet, mErrorMessage,
new Bundle());
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
}
首先在handleStartVerify中进行一些校验,然后通知mObserver进行onPackageInstalled。
PackageSessionVerifier中收到onPackageInstalled后,做了下面两件事。
java
session.setSessionReady();
callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
setSessionReady中,主要进行一些变量的赋值,
java
void setSessionReady() {
synchronized (mLock) {
// Do not allow destroyed/failed session to change state
if (mDestroyed || mSessionFailed) return;
mSessionReady = true;
mSessionApplied = false;
mSessionFailed = false;
mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
mSessionErrorMessage = "";
}
mCallback.onSessionChanged(this);
}
而callback.onResult(PackageManager.INSTALL_SUCCEEDED, null)中主要是调用onVerificationComplete()。
4 apk的install流程
java
@WorkerThread
private void onVerificationComplete() {
if (isStaged()) {
mStagingManager.commitSession(mStagedSession);
sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
return;
}
install();
}
/**
* Stages installs and do cleanup accordingly depending on whether the installation is
* successful or not.
*
* @return a future that will be completed when the whole process is completed.
*/
private CompletableFuture<Void> install() {
List<CompletableFuture<InstallResult>> futures = installNonStaged();
CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
if (t == null) {
setSessionApplied();
for (CompletableFuture<InstallResult> f : futures) {
InstallResult result = f.join();
result.session.dispatchSessionFinished(
INSTALL_SUCCEEDED, "Session installed", result.extras);
}
} else {
PackageManagerException e = (PackageManagerException) t.getCause();
setSessionFailed(e.error,
PackageManager.installStatusToString(e.error, e.getMessage()));
dispatchSessionFinished(e.error, e.getMessage(), null);
maybeFinishChildSessions(e.error, e.getMessage());
}
});
}
/**
* Stages sessions (including child sessions if any) for install.
*
* @return a list of futures to indicate the install results of each session.
*/
private List<CompletableFuture<InstallResult>> installNonStaged() {
final InstallingSession installingSession = createInstallingSession(future);
installingSession.installStage();
return futures;
}
public void installStage() {
setTraceMethod("installStage").setTraceCookie(System.identityHashCode(this));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
System.identityHashCode(this));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(this));
mPm.mHandler.post(this::start);
}
install最后走到了installStage,对应的perfetto如下:
java
private void start() {
if (DEBUG_INSTALL) Slog.i(TAG, "start " + mUser + ": " + this);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(this));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startInstall");
InstallRequest installRequest = new InstallRequest(this);
handleStartCopy(installRequest);
handleReturnCode(installRequest);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
installStage会走到mPm.mHandler.post(this::start), 对应的perfetto如下:
handleStartCopy中主要是获取安装目录信息。再来看handleReturnCode
java
private void handleReturnCode(InstallRequest installRequest) {
processPendingInstall(installRequest);
}
private void processPendingInstall(InstallRequest installRequest) {
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = copyApk(installRequest);
}
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
F2fsUtils.releaseCompressedBlocks(
mPm.mContext.getContentResolver(), new File(installRequest.getCodePath()));
}
installRequest.setReturnCode(mRet);
if (mParentInstallingSession != null) {
mParentInstallingSession.tryProcessInstallRequest(installRequest);
} else {
// Queue up an async operation since the package installation may take a little while.
mPm.mHandler.post(() -> processInstallRequests(
mRet == PackageManager.INSTALL_SUCCEEDED /* success */,
Collections.singletonList(installRequest)));
}
}
4.1 apk和二进制拷贝
首先拷贝apk。
java
private int copyApk(InstallRequest request) {
if (mMoveInfo == null) {
return copyApkForFileInstall(request);
} else {
return copyApkForMoveInstall(request);
}
}
private int copyApkForFileInstall(InstallRequest request) {
ret = PackageManagerServiceUtils.copyPackage(
mOriginInfo.mFile.getAbsolutePath(), request.getCodeFile());
...
final boolean isIncremental = isIncrementalPath(
request.getCodeFile().getAbsolutePath());
final File libraryRoot = new File(request.getCodeFile(), LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(request.getCodeFile());
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
request.getAbiOverride(), isIncremental);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
final String errorMessage = "Failed to copy native libraries";
request.setError(ret, errorMessage);
}
}
}
copyApkForFileInstall主要做了两件事:拷贝apk、常见nativelib目录。
先来看拷贝apk
java
/**
* Copy package to the target location.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
* @return returns status code according to those in
* {@link PackageManager}
*/
public static int copyPackage(String packagePath, File targetDir) {
copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
}
private static void copyFile(String sourcePath, File targetDir, String targetName)
throws ErrnoException, IOException {
if (!FileUtils.isValidExtFilename(targetName)) {
throw new IllegalArgumentException("Invalid filename: " + targetName);
}
Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
final File targetFile = new File(targetDir, targetName);
final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(targetFile.getAbsolutePath(), 0644);
FileInputStream source = null;
try {
source = new FileInputStream(sourcePath);
FileUtils.copy(source.getFD(), targetFd);
} finally {
IoUtils.closeQuietly(source);
}
}
copyPackage把apk拷贝到apk的安装目录,可以看到就是我们看到的/data/app下面的那个base.apk。
然后handle = NativeLibraryHelper.Handle.create(request.getCodeFile());创建目录,然后拷贝NativeBinaries,也就是/data/app下面的lib64。
4.2 installPackages流程
继续看processPendingInstall,之后会走到processInstallRequests。
java
private void processInstallRequests(boolean success, List<InstallRequest> installRequests) {
processApkInstallRequests(success, installRequests);
}
private void processApkInstallRequests(boolean success, List<InstallRequest> installRequests) {
mInstallPackageHelper.installPackagesTraced(installRequests);
for (InstallRequest request : installRequests) {
request.onInstallCompleted();
doPostInstall(request);
}
for (InstallRequest request : installRequests) {
mInstallPackageHelper.restoreAndPostInstall(request);
}
}
void installPackagesTraced(List<InstallRequest> requests) {
synchronized (mPm.mInstallLock) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
installPackagesLI(requests);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
}
processInstallRequests最后会开启installPackagesLI流程
5 installPackagesLI
先看perfetto中的流程。
java
/**
* Installs one or more packages atomically. This operation is broken up into four phases:
* <ul>
* <li><b>Prepare</b>
* <br/>Analyzes any current install state, parses the package and does initial
* validation on it.</li>
* <li><b>Scan</b>
* <br/>Interrogates the parsed packages given the context collected in prepare.</li>
* <li><b>Reconcile</b>
* <br/>Validates scanned packages in the context of each other and the current system
* state to ensure that the install will be successful.
* <li><b>Commit</b>
* <br/>Commits all scanned packages and updates system state. This is the only place
* that system state may be modified in the install flow and all predictable errors
* must be determined before this phase.</li>
* </ul>
*
* Failure at any phase will result in a full failure to install all packages.
*/
@GuardedBy("mPm.mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
//1.preparePackageLI
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
request.onPrepareStarted();
preparePackageLI(request);
//2、scanPackageTracedLI
final ScanResult scanResult = scanPackageTracedLI(request.getParsedPackage(),
request.getParseFlags(), request.getScanFlags(),
System.currentTimeMillis(), request.getUser(),
request.getAbiOverride());
//3、reconcilePackages
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
reconciledPackages = ReconcilePackageUtils.reconcilePackages(
requests, Collections.unmodifiableMap(mPm.mPackages),
versionInfos, mSharedLibraries,
mPm.mSettings.getKeySetManagerService(),
mPm.mSettings);
//4、commitPackagesLocked
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());
//5、executePostCommitStepsLIF
executePostCommitStepsLIF(reconciledPackages);
//6、broadcastPackageVerified
if (success) {
VerificationUtils.broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, rootHashString,
request.getDataLoaderType(), request.getUser(),
mContext);
}
}
从代码看,installPackagesLI主要进行了6步操作,下面一步步的看。
5.1 preparePackageLI-准备阶段
java
@GuardedBy("mPm.mInstallLock")
private void preparePackageLI(InstallRequest request) throws PrepareFailure {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
} catch (PackageManagerException e) {
throw new PrepareFailure("Failed parse during installPackageLI", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@GuardedBy("mPm.mInstallLock")
private void preparePackageLI(InstallRequest request) throws PrepareFailure {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
}
...
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
input, parsedPackage, false /*skipVerify*/);
parsedPackage.setSigningDetails(result.getResult());
...
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage, systemApp,
isUpdatedSystemAppFromExistingSetting ||
isUpdatedSystemAppInferred,
abiOverride, ScanPackageUtils.getAppLib32InstallDir());
...
doRenameLI(request, parsedPackage);
}
Prepare准备:分析任何当前安装状态,分析包并对其进行初始验证。
在这一阶段首先是将apk文件解析出来,解析它的AndroidManifest.xml文件,将结果记录起来。我们平时在清单文件中声明的Activity等组件就是在这一步被记录到Framework中的,后续才能通过startActivity等方式启动来,然后是对签名信息进行验证。
总结:分析当前安装包的状态,解析安装包并对其做初始化验证
5.2 scanPackageTracedLI-扫描阶段
java
@GuardedBy("mPm.mInstallLock")
private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
final @ParsingPackageUtils.ParseFlags int parseFlags,
@PackageManagerService.ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user, String cpuAbiOverride)
throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
try {
return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime,
user, cpuAbiOverride);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@GuardedBy("mPm.mInstallLock")
private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
final @ParsingPackageUtils.ParseFlags int parseFlags,
@PackageManagerService.ScanFlags int scanFlags,
long currentTime, @Nullable UserHandle user,
String cpuAbiOverride) throws PackageManagerException {
synchronized (mPm.mLock) {
assertPackageIsValid(parsedPackage, parseFlags, newScanFlags);
final ScanRequest request = new ScanRequest(parsedPackage,
initialScanRequest.mOldSharedUserSetting,
initialScanRequest.mOldPkg, installedPkgSetting,
initialScanRequest.mSharedUserSetting, disabledPkgSetting,
initialScanRequest.mOriginalPkgSetting,
initialScanRequest.mRealPkgName,parseFlags, scanFlags,
initialScanRequest.mIsPlatformPackage, user, cpuAbiOverride);
return ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector,
mPm.mFactoryTest, currentTime);
}
}
/**
* Just scans the package without any side effects.
*
* @param injector injector for acquiring dependencies
* @param request Information about the package to be scanned
* @param isUnderFactoryTest Whether or not the device is under factory test
* @param currentTime The current time, in millis
* @return The results of the scan
*/
@GuardedBy("mPm.mInstallLock")
@VisibleForTesting
@NonNull
public static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
PackageManagerServiceInjector injector, boolean isUnderFactoryTest,
long currentTime) throws PackageManagerException {
...
final boolean createNewPackage = (pkgSetting == null);
if (createNewPackage) {
pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
originalPkgSetting, disabledPkgSetting, realPkgName,
sharedUserSetting, destCodeFile,
parsedPackage.getNativeLibraryRootDir(),
AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage), AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags,
user,true /*allowInstall*/, instantApp, virtualPreload,
sStoppedSystemApp, UserManagerService.getInstance(),
usesSdkLibraries,
parsedPackage.getUsesSdkLibrariesVersionsMajor(),
usesStaticLibraries,
parsedPackage.getUsesStaticLibrariesVersions(),
parsedPackage.getMimeGroups(), newDomainSetId);
}
pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils
.getRawPrimaryCpuAbi(parsedPackage))
.setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
.setCpuAbiOverride(cpuAbiOverride);
return new ScanResult(request, pkgSetting, changedAbiCodePath,
!createNewPackage /* existingSettingCopied */,
Process.INVALID_UID /* previousAppId */ , sdkLibraryInfo,
staticSharedLibraryInfo, dynamicSharedLibraryInfos);
}
scanPackageTracedLI主要工作是把第一步preparePackageLI中解析的apk信息保存到一个PackageSetting对象中
总结:根据prepare阶段中收集的安装包状态信息去扫描解析出来的包
5.3 reconcilePackages-协调阶段
java
public static List<ReconciledPackage> reconcilePackages(
List<InstallRequest> installRequests,
Map<String, AndroidPackage> allPackages,
Map<String, Settings.VersionInfo> versionInfos,
SharedLibrariesImpl sharedLibraries,
KeySetManagerService ksms, Settings settings)
throws ReconcileFailure {
final List<ReconciledPackage> result = new ArrayList<>(installRequests.size());
// make a copy of the existing set of packages so we can combine them with incoming packages
final ArrayMap<String, AndroidPackage> combinedPackages =
new ArrayMap<>(allPackages.size() + installRequests.size());
combinedPackages.putAll(allPackages);
final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
new ArrayMap<>();
}
这里combinedPackages是当前安装包大小加上要安装包的大小,接着它把当前的安装包对象都放到combinedPackages中。allPackages.allPackages中是当前的安装包对象。combinedPackages是当前安装包大小加上要安装包的大小,接着它把当前的安装包对象都放到combinedPackages中。
incomingSharedLibraries对象是安装包对象中声明的即将安装的库。
先把每个安装包的解析包对象放入combinedPackages中。因为它是ArrayMap类型,所以它也可能覆盖之前的解析包对象。下面是处理共享库信息。
java
final Settings.VersionInfo versionInfo = versionInfos.get(installPackageName);
final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
final boolean isRollback = installRequest.isRollback();
final boolean compatMatch =
PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
sharedUserSetting, disabledPkgSetting,
signingDetails, compareCompat,
compareRecover, isRollback);
该段代码主要是用来验证安装包的签名。
java
final ReconciledPackage reconciledPackage = new ReconciledPackage(installRequests,
allPackages, installRequest,deletePackageAction,
allowedSharedLibInfos, signingDetails,
sharedUserSignaturesChanged, removeAppKeySetData);
如果之前的都没什么问题,会将相关信息封装到ReconciledPackage对象中,然后添加到结果result中。封装到ReconciledPackage对象的包括ReconcileRequest对象request、安装参数installArgs、新生成的PackageSettingscanResult.pkgSetting、安装信息res、准备结果对象、浏览结果对象、待删除包行动兑对象deletePackageAction、允许添加的包中的共享库信息、解析包签名信息对象、共享用户签名是否改变、是否删除签名信息removeAppKeySetData。
这段代码是得到每个安装包的共享库信息,在系统启动或者是系统解析包的情况下,是不进行该项操作的。
java
reconciledPackage.mCollectedSharedLibraryInfos =
sharedLibraries.collectSharedLibraryInfos(
installRequest.getParsedPackage(), combinedPackages,
incomingSharedLibraries);
主要是调用collectSharedLibraryInfos()方法来得到。collectSharedLibraryInfos主要是对解析包对象的getUsesLibraries()、getUsesStaticLibraries()、getUsesOptionalLibraries()、getUsesNativeLibraries()、getUsesOptionalNativeLibraries()进行处理,它们分别是从Manifest文件中配置的"uses-library"、"uses-static-library"、"uses-library"、"uses-native-library"、"uses-native-library"标签中解析出来的。其中getUsesLibraries()与getUsesOptionalLibraries()是根据标签中的属性"required"来区分。getUsesNativeLibraries()、getUsesOptionalNativeLibraries()也是根据标签中的属性"required"来区分。
它们又都调用了另一个同名collectSharedLibraryInfos()方法来实现。同名collectSharedLibraryInfos()方法的实现具体来说就是根据existingLibraries(存在的库), newLibraries(新加的库),如果新加的库不在它们两个中,根据参数required,如果它为false,可以跳过,如果它为true,则会抛出PackageManagerException异常。如果需要判断版本和摘要(根据参数),传递的参数摘要和对应的签名信息经过计算得出的相符,则通过。
这样最后,将结果result返回。
总结: 验证scan阶段扫描到的Package信息以及当前系统状态,确保apk的正确安装。
对普通APP替换安装的生成一个删除包行为对象。
对新生成的PackageSetting对象,验证签名。验证通过之后,将相关信息封装成ReconciledPackage对象,放到返回结果中。
对每一个安装应用包,收集共享库信息。
5.4 commitPackagesLocked
首先来看下perfetto中的commitPackages
java
commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());
@GuardedBy("mPm.mLock")
private void commitPackagesLocked(List<ReconciledPackage> reconciledPackages,
@NonNull int[] allUsers) {
AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, allUsers);
updateSettingsLI(pkg, allUsers, installRequest);
}
@GuardedBy("mPm.mLock")
public AndroidPackage commitReconciledScanResultLocked(
@NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
mPm.mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
commitPackageSettings(pkg, pkgSetting, oldPkgSetting, reconciledPkg);
}
这里面也是安装应用和扫描应用同时都会调用的方法,先只看扫描的情况
-
处理PackageSetting,这部分逻辑是和安装应用是混在一起的,主要包括
- SharedUserSetting, 这个一般只有覆盖安装的时候如果sharedUserId变了,要重新赋值
- 重命名包名的逻辑,安装重命名包名的时候会更新packages.xml文件
-
将PackageSetting写入package-restrictions.xml,里面主要存储的是disabled和enabled四大组件
-
最后调用commitPackageSettings()进行最后一步处理.
java
/**
* Adds a scanned package to the system. When this method is finished, the package will
* be available for query, resolution, etc...
*/
private void commitPackageSettings(@NonNull AndroidPackage pkg,
@NonNull PackageSetting pkgSetting,
@Nullable PackageSetting oldPkgSetting,
ReconciledPackage reconciledPkg) {
// writer
ArrayList<AndroidPackage> clientLibPkgs =
mSharedLibraries.commitSharedLibraryChanges(pkg, pkgSetting,
reconciledPkg.mAllowedSharedLibraryInfos,
reconciledPkg.getCombinedAvailablePackages(), scanFlags);
// writer
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
synchronized (mPm.mLock) {
// We don't expect installation to fail beyond this point
// Add the new setting to mSettings
mPm.mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPm.mPackages.put(pkg.getPackageName(), pkg);
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
mApexManager.registerApkInApex(pkg);
}
final Computer snapshot = mPm.snapshotComputer();
mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage, snapshot);
mPm.mAppsFilter.addPackage(snapshot, pkgSetting, isReplace,
(scanFlags & SCAN_DONT_KILL_APP) != 0 /* retainImplicitGrantOnReplace */);
mPm.addAllPackageProperties(pkg);
mPm.mPermissionManager.onPackageAdded(pkgSetting,
(scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
}
}
- 调用mSettings.insertPackageSettingLPw(),这里并非是更新packages.xml的地方,只是将pkgSetting,放到Settings.mPackages中
- 将AndroidPackage放到mPackages中
- 更新KeySetManagerService的应用信息,这是一个管理签名的服务
- 添加自定义的Permission和PermissionGroup到PermissionManagerService中
java
private void updateSettingsLI(AndroidPackage newPackage,
int[] allUsers, InstallRequest installRequest) {
updateSettingsInternalLI(newPackage, allUsers, installRequest);
}
private void updateSettingsInternalLI(AndroidPackage pkg,
int[] allUsers, InstallRequest installRequest) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
mPm.mSettings.addInstallerPackageNames(ps.getInstallSource());
...
mPm.mSettings.writeKernelMappingLPr(ps);
...
installRequest.setName(pkgName);
installRequest.setAppId(pkg.getUid());
installRequest.setPkg(pkg);
installRequest.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
//to update install status
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
mPm.writeSettingsLPrTEMP();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
1、addInstallerPackageNames向mInstallerPackages中加入ps.getInstallSource()
2、writeKernelMappingLPr来写/config/sdcardfs/,如向该目录下appid写入appid file
3、将settings写入packages.xml
java
@SuppressWarnings("GuardedBy")
void writeSettingsLPrTEMP(boolean sync) {
snapshotComputer(false);
mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
mSettings.writeLPr(mLiveComputer, sync);
}
总结:提交所有扫描的包并更新系统状态。这是唯一可以在安装流程和所有可预测错误中修改系统状态的地方.
5.5 executePostCommitStepsLIF
java
/**
* On successful install, executes remaining steps after commit completes and the package lock
* is released. These are typically more expensive or require calls to installd, which often
* locks on {@link com.android.server.pm.PackageManagerService.mLock}.
*/
@GuardedBy("mPm.mInstallLock")
private void executePostCommitStepsLIF(List<ReconciledPackage> reconciledPackages) {
final boolean performDexopt =
(!instantApp || android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& !pkg.isDebuggable()
&& (!onIncremental)
&& dexoptOptions.isCompilationEnabled()
&& !isApex;
if (performDexopt) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
if (useArtService()) {
PackageManagerLocal packageManagerLocal =
LocalManagerRegistry.getManager(PackageManagerLocal.class);
try (PackageManagerLocal.FilteredSnapshot snapshot =
packageManagerLocal.withFilteredSnapshot()) {
DexoptParams params =
dexoptOptions.convertToDexoptParams(0 /* extraFlags */);
DexoptResult dexOptResult = DexOptHelper.getArtManagerLocal().dexoptPackage(
snapshot, packageName, params);
installRequest.onDexoptFinished(dexOptResult);
}
}
}
PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
incrementalStorages);
}
调用了executePostCommitStepsLIF完成apk的安装,执行dex优化等操作。
检测是否需要进行dex优化:同时满足下面几种情况:
1.不是一个即时应用app或者instant_app_dexopt_enabled属性不是0
2.debuggable为false
3.不在增量文件系统上
4. 不是Apex
5. 编译选项没有skip
最后放出perfetto
5.6 broadcastPackageVerified
java
public static void broadcastPackageVerified(int verificationId, Uri packageUri,
int verificationCode, @Nullable String rootHashString,
int dataLoaderType, UserHandle user, Context context) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
if (rootHashString != null) {
intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
}
intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
context.sendBroadcastAsUser(intent, user,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
}
发送PACKAGE_VERIFIED广播。
6 返回结果
最后通知VerifyingSession#handleVerificationFinished
java
if (verifyingSession != null) {
verifyingSession.handleVerificationFinished();
}
void handleVerificationFinished() {
mWaitForVerificationToComplete = false;
handleReturnCode();
}
private void sendVerificationCompleteNotification() {
if (mParentVerifyingSession != null) {
mParentVerifyingSession.trySendVerificationCompleteNotification(this);
} else {
try {
mObserver.onPackageInstalled(null, mRet, mErrorMessage,
new Bundle());
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
}
这里是mObserver.onPackageInstalled
java
//framework/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
@Override
public void onPackageInstalled(String basePackageName, int returnCode,
String msg, Bundle extras) {
if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
// Continue verification for staged sessions
verifyStaged(session.mStagedSession, callback);
return;
}
if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
String errorMessage = PackageManager.installStatusToString(
returnCode, msg);
session.setSessionFailed(returnCode, errorMessage);
callback.onResult(returnCode, msg);
} else {
session.setSessionReady();
callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
}
}
}
//framework/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private void verifyNonStaged() throws PackageManagerException {
mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
mHandler.post(() -> {
if (dispatchPendingAbandonCallback()) {
// No need to continue if abandoned
return;
}
if (error == INSTALL_SUCCEEDED) {
onVerificationComplete();
} else {
onSessionVerificationFailure(error, msg);
}
});
});
}
@WorkerThread
private void onVerificationComplete() {
if (isStaged()) {
mStagingManager.commitSession(mStagedSession);
sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
return;
}
install();
}
这里走isStaged
java
private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
final IntentSender statusReceiver = getRemoteStatusReceiver();
if (statusReceiver != null) {
// Execute observer.onPackageInstalled on different thread as we don't want callers
// inside the system server have to worry about catching the callbacks while they are
// calling into the session
final SomeArgs args = SomeArgs.obtain();
args.arg1 = getPackageName();
args.arg2 = msg;
args.arg3 = extras;
args.arg4 = statusReceiver;
args.argi1 = returnCode;
args.argi2 = isPreapprovalRequested() && !isCommitted() ? 1 : 0;
mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
}
}
case MSG_ON_PACKAGE_INSTALLED:
final SomeArgs args = (SomeArgs) msg.obj;
final String packageName = (String) args.arg1;
final String message = (String) args.arg2;
final Bundle extras = (Bundle) args.arg3;
final IntentSender statusReceiver = (IntentSender) args.arg4;
final int returnCode = args.argi1;
final boolean isPreapproval = args.argi2 == 1;
args.recycle();
sendOnPackageInstalled(mContext, statusReceiver, sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
packageName, returnCode, isPreapproval, message, extras);
break;
sendOnPackageInstalled中先发送安装成功的通知,然后向IntentSender发送sendIntent。
从第2节中可知道IntentSender是在PackageInstaller的apk传入的。
java
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
全文完。