Android 開機(jī)應(yīng)用掃描相關(guān)總結(jié)
本文的內(nèi)容
- PkMS是怎么知道apk的位置
- 系統(tǒng)應(yīng)用和普通應(yīng)用的區(qū)別
- 應(yīng)用掃描的過(guò)程以及應(yīng)用信息的保存
PkMS怎么知道apk的位置
答案是按照路徑,對(duì)于手機(jī)用戶安裝應(yīng)用都是放在/data/app,對(duì)于系統(tǒng)應(yīng)用則是分布各個(gè)分區(qū)中,可以簡(jiǎn)單的認(rèn)為是目錄,/data就是data分區(qū),以下的分區(qū)都會(huì)被掃描,并且只會(huì)掃描priv-app和app目錄:
- system分區(qū)下的priv-app,app目錄/system/priv-app, /system/app
- product分區(qū)
- odm分區(qū)
- oem分區(qū)
- vendor分區(qū)
- system_ext分區(qū)
- data分區(qū),最后掃描
不過(guò)這些分區(qū)都執(zhí)行不是特別嚴(yán)格,沒(méi)涉及到gms測(cè)試的基本都不怎么管,那么apk是存在什么地方呢?例如:Settings就放在/system_ext/priv-app/Settings/Settings.apk,其他分區(qū)都是類似的,下面分析應(yīng)用掃描過(guò)程還會(huì)說(shuō)到。這里解釋一下,為什么要分priv-app和app兩個(gè)目錄進(jìn)行掃描? priv-app意思是這個(gè)目錄里面都是privileged應(yīng)用,可以獲取privilege權(quán)限,權(quán)限的定義frameworks/base/core/res/AndroidManifest.xml中找到
系統(tǒng)應(yīng)用和普通應(yīng)用的區(qū)別
答案還是路徑,data分區(qū)內(nèi)的應(yīng)用是普通應(yīng)用,其他分區(qū)安裝的應(yīng)用都是系統(tǒng)應(yīng)用,系統(tǒng)應(yīng)用均不可卸載
應(yīng)用掃描過(guò)程
scanDirLI()
scanDirLI的幾個(gè)參數(shù)說(shuō)明一下,
- scanDir,應(yīng)用目錄的集合,以上面Settings為例,目錄是/system_ext/priv-app
- scanFlags,掃描的標(biāo)志,當(dāng)為系統(tǒng)應(yīng)用時(shí),會(huì)加上SCAN_AS_SYSTEM標(biāo)志,如果是priv-app目錄還要加上SCAN_AS_PRIVILGED
- packageParser,這個(gè)就是解析AndroidManifest.xml主要工具類
- executorService,生產(chǎn)者線程
scanDirLI的邏輯比較簡(jiǎn)單,
- 以上面/system_ext/priv-app為例,遍歷目錄下的每個(gè)目錄和apk
- 使用ExecutorService(線程池)對(duì)所有目錄和apk進(jìn)行解析,詳細(xì)看PkMS解析package指南
- 獲取ParsedPackage并進(jìn)行調(diào)用addForInitLI()
addForInitLI()
邏輯相當(dāng)?shù)膹?fù)雜,考慮情況也要很多,有些自己也沒(méi)搞懂,只挑能說(shuō)的說(shuō)一下
1.首先重命名包名的邏輯,在PkMS當(dāng)中所有的package都是按照包名作為唯一的主鍵值,那么就可能出現(xiàn)應(yīng)用需要更換包名的情況,PkMS給出解決方案是加上<original-package android:name="com.android.oldpackagename" />指明需要覆蓋安裝的包名,但是這樣就需要考慮到諸多情況了
- 包名修改多次怎么辦
- 之前有安裝過(guò)老包名的應(yīng)用的處理情況
- 之前未安裝過(guò)老包名的應(yīng)用的處理情況
- 之前安裝過(guò)當(dāng)前包名的應(yīng)用的處理情況
那么PkMS是怎么做的呢?
- 設(shè)計(jì)了一個(gè)originalPackages表示之前原始package,當(dāng)遇到original-package時(shí),就將包名加入到originalPackages當(dāng)中,代碼如下
//ParsingPackageUtils.parseOriginalPackage()
String orig = sa.getNonConfigurationString(
R.styleable.AndroidManifestOriginalPackage_name,
0);
if (!pkg.getPackageName().equals(orig)) {
if (pkg.getOriginalPackages().isEmpty()) {
pkg.setRealPackage(pkg.getPackageName());
}
pkg.addOriginalPackage(orig);
}
- 然后在addForInitLI()中根據(jù)realPkgName獲取originalPkgSetting
- 接下來(lái)判斷是否要覆蓋安裝應(yīng)用
不過(guò)我感覺(jué)里面還是有些bug,似乎只是為了用gms應(yīng)用替換AOSP里面的應(yīng)用(如:PackageInstaller和PermissionController)專門設(shè)計(jì)的,并不具備普遍性,安裝的應(yīng)用及時(shí)加了標(biāo)簽也還是會(huì)被認(rèn)為是兩個(gè)應(yīng)用
2.對(duì)于覆蓋安裝系統(tǒng)應(yīng)用的data區(qū)應(yīng)用進(jìn)行處理,什么是覆蓋安裝系統(tǒng)應(yīng)用?即系統(tǒng)應(yīng)用有更新,可以通過(guò)應(yīng)用商店下載覆蓋安裝,但是并不會(huì)去卸載系統(tǒng)應(yīng)用,而是disable系統(tǒng)應(yīng)用,例如:Google的gms應(yīng)用都是如此
- 如果安裝的應(yīng)用出現(xiàn)問(wèn)題了,重新enable被disabled應(yīng)用
- 如果是系統(tǒng)更新的應(yīng)用,則需要掃描disabled系統(tǒng)應(yīng)用,因?yàn)榭赡軙?huì)有OTA升級(jí)導(dǎo)致系統(tǒng)應(yīng)用更新apk的情況,調(diào)用scanPackageOnlyLI(),進(jìn)行掃描,后面還會(huì)分析
- 接下來(lái)是根據(jù)disabled系統(tǒng)應(yīng)用和安裝應(yīng)用的版本號(hào),判斷是否需要重新使用系統(tǒng)應(yīng)用
3.處理簽名,對(duì)于安裝的應(yīng)用,必須要簽名
- 確認(rèn)是否需要重新簽名,對(duì)于非系統(tǒng)應(yīng)用來(lái)說(shuō),無(wú)需重新收集,這部分AOSP壓根沒(méi)寫,注釋里面的第二條還沒(méi)實(shí)現(xiàn)
// Verify certificates against what was last scanned. Force re-collecting certificate in two
// special cases:
// 1) when scanning system, force re-collect only if system is upgrading.
// 2) when scannning /data, force re-collect only if the app is privileged (updated from
// preinstall, or treated as privileged, e.g. due to shared user ID).
final boolean forceCollect = scanSystemPartition ? mIsUpgrade
: PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
- 是否跳過(guò)簽名驗(yàn)證,這個(gè)是覆蓋安裝時(shí)候使用
// Full APK verification can be skipped during certificate collection, only if the file is
// in verified partition, or can be verified on access (when apk verity is enabled). In both
// cases, only data in Signing Block is verified instead of the whole file.
// TODO(b/136132412): skip for Incremental installation
final boolean skipVerify = scanSystemPartition
|| (forceCollect && canSkipForcedPackageVerification(parsedPackage));
collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
4.還要處理一下第三步的反方向,即一開始有個(gè)安裝的應(yīng)用(data分區(qū)),而后OTA升級(jí)有添加了該系統(tǒng)應(yīng)用(system或其他分區(qū)),分為三種情況進(jìn)行討論
- 簽名不相同,則卸載data分區(qū)的應(yīng)用
- 如果系統(tǒng)應(yīng)用的版本號(hào)更高,則移除data分區(qū)的應(yīng)用,但保留應(yīng)用的數(shù)據(jù)
- 如果系統(tǒng)應(yīng)用的版本號(hào)更低,則將shouldHideSystemApp設(shè)置為true,也就是將在下面disable系統(tǒng)應(yīng)用
5.調(diào)用scanPackageNewLI()繼續(xù)掃描
6.調(diào)用reconcilePackagesLocked()進(jìn)行一致化處理
7.調(diào)用commitReconciledScanResultLocked()
scanPackageNewLI()
需要注意的是這個(gè)方法不僅僅是開機(jī)掃描的時(shí)候用,在安裝應(yīng)用的時(shí)候也是執(zhí)行的
- 再次處理重命名包名,這個(gè)應(yīng)該和安裝有關(guān)系,我測(cè)試的時(shí)候是覆蓋安裝系統(tǒng)應(yīng)用有效,普通應(yīng)用無(wú)法生效,會(huì)被認(rèn)為是兩個(gè)應(yīng)用
- 調(diào)用adjustFlags()重新調(diào)整scanFlags,分為以下幾種情況
- 重命名包名應(yīng)用或者覆蓋安裝的應(yīng)用是系統(tǒng)應(yīng)用,scanFlags添加上SCAN_AS_SYSTEM以及可能的其他flags
- 如果sharedUserId是包含了privilege應(yīng)用,則正在掃描的應(yīng)用也要加上SCAN_AS_PRIVILEGED
- 獲取SharedUserSetting,這個(gè)在addForInitLI()獲取過(guò)一次,是為了掃描disabledPkgSetting獲取的
- 構(gòu)建ScanRequest并且調(diào)用scanPackageOnlyLI()獲取ScanResult
scanPackageOnlyLI()
這部分主要的邏輯是更新packageSetting和nativelibrary的解析
- 確定是否需要獲取abi(needToDeriveAbi),如果packageSetting,如果應(yīng)用已安裝,則無(wú)需更改cpuAbi,如果未安裝,則需要解析native庫(kù)
- 獲取<uses-static-library>標(biāo)簽,這個(gè)我不知道是干嘛的,目前還沒(méi)遇到這種標(biāo)簽
- 更新或創(chuàng)建新的PackageSetting,創(chuàng)建的情況有兩種,首次安裝應(yīng)用或者是恢復(fù)出廠開機(jī)
- 根據(jù)第一步獲取的needToDeriveAbi獲取abi的類型并設(shè)置到PackageSetting中,另外就是要解析apk中的native so庫(kù),并獲取對(duì)應(yīng)so庫(kù)的路徑,這里有個(gè)SCAN_NEW_INSTALLflag,這個(gè)是給安裝用的,開機(jī)scan是不會(huì)有此問(wèn)題
- 設(shè)置安裝的時(shí)間戳,主要是第一次安裝和當(dāng)前安裝,目前不知道這個(gè)屬性的用處
- 創(chuàng)建ScanResult并返回
reconcilePackagesLocked()
先說(shuō)一下幾個(gè)參數(shù):
- ReconcileRequest,放置需要的參數(shù),allPackages->mPackages, scannedPackages->scanPackageOnlyLI()返回的ScanResult,sharedLibrarySource->mSharedLibraries解析之后才會(huì)進(jìn)行push
- KeySetManagerService簽名管理服務(wù)
reconcilePackagesLocked()也會(huì)被安裝應(yīng)用的流程調(diào)用,所以ReconcileRequest的參數(shù)有些是為了給它們用的,這個(gè)方法的主要功能就是為了校驗(yàn)簽名
- 首先檢查簽名是否需要更新ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags),這個(gè)不太能理解,簽名是可以更新的?
- 調(diào)用verifySignatures()進(jìn)行校驗(yàn)簽名
- 最后生成ReconciledPackage并返回
commitReconciledScanResultLocked()
這里面也是安裝應(yīng)用和掃描應(yīng)用同時(shí)都會(huì)調(diào)用的方法,先只看掃描的情況
- 處理PackageSetting,這部分邏輯是和安裝應(yīng)用是混在一起的,主要包括
- SharedUserSetting, 這個(gè)一般只有覆蓋安裝的時(shí)候如果sharedUserId變了,要重新賦值
- 重命名包名的邏輯,安裝重命名包名的時(shí)候會(huì)更新packages.xml文件
- 將PackageSetting寫入package-restrictions.xml,里面主要存儲(chǔ)的是disabled和enabled四大組件
- 最后調(diào)用commitPackageSettings()進(jìn)行最后一步處理
commitPackageSettings()
- 判斷是不是有廠商自定義的ResolveActivity(config_customResolverActivity屬性),如果有則調(diào)用setUpCustomResolverActivity()設(shè)置ResolveActivity為自定義的,ResolveActivity就是處理未指定的android.action.View的
- 處理ResolveActivity,如果沒(méi)有的話指定為framework的ResolveActivity
- 更新SharedLibraries,這部分還不太熟悉
- 檢查是否需要frozen應(yīng)用,如果需要但沒(méi)有frozen則退出安裝,有以下幾種情況無(wú)需frozen應(yīng)用
- SCAN_BOOTING,開機(jī)的時(shí)候,因?yàn)檫@時(shí)沒(méi)有應(yīng)用啟動(dòng)的了
- SCAN_DONT_KILL_APP,安裝不殺死應(yīng)用,這個(gè)一般要么在PkMS中指定,要么在adb安裝的時(shí)候指定
- SCAN_IGNORE_FROZEN,忽略FROZEN,這個(gè)連adb也沒(méi)有指定選項(xiàng),可能是代碼遺留
- 調(diào)用mSettings.insertPackageSettingLPw(),這里并非是更新packages.xml的地方,只是將pkgSetting,放到Settings.mPackages中
- 將AndroidPackage放到mPackages中
- 更新KeySetManagerService的應(yīng)用信息,這是一個(gè)管理簽名的服務(wù)
- 添加自定義的Permission和PermissionGroup到PermissionManagerService中
- 添加保護(hù)廣播,但這個(gè)并非給安裝應(yīng)用使用,而是系統(tǒng)應(yīng)用,例如:teleservice等
- 對(duì)于老的package或者Permission更新的需要對(duì)于申請(qǐng)了這些權(quán)限的應(yīng)用更新權(quán)限,注意,這些權(quán)限也是由應(yīng)用定義的
掃描完成之后需要做的事情
- 清除掉已刪除的系統(tǒng)應(yīng)用和disable原本就disable的應(yīng)用
- 調(diào)用PermissionManagerService.updateAllPermissions()更新權(quán)限,這部分是在所有應(yīng)用掃描完成之后才執(zhí)行
- 創(chuàng)建應(yīng)用數(shù)據(jù)存儲(chǔ)的目錄,注意這里只是針對(duì)system core應(yīng)用(如SystemUI)做的操作,data下的應(yīng)用由于有多用戶的存在,每個(gè)data應(yīng)用是針對(duì)單個(gè)用戶的,
- 最終將PackageSetting和permission以及簽名的fingerprint寫入到packages.xml當(dāng)中,這里這樣設(shè)計(jì)我覺(jué)得應(yīng)該是怕中途關(guān)機(jī),這樣一次性寫入所有的信息可以盡量減少寫了一半關(guān)機(jī)的情況
以上就是Android 開機(jī)應(yīng)用掃描相關(guān)總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Android 開機(jī)應(yīng)用掃描的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Android Shader應(yīng)用開發(fā)之雷達(dá)掃描效果
- Android應(yīng)用中使用ContentProvider掃描本地圖片并顯示
- Android實(shí)現(xiàn)掃描和生成二維碼
- Android實(shí)現(xiàn)掃描二維碼功能
- Android studio 實(shí)現(xiàn)手機(jī)掃描二維碼功能
- Android如何實(shí)現(xiàn)掃描和生成二維碼
- Android銀行卡掃描獲取銀行卡號(hào)
- Android實(shí)現(xiàn)銀行卡號(hào)掃描識(shí)別功能
- Android 6.0 掃描不到 Ble 設(shè)備需開啟位置權(quán)限的方法
- Android手機(jī)(設(shè)備)連接掃描槍掃碼遇到的問(wèn)題
- Android編程實(shí)現(xiàn)wifi掃描及連接的方法
- Android實(shí)現(xiàn)支付寶AR掃描動(dòng)畫效果
- Android 二維碼掃描和生成二維碼功能
相關(guān)文章
Android studio保存logcat日志到本地的操作
這篇文章主要介紹了Android studio保存logcat日志到本地的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
RecyclerView中監(jiān)聽(tīng)EditText變化的BUG的解決方法
本篇文章主要介紹了RecyclerView中監(jiān)聽(tīng)EditText變化的BUG的解決方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
Android中的HTextView庫(kù)實(shí)現(xiàn)TextView動(dòng)畫效果
HTextView是一個(gè)用來(lái)給TextView里的文字做各種轉(zhuǎn)換動(dòng)畫的開源庫(kù),不僅提供了多種動(dòng)畫選擇,而且還有重復(fù)字符的位移動(dòng)畫,雖然并沒(méi)有多么復(fù)雜,但是它使用的這些典型的設(shè)計(jì)模式以及各種動(dòng)畫的實(shí)現(xiàn)確實(shí)可以從中讓我們學(xué)到不少知識(shí)2023-12-12
Android實(shí)現(xiàn)顯示系統(tǒng)實(shí)時(shí)時(shí)間
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)顯示系統(tǒng)實(shí)時(shí)時(shí)間,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
Kotlin?LinearLayout與RelativeLayout布局使用詳解
Kotlin?的基本特性就先寫到這里,我們這個(gè)系列的定位是基礎(chǔ),也就是能用就好,夠用就好,我們不會(huì)舉太多的例子,但是這些都是最經(jīng)常用到的特性。從這節(jié)開始就是Kotlin和android?進(jìn)行結(jié)合,使用Kotlin進(jìn)行安卓應(yīng)用的開發(fā)了2022-12-12
Flutter實(shí)戰(zhàn)教程之酷炫的開關(guān)動(dòng)畫效果
這篇文章主要給大家介紹了關(guān)于Flutter實(shí)戰(zhàn)教程之酷炫的開關(guān)動(dòng)畫效果的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Android xml文件的序列化實(shí)現(xiàn)代碼
Android提供了XmlSerializer來(lái)實(shí)現(xiàn)XML文件的序列化。相比傳統(tǒng)方式,更高效安全,需要的朋友可以參考下2014-02-02
Android運(yùn)動(dòng)健康睡眠自定義控件的實(shí)現(xiàn)
這篇文章主要介紹了Android實(shí)現(xiàn)運(yùn)動(dòng)健康睡眠自定義控件的方法,幫助大家更好的理解和學(xué)習(xí)使用Android開發(fā),感興趣的朋友可以了解下2021-03-03
Android通過(guò)AlarmManager類實(shí)現(xiàn)簡(jiǎn)單鬧鐘功能
這篇文章主要為大家詳細(xì)介紹了Android通過(guò)AlarmManager類實(shí)現(xiàn)簡(jiǎn)單鬧鐘功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06

