這是JsonChao的第153期分享
閱讀本文前最好閱讀Android PMS處理APK的複製這篇文章,因為它和本篇文章本來是一篇文章,由於公號文章的字數限制,被拆分為了兩篇文章,這一篇我們接着來學習PMS處理APK的安裝。
照例先來查看安裝APK的時序圖。

handleReturnCode方法中只調用了processPendingInstall方法,注釋1處用於檢查APK的狀態的,在安裝前確保安裝環境的可靠,如果不可靠會清除複製的APK文件,注釋3處用於處理安裝後的收尾操作,如果安裝不成功,刪除掉安裝相關的目錄與文件。主要來看注釋2處的installPackageTracedLI方法,其內部會調用PMS的installPackageLI方法。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
privatevoidinstallPackageLI(InstallArgsargs,PackageInstalledInfores){...PackageParserpp=newPackageParser();pp.setSeparateProcesses(mSeparateProcesses);pp.setDisplayMetrics(mMetrics);pp.setCallback(mPackageParserCallback);Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,"parsePackage");finalPackageParser.Packagepkg;try{//解析APKpkg=pp.parsePackage(tmpPackageFile,parseFlags);//1}catch(PackageParserExceptione){res.setError("FailedparseduringinstallPackageLI",e);return;}finally{Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}...pp=null;StringoldCodePath=null;booleansystemApp=false;synchronized(mPackages){//檢查APK是否存在if((installFlags&PackageManager.INSTALL_REPLACE_EXISTING)!=0){StringoldName=mSettings.getRenamedPackageLPr(pkgName);//獲取沒被改名前的包名if(pkg.mOriginalPackages!=null&&pkg.mOriginalPackages.contains(oldName)&&mPackages.containsKey(oldName)){pkg.setPackageName(oldName);//2pkgName=pkg.packageName;replace=true;//設置標誌位表示是替換安裝if(DEBUG_INSTALL)Slog.d(TAG,"Replacingexistingrenamedpackage:oldName="+oldName+"pkgName="+pkgName);}...}PackageSettingps=mSettings.mPackages.get(pkgName);//查看Settings中是否存有要安裝的APK的信息,如果有就獲取簽名信息if(ps!=null){//3if(DEBUG_INSTALL)Slog.d(TAG,"Existingpackage:"+ps);PackageSettingsignatureCheckPs=ps;if(pkg.applicationInfo.isStaticSharedLibrary()){SharedLibraryEntrylibraryEntry=getLatestSharedLibraVersionLPr(pkg);if(libraryEntry!=null){signatureCheckPs=mSettings.getPackageLPr(libraryEntry.apk);}}//檢查簽名的正確性if(shouldCheckUpgradeKeySetLP(signatureCheckPs,scanFlags)){if(!checkUpgradeKeySetLP(signatureCheckPs,pkg)){res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"Package"+pkg.packageName+"upgradekeysdonotmatchthe"+"previouslyinstalledversion");return;}}...}intN=pkg.permissions.size();for(inti=N-1;i>=0;i--){//遍歷每個權限,對權限進行處理PackageParser.Permissionperm=pkg.permissions.get(i);BasePermissionbp=mSettings.mPermissions.get(perm.info.name);}}}if(systemApp){if(onExternal){//系統APP不能在SD卡上替換安裝res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,"Cannotinstallupdatestosystemappsonsdcard");return;}elseif(instantApp){//系統APP不能被InstantApp替換res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,"Cannotupdateasystemappwithaninstantapp");return;}}...//重命名臨時文件if(!args.doRename(res.returnCode,pkg,oldCodePath)){//4res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE,"Failedrename");return;}startIntentFilterVerifications(args.user.getIdentifier(),replace,pkg);try(PackageFreezerfreezer=freezePackageForInstall(pkgName,installFlags,"installPackageLI")){if(replace){//5//替換安裝...replacePackageLIF(pkg,parseFlags,scanFlags|SCAN_REPLACING,args.user,installerPackageName,res,args.installReason);}else{//安裝新的APKinstallNewPackageLIF(pkg,parseFlags,scanFlags|SCAN_DELETE_DATA_ON_FAILURES,args.user,installerPackageName,volumeUuid,res,args.installReason);}}synchronized(mPackages){finalPackageSettingps=mSettings.mPackages.get(pkgName);if(ps!=null){//更新應用程序所屬的用戶res.newUsers=ps.queryInstalledUsers(sUserManager.getUserIds(),true);ps.setUpdateAvailable(false/*updateAvailable*/);}...}}installPackageLI方法的代碼有將近500行,這裡截取主要的部分,主要做了幾件事:
創建PackageParser解析APK。
檢查APK是否存在,如果存在就獲取此前沒被改名前的包名並在注釋1處賦值給PackageParser.Package類型的pkg,在注釋3處將標誌位replace置為true表示是替換安裝。
注釋3處,如果Settings中保存有要安裝的APK的信息,說明此前安裝過該APK,則需要校驗APK的簽名信息,確保安全的進行替換。
在注釋4處將臨時文件重新命名,比如前面提到的/data/app/vmdl18300388.tmp/base.apk,重命名為/data/app/包名-1/base.apk。這個新命名的包名會帶上一個數字後綴1,每次升級一個已有的App,這個數字會不斷的累加。
系統APP的更新安裝會有兩個限制,一個是系統APP不能在SD卡上替換安裝,另一個是系統APP不能被Instant App替換。
注釋5處根據replace來做區分,如果是替換安裝就會調用replacePackageLIF方法,其方法內部還會對系統APP和非系統APP進行區分處理,如果是新安裝APK會調用installNewPackageLIF方法。
這裡我們以新安裝APK為例,會調用PMS的installNewPackageLIF方法。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
privatevoidinstallNewPackageLIF(PackageParser.Packagepkg,finalintpolicyFlags,intscanFlags,UserHandleuser,StringinstallerPackageName,StringvolumeUuid,PackageInstalledInfores,intinstallReason){...try{//掃描APKPackageParser.PackagenewPackage=scanPackageTracedLI(pkg,policyFlags,scanFlags,System.currentTimeMillis(),user);//更新Settings信息updateSettingsLI(newPackage,installerPackageName,null,res,user,installReason);if(res.returnCode==PackageManager.INSTALL_SUCCEEDED){//安裝成功後,為新安裝的應用程序準備數據prepareAppDataAfterInstallLIF(newPackage);}else{//安裝失敗則刪除APKdeletePackageLIF(pkgName,UserHandle.ALL,false,null,PackageManager.DELETE_KEEP_DATA,res.removedInfo,true,null);}}catch(PackageManagerExceptione){res.setError("Packagecouldn'tbeinstalledin"+pkg.codePath,e);}Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}installNewPackageLIF主要做了以下3件事:
掃描APK,將APK的信息存儲在PackageParser.Package類型的newPackage中,一個Package的信息包含了1個base APK以及0個或者多個split APK。
更新該APK對應的Settings信息,Settings用於保存所有包的動態設置。
如果安裝成功就為新安裝的應用程序準備數據,安裝失敗就刪除APK。
安裝APK的過程就講到這裡,就不再往下分析下去,有興趣的同學可以接着深挖。
本文和上一篇文章Android PMS處理APK的複製主要講解了PMS是如何處理APK複製和安裝的,主要有幾個步驟:
1. PackageInstaller安裝APK時會將APK的信息交由PMS處理,PMS通過向PackageHandler發送消息來驅動APK的複製和安裝工作。
2. PMS發送INIT_COPY和MCS_BOUND類型的消息,控制PackageHandler來綁定DefaultContainerService,完成複製APK等工作。
3. 複製APK完成後,會開始進行安裝APK的流程,包括安裝前的檢查、安裝APK和安裝後的收尾工作。
END
出身普通的人,如何真正改變命運?
這是我過去五、六年一直研究的命題。首先,是為自己研究,因為我是從小城鎮出來的,通過持續不斷地逆襲立足深圳。越是出身普通的人,就越需要有耐心,去進行系統性地全面提升,這方面,我有非常豐富的實踐經驗和方法論。因此,我開啟了 「JsonChao」 的成長社群,希望和你一起完成系統性地蛻變。
星球目前有哪些服務?365 元每年,每天一元,給自己的成長持續加油💪。
為了回饋 JsonChao 公眾號的老用戶,我申請了200 張優惠券,8.3號過期,先到者先得,錯過再無。