做項(xiàng)目掙錢的網(wǎng)站競價(jià)托管如何托管
??現(xiàn)在馬上進(jìn)入正式的安裝流程。
??從前面文章 Android 安裝過程四 MSG_INSTALL消息的處理 安裝之前的驗(yàn)證知道,在驗(yàn)證之后沒有什么問題的情況下,會回調(diào)onVerificationComplete()方法,它位于PackageInstallerSession類中。
private void onVerificationComplete() {// Staged sessions will be installed later during bootif (isStaged()) {// TODO(b/136257624): Remove this once all verification logic has been transferred out// of StagingManager.mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);// TODO(b/136257624): We also need to destroy internals for verified staged session,// otherwise file descriptors are never closed for verified staged session until rebootreturn;}install();}……private void install() {try {installNonStaged();} catch (PackageManagerException e) {final String completeMsg = ExceptionUtils.getCompleteMessage(e);onSessionInstallationFailure(e.error, completeMsg);}}
??看到不是在緩存下來進(jìn)行安裝的情況下,會繼續(xù)調(diào)用installNonStaged()方法。
private void installNonStaged()throws PackageManagerException {final PackageManagerService.InstallParams installingSession = makeInstallParams();if (installingSession == null) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,"Session should contain at least one apk session for installation");}if (isMultiPackage()) {final List<PackageInstallerSession> childSessions;synchronized (mLock) {childSessions = getChildSessionsLocked();}List<PackageManagerService.InstallParams> installingChildSessions =new ArrayList<>(childSessions.size());boolean success = true;PackageManagerException failure = null;for (int i = 0; i < childSessions.size(); ++i) {final PackageInstallerSession session = childSessions.get(i);try {final PackageManagerService.InstallParams installingChildSession =session.makeInstallParams();if (installingChildSession != null) {installingChildSessions.add(installingChildSession);}} catch (PackageManagerException e) {failure = e;success = false;}}if (!success) {final IntentSender statusReceiver;synchronized (mLock) {statusReceiver = mRemoteStatusReceiver;}sendOnPackageInstalled(mContext, statusReceiver, sessionId,isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,failure.error, failure.getLocalizedMessage(), null);return;}mPm.installStage(installingSession, installingChildSessions);} else {mPm.installStage(installingSession);}}
構(gòu)造安裝參數(shù)InstallParams對象
??和驗(yàn)證那塊的代碼有些像,先調(diào)用makeInstallParams()構(gòu)造安裝參數(shù)InstallParams對象installingSession。暫時忽略多包安裝的情況,接著就是到PackageManagerService對象中調(diào)用installStage(installingSession)方法,執(zhí)行安裝。
??先看一下安裝參數(shù)的構(gòu)成方法makeInstallParams():
@Nullableprivate PackageManagerService.InstallParams makeInstallParams()throws PackageManagerException {synchronized (mLock) {if (mDestroyed) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");}if (!mSealed) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");}}// Do not try to install staged apex session. Parent session will have at least one apk// session.if (!isMultiPackage() && isApexSession() && params.isStaged) {sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,"Apex package should have been installed by apexd", null);return null;}final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {@Overridepublic void onUserActionRequired(Intent intent) {throw new IllegalStateException();}@Overridepublic void onPackageInstalled(String basePackageName, int returnCode, String msg,Bundle extras) {if (isStaged()) {sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);} else {// We've reached point of no return; call into PMS to install the stage.// Regardless of success or failure we always destroy session.destroyInternal();dispatchSessionFinished(returnCode, msg, extras);}}};final UserHandle user;if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {user = UserHandle.ALL;} else {user = new UserHandle(userId);}if (params.isStaged) {params.installFlags |= INSTALL_STAGED;}if (!isMultiPackage() && !isApexSession()) {synchronized (mLock) {// This shouldn't be null, but have this code path just in case.if (mPackageLite == null) {Slog.wtf(TAG, "Session: " + sessionId + ". Don't have a valid PackageLite.");}mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);}}synchronized (mLock) {return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,mSigningDetails, mInstallerUid, mPackageLite);}}
??首先檢查Session對象的狀態(tài),如果是mDestroyed或!mSealed,都會拋出PackageManagerException。
??接著會生成一個回調(diào)對象localObserver。等待后面安裝完畢之后,會調(diào)用它的onPackageInstalled回調(diào)接口。
??如果不是多包安裝和不是apex安裝的情況下,如果成員變量為null,會調(diào)用getOrParsePackageLiteLocked(stageDir, /* flags */ 0)再次生成它。
??最后就是調(diào)用InstallParams 的構(gòu)造函數(shù)來生成InstallParams 對象了。
進(jìn)入PackageManagerService中執(zhí)行安裝
??PackageManagerService類中的installStage(InstallParams params)如下:
void installStage(InstallParams params) {final Message msg = mHandler.obtainMessage(INIT_COPY);params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));msg.obj = params;Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",System.identityHashCode(msg.obj));Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(msg.obj));mHandler.sendMessage(msg);}
??看下INIT_COPY消息的處理,
void doHandleMessage(Message msg) {switch (msg.what) {case INIT_COPY: {HandlerParams params = (HandlerParams) msg.obj;if (params != null) {if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(params));Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");params.startCopy();Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}break;}…………
??msg.obj就是前面構(gòu)造的InstallParams 對象,這里調(diào)用它的startCopy()方法。InstallParams 是繼承自HandlerParams類,它的startCopy(),就是調(diào)用handleStartCopy()和handleReturnCode()。這倆方法是實(shí)現(xiàn)在InstallParams 類中的。
InstallParams 類的handleStartCopy()
??先看InstallParams 類的handleStartCopy():
public void handleStartCopy() {if ((installFlags & PackageManager.INSTALL_APEX) != 0) {mRet = INSTALL_SUCCEEDED;return;}PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);// For staged session, there is a delay between its verification and install. Device// state can change within this delay and hence we need to re-verify certain conditions.boolean isStaged = (installFlags & INSTALL_STAGED) != 0;if (isStaged) {mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags);if (mRet != INSTALL_SUCCEEDED) {return;}}mRet = overrideInstallLocation(pkgLite);}
??通過PackageManagerServiceUtils.getMinimalPackageInfo()得到包信息對象pkgLite。接著就調(diào)用overrideInstallLocation(pkgLite)重寫安裝位置,主要是可能改變安裝標(biāo)識。
??這兩個方法咱們主要關(guān)注一下關(guān)于安裝位置方面的東西。
??先看下PackageManagerServiceUtils.getMinimalPackageInfo()方法:
public static PackageInfoLite getMinimalPackageInfo(Context context, PackageLite pkg,String packagePath, int flags, String abiOverride) {final PackageInfoLite ret = new PackageInfoLite();if (packagePath == null || pkg == null) {Slog.i(TAG, "Invalid package file " + packagePath);ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;return ret;}final File packageFile = new File(packagePath);final long sizeBytes;try {sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);} catch (IOException e) {if (!packageFile.exists()) {ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;} else {ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;}return ret;}final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,pkg.getPackageName(), pkg.getInstallLocation(), sizeBytes, flags);ret.packageName = pkg.getPackageName();ret.splitNames = pkg.getSplitNames();ret.versionCode = pkg.getVersionCode();ret.versionCodeMajor = pkg.getVersionCodeMajor();ret.baseRevisionCode = pkg.getBaseRevisionCode();ret.splitRevisionCodes = pkg.getSplitRevisionCodes();ret.installLocation = pkg.getInstallLocation();ret.verifiers = pkg.getVerifiers();ret.recommendedInstallLocation = recommendedInstallLocation;ret.multiArch = pkg.isMultiArch();ret.debuggable = pkg.isDebuggable();return ret;}
??這里需要關(guān)注的是PackageInfoLite對象的recommendedInstallLocation和installLocation成員變量。
??這里看到recommendedInstallLocation的意思是推薦的安裝位置,如果結(jié)果如果是以PackageHelper.RECOMMEND_FAILED 開頭的,代表失敗,出問題了。
??像這里計(jì)算安裝大小sizeBytes時,出現(xiàn)異常時,RECOMMEND_FAILED_INVALID_URI代表文件不存在,RECOMMEND_FAILED_INVALID_APK代表不是有效的APK文件。
??installLocation是來自安裝包的解析屬性,它是配置在Manifest中的"installLocation"屬性,如果沒有配置,默認(rèn)為PackageInfo.INSTALL_LOCATION_UNSPECIFIED。
??推薦安裝位置,主要是通過PackageHelper.resolveInstallLocation()方法來得到。再看一下它:
public static int resolveInstallLocation(Context context, SessionParams params)throws IOException {ApplicationInfo existingInfo = null;try {existingInfo = context.getPackageManager().getApplicationInfo(params.appPackageName,PackageManager.MATCH_ANY_USER);} catch (NameNotFoundException ignored) {}final int prefer;final boolean checkBoth;boolean ephemeral = false;if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {prefer = RECOMMEND_INSTALL_INTERNAL;ephemeral = true;checkBoth = false;} else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {prefer = RECOMMEND_INSTALL_EXTERNAL;checkBoth = true;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {// When app is already installed, prefer same mediumif (existingInfo != null) {// TODO: distinguish if this is external ASECif ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {prefer = RECOMMEND_INSTALL_EXTERNAL;} else {prefer = RECOMMEND_INSTALL_INTERNAL;}} else {prefer = RECOMMEND_INSTALL_INTERNAL;}checkBoth = true;} else {prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;}boolean fitsOnInternal = false;if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {fitsOnInternal = fitsOnInternal(context, params);}boolean fitsOnExternal = false;if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {fitsOnExternal = fitsOnExternal(context, params);}if (prefer == RECOMMEND_INSTALL_INTERNAL) {// The ephemeral case will either fit and return EPHEMERAL, or will not fit// and will fall through to return INSUFFICIENT_STORAGEif (fitsOnInternal) {return (ephemeral)? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL: PackageHelper.RECOMMEND_INSTALL_INTERNAL;}} else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {if (fitsOnExternal) {return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}}if (checkBoth) {if (fitsOnInternal) {return PackageHelper.RECOMMEND_INSTALL_INTERNAL;} else if (fitsOnExternal) {return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}}return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;}
??從這里可以看到推薦安裝的值。
??這里定義變量prefer是比較推薦安裝的地方。checkBoth是檢查內(nèi)部存儲和外部存儲空間是否滿足。
??優(yōu)先判斷的是標(biāo)識installFlags。如果它不滿足,會去判斷installLocation中的值。
??如果明確推薦位置,checkBoth為false,它之后不會內(nèi)部和外部存儲空間兩個都做判斷。
??像滿足安裝位置為INSTALL_LOCATION_PREFER_EXTERNAL或PackageInfo.INSTALL_LOCATION_AUTO的情況下,將checkBoth = true。像INSTALL_LOCATION_INTERNAL_ONLY明確就在內(nèi)部安裝,就將checkBoth = false。
??fitsOnInternal()和fitsOnExternal()兩個方法就是去判斷內(nèi)部空間和外部空間是否滿足安裝大小。如果滿足,返回true。
??如果推薦內(nèi)部安裝,并且內(nèi)部大小充足,就返回RECOMMEND_INSTALL_INTERNAL(Instant安裝返回RECOMMEND_INSTALL_EPHEMERAL)。推薦外部安裝,外部空間滿足,就返回RECOMMEND_INSTALL_EXTERNAL。
??如果前面兩個都不是,并且滿足需要檢查兩個空間的情況,如果空間足夠,優(yōu)先內(nèi)部安裝,返回RECOMMEND_INSTALL_INTERNAL,再外部安裝。
??如果以上都不滿足,返回RECOMMEND_FAILED_INSUFFICIENT_STORAGE,代表空間不足。
??handleStartCopy()接下來執(zhí)行overrideInstallLocation(pkgLite),看一下它:
private int overrideInstallLocation(PackageInfoLite pkgLite) {final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;if (DEBUG_INSTANT && ephemeral) {Slog.v(TAG, "pkgLite for install: " + pkgLite);}if (origin.staged) {// If we're already staged, we've firmly committed to an install locationif (origin.file != null) {installFlags |= PackageManager.INSTALL_INTERNAL;} else {throw new IllegalStateException("Invalid stage location");}} else if (pkgLite.recommendedInstallLocation== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {/** If we are not staged and have too little free space, try to free cache* before giving up.*/// TODO: focus freeing disk space on the target devicefinal StorageManager storage = StorageManager.from(mContext);final long lowThreshold = storage.getStorageLowBytes(Environment.getDataDirectory());final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(origin.resolvedPath, packageAbiOverride);if (sizeBytes >= 0) {try {mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,mPackageLite, origin.resolvedPath, installFlags,packageAbiOverride);} catch (InstallerException e) {Slog.w(TAG, "Failed to free cache", e);}}/** The cache free must have deleted the file we downloaded to install.** TODO: fix the "freeCache" call to not delete the file we care about.*/if (pkgLite.recommendedInstallLocation== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {pkgLite.recommendedInstallLocation= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;}}int ret = INSTALL_SUCCEEDED;int loc = pkgLite.recommendedInstallLocation;if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;} else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {ret = PackageManager.INSTALL_FAILED_INVALID_APK;} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {ret = PackageManager.INSTALL_FAILED_INVALID_URI;} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;} else {// Override with defaults if needed.loc = installLocationPolicy(pkgLite);final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;if (!onInt) {// Override install location with flagsif (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {// Set the flag to install on external media.installFlags &= ~PackageManager.INSTALL_INTERNAL;} else {// Make sure the flag for installing on external// media is unsetinstallFlags |= PackageManager.INSTALL_INTERNAL;}}}return ret;}
??可以看到的是,如果pkgLite的recommendedInstallLocation為RECOMMEND_FAILED_INSUFFICIENT_STORAGE時,從上面我們知道,這是由于存儲空間不足了。在這種情況下,它會嘗試去釋放一些空間,調(diào)用的是mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0)。在釋放一些空間之后,接著再去執(zhí)行一遍PackageManagerServiceUtils.getMinimalPackageInfo(),看看能不能后續(xù)得到滿足的結(jié)果。
??接下來,判斷pkgLite.recommendedInstallLocation的值,如果是以PackageManager.INSTALL_FAILED開頭的結(jié)果,都直接設(shè)置返回結(jié)果。
??如果不是失敗的結(jié)果,會接著調(diào)用installLocationPolicy(pkgLite)。該方法是又去根據(jù)該應(yīng)用之前的安裝包信息來再次做判斷。例如,安裝包是系統(tǒng)應(yīng)用包,則返回PackageHelper.RECOMMEND_INSTALL_INTERNAL。在配置文件沒有明確指定位置的情況下,安裝包在外部存儲空間,則返回PackageHelper.RECOMMEND_INSTALL_EXTERNAL。如果沒有之前的安裝包,則返回它自己的推薦值。
??如果installFlags里面有INSTALL_INTERNAL標(biāo)識,則結(jié)果返回INSTALL_SUCCEEDED。如果沒有INSTALL_INTERNAL標(biāo)識,假如loc為不等于PackageHelper.RECOMMEND_INSTALL_EXTERNAL,則將installFlags里加上INSTALL_INTERNAL標(biāo)識。
InstallParams 類的handleReturnCode()
??它執(zhí)行processPendingInstall()方法:
private void processPendingInstall() {InstallArgs args = createInstallArgs(this);if (mRet == PackageManager.INSTALL_SUCCEEDED) {mRet = args.copyApk();}if (mRet == PackageManager.INSTALL_SUCCEEDED) {F2fsUtils.releaseCompressedBlocks(mContext.getContentResolver(), new File(args.getCodePath()));}if (mParentInstallParams != null) {mParentInstallParams.tryProcessInstallRequest(args, mRet);} else {PackageInstalledInfo res = createPackageInstalledInfo(mRet);processInstallRequestsAsync(res.returnCode == PackageManager.INSTALL_SUCCEEDED,Collections.singletonList(new InstallRequest(args, res)));}}}
??首先通過createInstallArgs(this)生成一個InstallArgs 對象。將InstallParams 里的信息,轉(zhuǎn)到InstallArgs 類對象中。在這里,對于我們的例子,它是FileInstallArgs對象。
??接著mRet == PackageManager.INSTALL_SUCCEEDED的情況下,這里會執(zhí)行FileInstallArgs對象的copyApk()。FileInstallArgs對象的copyApk(),如果文件還沒有緩存到對應(yīng)的文件夾下面,需要執(zhí)行復(fù)制。如果文件已經(jīng)復(fù)制過,則不需要再次復(fù)制,直接返回INSTALL_SUCCEEDED結(jié)果。像例子中的,目前已經(jīng)復(fù)制過,所以直接返回INSTALL_SUCCEEDED結(jié)果。
??下面mRet ==PackageManager.INSTALL_SUCCEEDED的情況下,如果文件系統(tǒng)是F2Fs,啟動壓縮情況下,需要先釋放壓縮的數(shù)據(jù)塊,以備下面讀取使用,所以調(diào)用F2fsUtils.releaseCompressedBlocks()方法。
??如果mParentInstallParams不為null,它是一個MultiPackageInstallParams對象,用來在多包安裝時使用,目前的InstallParams對象是多包安裝中的一個。需要調(diào)用MultiPackageInstallParams類的tryProcessInstallRequest()方法。
??如果是單文件安裝包,則先生成一個PackageInstalledInfo對象res。然后將InstallArgs 對象和res封裝到InstallRequest對象中。最后是調(diào)用processInstallRequestsAsync()方法來執(zhí)行后續(xù)安裝。
處理安裝請求
??看下processInstallRequestsAsync()代碼:
// Queue up an async operation since the package installation may take a little while.private void processInstallRequestsAsync(boolean success,List<InstallRequest> installRequests) {mHandler.post(() -> {List<InstallRequest> apexInstallRequests = new ArrayList<>();List<InstallRequest> apkInstallRequests = new ArrayList<>();for (InstallRequest request : installRequests) {if ((request.args.installFlags & PackageManager.INSTALL_APEX) != 0) {apexInstallRequests.add(request);} else {apkInstallRequests.add(request);}}// Note: supporting multi package install of both APEXes and APKs might requir some// thinking to ensure atomicity of the install.if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {// This should've been caught at the validation step, but for some reason wasn't.throw new IllegalStateException("Attempted to do a multi package install of both APEXes and APKs");}if (!apexInstallRequests.isEmpty()) {if (success) {// Since installApexPackages requires talking to external service (apexd), we// schedule to run it async. Once it finishes, it will resume the install.Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),"installApexPackages");t.start();} else {// Non-staged APEX installation failed somewhere before// processInstallRequestAsync. In that case just notify the observer about the// failure.InstallRequest request = apexInstallRequests.get(0);notifyInstallObserver(request.installResult, request.args.observer);}return;}if (success) {for (InstallRequest request : apkInstallRequests) {request.args.doPreInstall(request.installResult.returnCode);}synchronized (mInstallLock) {installPackagesTracedLI(apkInstallRequests);}for (InstallRequest request : apkInstallRequests) {request.args.doPostInstall(request.installResult.returnCode, request.installResult.uid);}}for (InstallRequest request : apkInstallRequests) {restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,new PostInstallData(request.args, request.installResult, null));}});}
??參數(shù)installRequests是List格式,因?yàn)槎喟惭b,也是調(diào)用的它。
??顯然是將操作封裝成消息了,mHandler發(fā)送的消息是在叫"PackageManager"的線程中執(zhí)行的。
??這里有Apex安裝方式的處理??梢钥吹?#xff0c;多包裝時,是不能既用Apex,又用Apk安裝方式的。咱們這里主要看Apk的安裝方式。
??在目前沒錯誤的情況下,會調(diào)用installPackagesTracedLI(apkInstallRequests)執(zhí)行安裝。在安裝之前和之后,都會執(zhí)行對應(yīng)InstallArgs對象的doPreInstall和doPostInstall方法。從名字能看出來,是安裝之前的操作和安裝之后的操作。由于這里是FileInstallArgs類對象,所以執(zhí)行的是FileInstallArgs類的doPreInstall和doPostInstall方法。FileInstallArgs類的doPreInstall和doPostInstall方法都是根據(jù)參數(shù)狀態(tài),如果不是INSTALL_SUCCEEDED,則執(zhí)行清理操作。具體看下面。咱們這里按照還沒發(fā)生錯誤的情況下,繼續(xù)往下看。
??restoreAndPostInstall()方法在不是升級包的情況下(首次安裝屬于這種情況),會向備份管理服務(wù)要求執(zhí)行回復(fù)數(shù)據(jù),當(dāng)然是在合適的情況下(備份服務(wù)可能不會啟動,得配置PackageManager.FEATURE_BACKUP Feature,才會啟動),才會具體執(zhí)行。如果它是替換升級的情況,它還可能會向RollbackManager請求回退數(shù)據(jù),得配置安裝參數(shù)中的PackageManager.INSTALL_ENABLE_ROLLBACK或者PackageManager.INSTALL_REQUEST_DOWNGRADE標(biāo)識。如果這兩個都沒滿足條件,它會做安裝之后的工作。在這里它是向"PackageManager"線程發(fā)送POST_INSTALL消息。它的處理實(shí)現(xiàn)在PackageHandler的handleMessage(Message msg)方法中,在處理POST_INSTALL消息時,會調(diào)用handlePackagePostInstall()方法,詳見下面。
安裝
??接下來繼續(xù)查看installPackagesTracedLI(List requests)的實(shí)現(xiàn):
@GuardedBy("mInstallLock")private void installPackagesTracedLI(List<InstallRequest> requests) {try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");installPackagesLI(requests);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}
??主要是調(diào)用installPackagesLI(requests),繼續(xù)看它的實(shí)現(xiàn):
/*** 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("mInstallLock")private void installPackagesLI(List<InstallRequest> requests) {final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());final Map<String, PackageSetting> lastStaticSharedLibSettings =new ArrayMap<>(requests.size());final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());boolean success = false;try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");for (InstallRequest request : requests) {// TODO(b/109941548): remove this once we've pulled everything from it and into// scan, reconcile or commit.final PrepareResult prepareResult;try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");prepareResult =preparePackageLI(request.args, request.installResult);} catch (PrepareFailure prepareFailure) {request.installResult.setError(prepareFailure.error,prepareFailure.getMessage());request.installResult.origPackage = prepareFailure.conflictingPackage;request.installResult.origPermission = prepareFailure.conflictingPermission;return;} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);request.installResult.installerPackageName =request.args.installSource.installerPackageName;final String packageName = prepareResult.packageToScan.getPackageName();prepareResults.put(packageName, prepareResult);installResults.put(packageName, request.installResult);installArgs.put(packageName, request.args);try {final ScanResult result = scanPackageTracedLI(prepareResult.packageToScan, prepareResult.parseFlags,prepareResult.scanFlags, System.currentTimeMillis(),request.args.user, request.args.abiOverride);if (null != preparedScans.put(result.pkgSetting.pkg.getPackageName(), result)) {request.installResult.setError(PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,"Duplicate package " + result.pkgSetting.pkg.getPackageName()+ " in multi-package install request.");return;}createdAppId.put(packageName, optimisticallyRegisterAppId(result));versionInfos.put(result.pkgSetting.pkg.getPackageName(),getSettingsVersionForPackage(result.pkgSetting.pkg));if (result.staticSharedLibraryInfo != null) {final PackageSetting sharedLibLatestVersionSetting =getSharedLibLatestVersionSetting(result);if (sharedLibLatestVersionSetting != null) {lastStaticSharedLibSettings.put(result.pkgSetting.pkg.getPackageName(),sharedLibLatestVersionSetting);}}} catch (PackageManagerException e) {request.installResult.setError("Scanning Failed.", e);return;}}ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,installResults,prepareResults,mSharedLibraries,Collections.unmodifiableMap(mPackages), versionInfos,lastStaticSharedLibSettings);CommitRequest commitRequest = null;synchronized (mLock) {Map<String, ReconciledPackage> reconciledPackages;try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");reconciledPackages = reconcilePackagesLocked(reconcileRequest, mSettings.getKeySetManagerService(), mInjector);} catch (ReconcileFailure e) {for (InstallRequest request : requests) {request.installResult.setError("Reconciliation failed...", e);}return;} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");commitRequest = new CommitRequest(reconciledPackages,mUserManager.getUserIds());commitPackagesLocked(commitRequest);success = true;} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}executePostCommitSteps(commitRequest);} finally {if (success) {for (InstallRequest request : requests) {final InstallArgs args = request.args;if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {continue;}if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {continue;}// For incremental installs, we bypass the verifier prior to install. Now// that we know the package is valid, send a notice to the verifier with// the root hash of the base.apk.final String baseCodePath = request.installResult.pkg.getBaseApkPath();final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();final Uri originUri = Uri.fromFile(args.origin.resolvedFile);final int verificationId = mPendingVerificationToken++;final String rootHashString = PackageManagerServiceUtils.buildVerificationRootHashString(baseCodePath, splitCodePaths);broadcastPackageVerified(verificationId, originUri,PackageManager.VERIFICATION_ALLOW, rootHashString,args.mDataLoaderType, args.getUser());}} else {for (ScanResult result : preparedScans.values()) {if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),false)) {cleanUpAppIdCreation(result);}}// TODO(patb): create a more descriptive reason than unknown in future release// mark all non-failure installs as UNKNOWN so we do not treat them as successfor (InstallRequest request : requests) {if (request.installResult.freezer != null) {request.installResult.freezer.close();}if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;}}}Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}
??參數(shù)requests是List類型,是個集合,代表它可以安裝1個或者多個應(yīng)用,每個應(yīng)用安裝對應(yīng)一個InstallRequest。通過注釋可以看到,將安裝分成了4個階段:準(zhǔn)備、瀏覽、協(xié)調(diào)、提交。當(dāng)然翻譯不一定準(zhǔn)確,這里就這樣稱呼它們。
??開始時按照參數(shù)requests的數(shù)量生成了好幾個ArrayMap對象。prepareResults里的是準(zhǔn)備階段返回的結(jié)果,preparedScans里面是瀏覽階段返回的結(jié)果,installArgs則是安裝參數(shù),installResults是安裝結(jié)果,versionInfos里面放的是和存儲卷相關(guān)的版本信息。lastStaticSharedLibSettings存放的則是最新版本的靜態(tài)庫對應(yīng)的PackageSetting對象。createdAppId則是存放的對應(yīng)應(yīng)用的AppId是否創(chuàng)建。
??下面根據(jù)請求數(shù)組,進(jìn)行遍歷。
??preparePackageLI(request.args, request.installResult)是準(zhǔn)備階段的實(shí)現(xiàn),具體見 Android 安裝應(yīng)用-準(zhǔn)備階段。如果準(zhǔn)備階段拋出異常PrepareFailure,將失敗信息收集到request.installResult中。然后就直接整個返回,不再向下進(jìn)行。如果沒有拋出異常,將安裝成功PackageManager.INSTALL_SUCCEEDED設(shè)置到request.installResult的返回代碼中。然后收集準(zhǔn)備結(jié)果,安裝結(jié)果,安裝參數(shù),都是以包名為key。
??scanPackageTracedLI()則是瀏覽階段的實(shí)現(xiàn),具體見文章 Android 安裝應(yīng)用-瀏覽階段。然后將瀏覽結(jié)果對象result收集到集合preparedScans中,也是以包名作為key。繼續(xù)將appId,存儲卷的版本信息添加到createdAppId、versionInfos中。
??如果應(yīng)用是靜態(tài)庫,則得到最新版本的PackageSetting對象sharedLibLatestVersionSetting,如果它不為空,則將它放到lastStaticSharedLibSettings中。
??下面就是退出循環(huán)了。接著使用之前的結(jié)果,構(gòu)造ReconcileRequest對象reconcileRequest。
??下面reconcilePackagesLocked()就是協(xié)商階段的實(shí)現(xiàn),詳見 Android 應(yīng)用安裝-協(xié)調(diào)階段。
??繼續(xù)構(gòu)造CommitRequest對象commitRequest,調(diào)用commitPackagesLocked(commitRequest),它是提交階段的實(shí)現(xiàn),詳見文章 Android 應(yīng)用安裝-提交階段。并且將變量success = true,代表提交成功。
??executePostCommitSteps(commitRequest)則是提交之后的操作步驟,詳見 Android 安裝應(yīng)用-提交階段之后剩下的操作。
??繼續(xù)往下,如果success為true,則代表安裝成功。繼續(xù)遍歷安裝請求,如果是增量安裝,則跳出循環(huán)。如果不是增量安裝,發(fā)送一個廣播給驗(yàn)證者(注冊接收Intent.ACTION_PACKAGE_VERIFIED的廣播)。
??如果success為false,則遍歷瀏覽結(jié)果集合,如果在之前沒有創(chuàng)建成功AppId,則這里調(diào)用cleanUpAppIdCreation(result)清理Settings對象中維持的appId。
??在success為false的情況下,它接著遍歷安裝請求。將該應(yīng)用對應(yīng)的freezer關(guān)閉,其實(shí)就是將該包從PackageManagerService對象的成員mFrozenPackages中刪除。對于安裝結(jié)果為PackageManager.INSTALL_SUCCEEDED的,也將返回代碼設(shè)置為PackageManager.INSTALL_UNKNOWN。
安裝之后的處理
??看下handlePackagePostInstall()的實(shí)現(xiàn)代碼如下:
private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,boolean virtualPreload, boolean launchedForRestore, String installerPackage,IPackageInstallObserver2 installObserver, int dataLoaderType) {boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;final String packageName = res.name;final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;final boolean removedBeforeUpdate = (pkgSetting == null)|| (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(res.pkg.getPath()));if (succeeded && removedBeforeUpdate) {Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "+ "could be executed");res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED;res.returnMsg = "Package was removed before install could complete.";// Remove the update failed package's older resources safely nowInstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;if (args != null) {synchronized (mInstallLock) {args.doPostDeleteLI(true);}}notifyInstallObserver(res, installObserver);return;}if (succeeded) {// Clear the uid cache after we installed a new package.mPerUidReadTimeoutsCache = null;// Send the removed broadcastsif (res.removedInfo != null) {res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);}final String installerPackageName =res.installerPackageName != null? res.installerPackageName: res.removedInfo != null? res.removedInfo.installerPackageName: null;synchronized (mLock) {mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);}// Determine the set of users who are adding this package for// the first time vs. those who are seeing an update.int[] firstUserIds = EMPTY_INT_ARRAY;int[] firstInstantUserIds = EMPTY_INT_ARRAY;int[] updateUserIds = EMPTY_INT_ARRAY;int[] instantUserIds = EMPTY_INT_ARRAY;final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0;final PackageSetting ps = pkgSetting;for (int newUser : res.newUsers) {final boolean isInstantApp = ps.getInstantApp(newUser);if (allNewUsers) {if (isInstantApp) {firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);} else {firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);}continue;}boolean isNew = true;for (int origUser : res.origUsers) {if (origUser == newUser) {isNew = false;break;}}if (isNew) {if (isInstantApp) {firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);} else {firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);}} else {if (isInstantApp) {instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);} else {updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);}}}// Send installed broadcasts if the package is not a static shared lib.if (res.pkg.getStaticSharedLibName() == null) {mProcessLoggingHandler.invalidateBaseApkHash(res.pkg.getBaseApkPath());// Send added for users that see the package for the first time// sendPackageAddedForNewUsers also deals with system appsint appId = UserHandle.getAppId(res.uid);boolean isSystem = res.pkg.isSystem();sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,dataLoaderType);// Send added for users that don't see the package for the first timeBundle extras = new Bundle(1);extras.putInt(Intent.EXTRA_UID, res.uid);if (update) {extras.putBoolean(Intent.EXTRA_REPLACING, true);}extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);// Send to all running apps.final SparseArray<int[]> newBroadcastAllowList;synchronized (mLock) {newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(getPackageSettingInternal(res.name, Process.SYSTEM_UID),updateUserIds, mSettings.getPackagesLocked());}sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,null /*targetPackage*/, null /*finishedReceiver*/,updateUserIds, instantUserIds, newBroadcastAllowList, null);if (installerPackageName != null) {// Send to the installer, even if it's not running.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,installerPackageName, null /*finishedReceiver*/,updateUserIds, instantUserIds, null /* broadcastAllowList */, null);}// if the required verifier is defined, but, is not the installer of record// for the package, it gets notifiedfinal boolean notifyVerifier = mRequiredVerifierPackage != null&& !mRequiredVerifierPackage.equals(installerPackageName);if (notifyVerifier) {sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,mRequiredVerifierPackage, null /*finishedReceiver*/,updateUserIds, instantUserIds, null /* broadcastAllowList */, null);}// If package installer is defined, notify package installer about new// app installedif (mRequiredInstallerPackage != null) {sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,mRequiredInstallerPackage, null /*finishedReceiver*/,firstUserIds, instantUserIds, null /* broadcastAllowList */, null);}// Send replaced for users that don't see the package for the first timeif (update) {sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,packageName, extras, 0 /*flags*/,null /*targetPackage*/, null /*finishedReceiver*/,updateUserIds, instantUserIds, res.removedInfo.broadcastAllowList,null);if (installerPackageName != null) {sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,extras, 0 /*flags*/,installerPackageName, null /*finishedReceiver*/,updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);}if (notifyVerifier) {sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,extras, 0 /*flags*/,mRequiredVerifierPackage, null /*finishedReceiver*/,updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);}sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,null /*package*/, null /*extras*/, 0 /*flags*/,packageName /*targetPackage*/,null /*finishedReceiver*/, updateUserIds, instantUserIds,null /*broadcastAllowList*/,getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());} else if (launchedForRestore && !res.pkg.isSystem()) {// First-install and we did a restore, so we're responsible for the// first-launch broadcast.if (DEBUG_BACKUP) {Slog.i(TAG, "Post-restore of " + packageName+ " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));}sendFirstLaunchBroadcast(packageName, installerPackage,firstUserIds, firstInstantUserIds);}// Send broadcast package appeared if external for all usersif (res.pkg.isExternalStorage()) {if (!update) {final StorageManager storage = mInjector.getSystemService(StorageManager.class);VolumeInfo volume =storage.findVolumeByUuid(res.pkg.getStorageUuid().toString());int packageExternalStorageType =getPackageExternalStorageType(volume, res.pkg.isExternalStorage());// If the package was installed externally, log it.if (packageExternalStorageType != StorageEnums.UNKNOWN) {FrameworkStatsLog.write(FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,packageExternalStorageType, packageName);}}if (DEBUG_INSTALL) {Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");}final int[] uidArray = new int[]{res.pkg.getUid()};ArrayList<String> pkgList = new ArrayList<>(1);pkgList.add(packageName);sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);}} else if (!ArrayUtils.isEmpty(res.libraryConsumers)) { // if static shared libint[] allUsers = mInjector.getUserManagerService().getUserIds();for (int i = 0; i < res.libraryConsumers.size(); i++) {AndroidPackage pkg = res.libraryConsumers.get(i);// send broadcast that all consumers of the static shared library have changedsendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,new ArrayList<>(Collections.singletonList(pkg.getPackageName())),pkg.getUid(), null);}}// Work that needs to happen on first install within each userif (firstUserIds != null && firstUserIds.length > 0) {for (int userId : firstUserIds) {restorePermissionsAndUpdateRolesForNewUserInstall(packageName,pkgSetting.getInstallReason(userId), userId);}}if (allNewUsers && !update) {notifyPackageAdded(packageName, res.uid);} else {notifyPackageChanged(packageName, res.uid);}// Log current value of "unknown sources" settingEventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,getUnknownSourcesSettings());// Remove the replaced package's older resources safely nowInstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;if (args != null) {if (!killApp) {// If we didn't kill the app, defer the deletion of code/resource files, since// they may still be in use by the running application. This mitigates problems// in cases where resources or code is loaded by a new Activity before// ApplicationInfo changes have propagated to all application threads.scheduleDeferredNoKillPostDelete(args);} else {synchronized (mInstallLock) {args.doPostDeleteLI(true);}}} else {// Force a gc to clear up things. Ask for a background one, it's fine to go on// and not block here.VMRuntime.getRuntime().requestConcurrentGC();}// Notify DexManager that the package was installed for new users.// The updated users should already be indexed and the package code paths// should not change.// Don't notify the manager for ephemeral apps as they are not expected to// survive long enough to benefit of background optimizations.for (int userId : firstUserIds) {PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);// There's a race currently where some install events may interleave with an// uninstall. This can lead to package info being null (b/36642664).if (info != null) {mDexManager.notifyPackageInstalled(info, userId);}}}final boolean deferInstallObserver = succeeded && update && !killApp;if (deferInstallObserver) {scheduleDeferredNoKillInstallObserver(res, installObserver);} else {notifyInstallObserver(res, installObserver);}}
??succeeded為true代表安裝成功,update為true代表是更新升級。removedBeforeUpdate代表更新失敗或者是系統(tǒng)應(yīng)用包更新,但是PackageSetting對象的文件路徑和文件安裝路徑不一致,代表更新之前需要刪除。
??如果succeeded為true并且removedBeforeUpdate也為true的情況,會將res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED,將安裝成功的返回碼改成安裝失敗。并且如果res.removedInfo.args不為null的情況下,會執(zhí)行刪除舊包路徑及其包下面內(nèi)容。接著調(diào)用notifyInstallObserver(res, installObserver)執(zhí)行回調(diào)。
??接下來succeeded如果為true,代表著安裝成功的情況。在res.removedInfo不為null的情況下,將調(diào)用它的sendPackageRemovedBroadcasts(killApp, false)發(fā)送包被刪除的廣播。在前面 Android 安裝應(yīng)用-準(zhǔn)備階段 中,會在更新安裝的情況下,初始化res.removedInfo,更新安裝涉及到舊包的刪除,這里會發(fā)送包被刪除的廣播。res.removedInfo是PackageRemovedInfo對象,所以會調(diào)用它的sendPackageRemovedBroadcasts(killApp, false)方法,它會向能看到它的普通應(yīng)用發(fā)送Intent.ACTION_PACKAGE_REMOVED廣播,向安裝該應(yīng)用的應(yīng)用發(fā)送Intent.ACTION_PACKAGE_REMOVED廣播。向平臺包應(yīng)用(包名為"android")發(fā)送Intent.ACTION_PACKAGE_REMOVED_INTERNAL廣播。如果數(shù)據(jù)都被刪除,并且不是系統(tǒng)應(yīng)用包更新的情況,它會向能看到它的普通應(yīng)用發(fā)送Intent.ACTION_PACKAGE_FULLY_REMOVED廣播。它還會向能看到它的普通應(yīng)用發(fā)送Intent.ACTION_UID_REMOVED廣播。
??下面的四個數(shù)組變量firstUserIds、firstInstantUserIds、updateUserIds、instantUserIds是和用戶有關(guān),前面兩個是首次添加該包的用戶(分普通應(yīng)用安裝和Instant安裝),后面兩個是之前就添加過該包的用戶,現(xiàn)在更新安裝依然存在的用戶。res.origUsers就是之前添加過該包的用戶,res.newUsers則是目前更新添加該包的用戶。這塊處理的邏輯就是res.origUsers不存在,res.newUsers存在的就放入firstUserIds或firstInstantUserIds中;res.origUsers存在,res.newUsers存在的就放入updateUserIds或instantUserIds中。
??res.pkg.getStaticSharedLibName() == null代表安裝應(yīng)用不是靜態(tài)分享包,在該種情況下,sendPackageAddedForNewUsers()方法會向firstUserIds或firstInstantUserIds對應(yīng)的能看到的普通應(yīng)用發(fā)送Intent.ACTION_PACKAGE_ADDED廣播。對于這些首次添加該包的用戶,如果應(yīng)用是系統(tǒng)應(yīng)用或者虛擬預(yù)加載的,如果這些用戶是運(yùn)行狀態(tài),它會該用戶的應(yīng)用發(fā)送Intent.ACTION_LOCKED_BOOT_COMPLETED廣播,如果該用戶沒有被鎖定,它還會發(fā)送Intent.ACTION_BOOT_COMPLETED廣播。
??下面還會接著處理應(yīng)用不是靜態(tài)分享包時,還會向updateUserIds或instantUserIds中的普通應(yīng)用發(fā)送Intent.ACTION_PACKAGE_ADDED廣播。如果安裝者應(yīng)用不為null,向它發(fā)送Intent.ACTION_PACKAGE_ADDED廣播。如果mRequiredVerifierPackage不為null,并且它不為安裝者應(yīng)用,則它向mRequiredVerifierPackage發(fā)送Intent.ACTION_PACKAGE_ADDED廣播。如果包安裝者定義了(mRequiredInstallerPackage != null),會向它發(fā)送Intent.ACTION_PACKAGE_ADDED廣播。
??如果是在更新安裝的情況下,它會向updateUserIds或instantUserIds應(yīng)用中的普通應(yīng)用發(fā)送Intent.ACTION_PACKAGE_REPLACED廣播,如果安裝者應(yīng)用不為null,向它發(fā)送Intent.ACTION_PACKAGE_REPLACED廣播,如果mRequiredVerifierPackage不為null,并且它不為安裝者應(yīng)用,則它向mRequiredVerifierPackage發(fā)送Intent.ACTION_PACKAGE_REPLACED廣播。它還會向自己發(fā)送Intent.ACTION_MY_PACKAGE_REPLACED廣播。
??如果不是更新安裝,是恢復(fù)數(shù)據(jù)的普通應(yīng)用,它將向第一次添加該包的用戶的安裝它的應(yīng)用包發(fā)送Intent.ACTION_PACKAGE_FIRST_LAUNCH廣播。
??接下來如果包是在外部存儲空間中,它將發(fā)送Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE廣播,將該應(yīng)用發(fā)生了變化進(jìn)行通知。
??下面進(jìn)入else if (!ArrayUtils.isEmpty(res.libraryConsumers))分支,意味著它是一個庫,被別的應(yīng)用引用,res.libraryConsumers就是引用它的應(yīng)用。它會發(fā)送Intent.ACTION_PACKAGE_CHANGED廣播,通知其他能看到引用庫的應(yīng)用的其他普通應(yīng)用發(fā)生了變化。
??在安裝的過程中,如果有新增的用戶,會執(zhí)行恢復(fù)新增用戶的運(yùn)行時權(quán)限,更新對應(yīng)用戶的更喜歡的Activity。
??在allNewUsers為true,update為false的情況下,代表第一次安裝,會通過notifyPackageAdded(packageName, res.uid),調(diào)用成員變量mPackageListObservers中的onPackageAdded接口。其他情況下,會調(diào)用成員變量mPackageListObservers中的onPackageChanged接口。
??如果res.removedInfo.args不為null,會調(diào)用它的doPostDeleteLI(true)執(zhí)行刪除資源和代碼。不過如果設(shè)置了不殺死應(yīng)用的情況,會調(diào)用scheduleDeferredNoKillPostDelete(args)延遲刪除。
??對于新增的用戶,調(diào)用mDexManager.notifyPackageInstalled(info, userId)通知包安裝了。
??最后就是處理返回結(jié)果。不過也分是否殺死應(yīng)用兩種情況,如果是不殺死應(yīng)用的情況,也是延遲消息處理。最后都是通過調(diào)用notifyInstallObserver(res, installObserver)來處理。
將處理結(jié)果返回給應(yīng)用
??notifyInstallObserver(res, installObserver)的代碼如下:
private void notifyInstallObserver(PackageInstalledInfo info,IPackageInstallObserver2 installObserver) {if (installObserver != null) {try {Bundle extras = extrasForInstallResult(info);installObserver.onPackageInstalled(info.name, info.returnCode,info.returnMsg, extras);} catch (RemoteException e) {Slog.i(TAG, "Observer no longer exists.");}}}
??可見是通過參數(shù)IPackageInstallObserver2 類型的onPackageInstalled接口返回。這個回調(diào)接口是來自安裝參數(shù)FileInstallArgs類對象的成員變量observer。它又來自InstallParams類對象的observer。InstallParams類對象的創(chuàng)建來自PackageInstallerSession的makeInstallParams()。
@Nullableprivate PackageManagerService.InstallParams makeInstallParams()throws PackageManagerException {……final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {@Overridepublic void onUserActionRequired(Intent intent) {throw new IllegalStateException();}@Overridepublic void onPackageInstalled(String basePackageName, int returnCode, String msg,Bundle extras) {if (isStaged()) {sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);} else {// We've reached point of no return; call into PMS to install the stage.// Regardless of success or failure we always destroy session.destroyInternal();dispatchSessionFinished(returnCode, msg, extras);}}};……synchronized (mLock) {return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,mSigningDetails, mInstallerUid, mPackageLite);}}
??IPackageInstallObserver2 對象是通過構(gòu)造InstallParams對象時作為參數(shù)傳遞過去的。
??isStaged()代表這個安裝是需要重啟才執(zhí)行安裝。普通應(yīng)用的安裝是走else分支。
??destroyInternal()是用來銷毀session,因?yàn)槟壳耙呀?jīng)安裝完畢,有結(jié)果了。
??dispatchSessionFinished(returnCode, msg, extras)用來派發(fā)結(jié)果。看一下它的代碼:
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);synchronized (mLock) {mFinalStatus = returnCode;mFinalMessage = msg;}final boolean success = (returnCode == INSTALL_SUCCEEDED);// Send broadcast to default launcher only if it's a new install// TODO(b/144270665): Secure the usage of this broadcast.final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId);}mCallback.onSessionFinished(this, success);if (isDataLoaderInstallation()) {logDataLoaderInstallationSession(returnCode);}}
??sendUpdateToRemoteStatusReceiver(returnCode, msg, extras)是向應(yīng)用端發(fā)送廣播安裝結(jié)果。
??給mFinalStatus、mFinalMessage賦值,存放結(jié)果。
??如果是新安裝,就是不是更新安裝,并且安裝成功的情況下,mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /icon/), userId)會發(fā)送對應(yīng)的廣播。
??mCallback.onSessionFinished(this, success)主要是Session已經(jīng)完成了,所以需要將它去除。
??咱們這里主要看看sendUpdateToRemoteStatusReceiver(returnCode, msg, extras),代碼如下:
private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {final IntentSender statusReceiver;final String packageName;synchronized (mLock) {statusReceiver = mRemoteStatusReceiver;packageName = mPackageName;}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 = packageName;args.arg2 = msg;args.arg3 = extras;args.arg4 = statusReceiver;args.argi1 = returnCode;mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();}}
??這里的mRemoteStatusReceiver就是在 Android 安裝過程一 界面跳轉(zhuǎn) 中InstallInstalling Activity中session.commit(pendingIntent.getIntentSender())里的pendingIntent.getIntentSender()。pendingIntent是待發(fā)送的BROADCAST_ACTION廣播(詳見Android 安裝過程一 界面跳轉(zhuǎn)),它的接收是在安裝進(jìn)程中的InstallEventReceiver中,它是配置在Manifest文件中。
??在這里是通過消息MSG_ON_PACKAGE_INSTALLED的處理,它的處理是在sendOnPackageInstalled()中,看一下:
private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,boolean showNotification, int userId, String basePackageName, int returnCode,String msg, Bundle extras) {if (INSTALL_SUCCEEDED == returnCode && showNotification) {boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);Notification notification = PackageInstallerService.buildSuccessNotification(context,context.getResources().getString(update ? R.string.package_updated_device_owner :R.string.package_installed_device_owner),basePackageName,userId);if (notification != null) {NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);notificationManager.notify(basePackageName,SystemMessageProto.SystemMessage.NOTE_PACKAGE_STATE,notification);}}final Intent fillIn = new Intent();fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);fillIn.putExtra(PackageInstaller.EXTRA_STATUS,PackageManager.installStatusToPublicStatus(returnCode));fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,PackageManager.installStatusToString(returnCode, msg));fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);if (extras != null) {final String existing = extras.getString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);if (!TextUtils.isEmpty(existing)) {fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);}}try {target.sendIntent(context, 0, fillIn, null, null);} catch (IntentSender.SendIntentException ignored) {}}
??這里就是發(fā)送了BROADCAST_ACTION廣播,咱們再看看安裝進(jìn)程中的接收者的處理。
??安裝進(jìn)程中的接收者的處理是在EventResultPersister中的onEventReceived()中,代碼如下:
void onEventReceived(@NonNull Context context, @NonNull Intent intent) {int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));return;}int id = intent.getIntExtra(EXTRA_ID, 0);String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);EventResultObserver observerToCall = null;synchronized (mLock) {int numObservers = mObservers.size();for (int i = 0; i < numObservers; i++) {if (mObservers.keyAt(i) == id) {observerToCall = mObservers.valueAt(i);mObservers.removeAt(i);break;}}if (observerToCall != null) {observerToCall.onResult(status, legacyStatus, statusMessage);} else {mResults.put(id, new EventResult(status, legacyStatus, statusMessage));writeState();}}}
??注冊的回調(diào)在成員變量mObservers,它是SparseArray類型。接收到廣播之后,通過參數(shù)id來找到對應(yīng)的回調(diào)函數(shù),就可以回調(diào)對應(yīng)的函數(shù)了。
??再看一下它的注冊回調(diào)也是在InstallInstalling Activity中,如下:
mInstallId = InstallEventReceiver.addObserver(this, EventResultPersister.GENERATE_NEW_ID,this::launchFinishBasedOnResult)
??所以最后會調(diào)用launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage)方法,再顯示對應(yīng)的界面。(詳見Android 安裝過程一 界面跳轉(zhuǎn))。
總結(jié)
??安裝這個過程還是挺復(fù)雜的,最主要的是它將之分成4個階段:準(zhǔn)備、瀏覽、協(xié)調(diào)、提交。每個步驟中都有好多代碼,并且代碼中處理不是只針對普通應(yīng)用的安裝,它是包括其他形式安裝的處理,該文章主要是有關(guān)普通應(yīng)用的安裝,還是結(jié)合了前面文章的例子,將所有的流程過了一遍,其中的相關(guān)細(xì)節(jié),還沒有深究,留待后續(xù)繼續(xù)琢磨。