明確了witen 5.1升級到bturnc 5.3不綁定imei的用戶選項;
6掃描APK目錄
在PackageManagerService的構造函數中調用scanDirTracedLI方法來掃描壹個目錄的apk文件。
在Android 10.0中,PKMS主要掃描以下路徑的APK信息:
/供應商/覆蓋
/產品/覆蓋
/產品服務/覆蓋
/ODM/覆蓋
/OEM/覆蓋
/系統/框架
/系統/私人應用程序
/系統/應用程序
/供應商/私人應用程序
/供應商/應用程序
/odm/priv-app
/odm/app
/oem/app
/oem/priv-app
/產品/隱私應用程序
/產品/應用程序
/產品服務/隱私應用程序
/產品服務/應用程序
/產品服務/隱私應用程序
我們以scanDirTracedLI()為入口來分析壹下:
6.1[ParallelPackageParser.java]scanDirTracedLI()
從下面的函數可以看出,scanDirTracedLI的入口非常簡單。首先添加systtrace的壹些日誌痕跡,然後調用scanDirLI()進行分析。
private void scanDirTracedLI(File scanDir,final int parseFlags,int scanFlags,long currentTime) {
TRACE . TRACE begin(TRACE _ TAG _ PACKAGE _ MANAGER," scanDir["+scanDir . getabsolutepath()+"]");
嘗試{
scanDirLI(scanDir,parseFlags,scanFlags,current time);
}最後{
TRACE . TRACE end(TRACE _ TAG _ PACKAGE _ MANAGER);
}
}
6.2[ParallelPackageParser.java]斯坎迪裏
ScanDirLI()使用ParallelPackageParser的對象,這是壹個隊列。這裏我們取所有手機系統的apk,然後從這些隊列中取出apk,然後調用PackageParser進行解析。
private void scanDirLI(File scanDir,int parseFlags,int scanFlags,long currentTime) {
final File[]files = scandir . list files();
if (ArrayUtils.isEmpty(files)) {
Log.d(標簽,“應用目錄中沒有文件”+scanDir);
返回;
}
if(調試包掃描){
Log.d(標簽,"掃描應用目錄"+scanDir+" scan flags = "+scan flags
+" flags = 0x "+integer . tohexstring(parse flags));
}
//parallelPackageParser是收集系統apk文件的隊列。
//然後從這個隊列中逐個取出apk,調用PackageParser進行解析。
try(ParallelPackageParser ParallelPackageParser = new ParallelPackageParser(
mSeparateProcesses,mOnlyCore,mMetrics,mCacheDir,
mparallelpackaparsercallback)){
//並行提交文件進行解析
int file count = 0;
for(文件文件:文件){
//是Apk文件或目錄。
最終布爾值is package =(isApkFile(file)| | file . is directory())
& amp& amp!package installer service . isstagename(file . getname());
過濾掉非apk文件,如果沒有,跳過繼續掃描。
如果(!isPackage) {
//忽略不是包的條目
繼續;
}
parallelPackageParser中存儲APK信息的對象mQueue,將PackageParser()函數賦給隊列中的pkg成員。
//引用[6.3]
parallelpackageparser . submit(file,parse flags);
filecount++;
}
//逐個處理結果
for(;文件計數& gt0;文件計數- ) {
//從parallelPackageParser中取出隊列apk的信息。
ParallelPackageParser。parse result parse result = parallelpackageparser . take();
throwable throwable = parse result . throwable;
int errorCode = PackageManager。INSTALL _ SUCCEEDED
if (throwable == null) {
// TODO(toddke):在掃描鏈中下移
//靜態共享庫有合成的包名
if(parse result . pkg . application info . isstaticsharedlibrary()){
renameStaticSharedLibraryPackage(parse result . pkg);
}
嘗試{
//調用scanPackageChildLI方法掃描特定的apk文件。
//這個類的實例代表壹個apk文件,所以是APK文件對應的數據結構。
//引用[6.4]
scanPackageChildLI(parse result . pkg,parseFlags,scanFlags,
currentTime,null);
} catch(PackageManagerException e){
errorCode = e.error
Slog.w(TAG,"未能掃描"+parse result . scan file+":"+e . getmessage());
}
} else if(throwable instance of package parser。PackageParserException) {
PackageParser。package parser exception e =(package parser。PackageParserException)
可投擲的;
errorCode = e.error
Slog.w(TAG,"未能解析"+parse result . scan file+":"+e . getmessage());
}否則{
拋出新的IllegalStateException("解析時出現意外異常"
+ parseResult.scanFile,throwable);
}
//刪除無效的用戶數據應用程序
//如果是非系統apk,解析失敗。
if((scan flags & amp;SCAN_AS_SYSTEM) == 0。& amp
錯誤代碼!= PackageManager。安裝成功){
logCriticalInfo(Log。警告,
”刪除“+ parseResult.scanFile”處的無效包);
//非系統包掃描失敗。刪除文件。
removeCodePathLI(parse result . scan file);
}
}
}
}
6.3[ParallelPackageParser.java]提交
將掃描路徑中的APK和其他內容放入隊列mQueue中,並將parsePackage()賦給ParseResult以供後續調用。
public void submit(文件掃描文件,int parseFlags) {
mservice . submit(()-& gt;{
parse result pr = new parse result();
TRACE . TRACE begin(TRACE _ TAG _ PACKAGE _ MANAGER," parallel parse PACKAGE["+scan file+"]");
嘗試{
package parser PP = new package parser();
PP . setseparateprocesses(mSeparateProcesses);
PP . setonlycoresses(monly core);
pp.setDisplayMetrics(公制);
PP . setcachedir(mCacheDir);
PP . set callback(mPackageParserCallback);
pr . scan file = scan file;
pr.pkg = parsePackage(pp,scanFile,parse flags);
} catch(可投擲e) {
pr . throwable = e;
}最後{
TRACE . TRACE end(TRACE _ TAG _ PACKAGE _ MANAGER);
}
嘗試{
mqueue . put(pr);
} catch (InterruptedException e) {
Thread.currentThread()。中斷();
//將結果傳播給take()的調用方。
//這有助於防止主線程在等待時被卡住
// ParallelPackageParser在中斷時結束
mInterruptedInThread = thread . current thread()。getName();
}
});
}
通過parsePackage分析apk。如果傳入的packageFile是壹個目錄,則調用parseClusterPackage()進行解析。
如果調用了傳入的APK文件,則調用parseMonolithicPackage()進行解析。
公共包解析包(文件包文件,整數標誌,布爾使用緩存)
拋出PackageParserException {
...
if (packageFile.isDirectory()) {
//如果傳遞的packageFile是目錄,則調用parseClusterPackage()進行解析。
parsed = parseClusterPackage(package file,flags);
}否則{
//如果是APK文件,調用parseMonolithicPackage()進行解析。
parsed = parsimonolicipackage(package file,flags);
}
...
返回已解析的;
}
讓我們來看看parseClusterPackage()
函數:解析給定目錄中包含的所有apk,並將它們作為單個包處理。這也可以執行完整性檢查,例如要求相同的包名和版本代碼、單個基本APK和唯壹的分割名。
首先用parseClusterPackageLite()解析目錄中的apk文件,主要區別在於是核心應用還是非核心應用。核心應用只有壹個,非核心應用可以沒有,也可以有多個。非核心應用的功能主要是節省資源和代碼。然後在核心應用上調用parseBaseApk分析,生成包。非核心應用調用parseSplitApk,分析結果放在前面的包對象中。
私有包parseClusterPackage(文件packageDir,int flags)拋出PackageParserException {
//獲取應用程序目錄的PackageLite對象,該對象在目錄中分別存儲核心應用程序和非核心應用程序的名稱。
final package lite = parseClusterPackageLite(package dir,0);
//如果lite中沒有核心應用,退出。
如果(mOnlyCoreApps & amp& amp!lite.coreApp) {
拋出新的PackageParserException(INSTALL _ PARSE _ FAILED _ MANIFEST _ formattered,
"不是core app:"+package dir);
}
//生成拆分依賴關系樹。
//建立分區依賴關系樹
SparseArray split dependencies = null;
最終SplitAssetLoader assetLoader
if(lite . isolated splits & amp;& amp!array utils . isempty(lite . split names)){
嘗試{
split dependencies = splitassetdependencyloader . createdependencies from package(lite);
asset loader = new SplitAssetDependencyLoader(lite,splitDependencies,flags);
} catch(SplitAssetDependencyLoader。IllegalDependencyException e) {
拋出新的PackageParserException(INSTALL _ PARSE _ FAILED _ BAD _ MANIFEST,e . getmessage());
}
}否則{
asset loader = new DefaultSplitAssetLoader(lite,flags);
}
嘗試{
final asset manager assets = asset loader . getbase asset manager();
最終文件baseApk =新文件(lite . base code path);
//分析核心應用程序
最終包pkg = parseBaseApk(baseApk,assets,flags);
if (pkg == null) {
拋出新的PackageParserException(INSTALL _ PARSE _ FAILED _ NOT _ APK
無法解析基本APK:“+base apk”);
}
如果(!array utils . isempty(lite . split names)){
final int num = lite . split names . length;
pkg . split names = lite . split names;
pkg . splitcodepaths = lite . splitcodepaths;
pkg . splitrevisioncodes = lite . splitrevisioncodes;
pkg . split flags = new int[num];
pkg . splitprivateflags = new int[num];
pkg . application info . split names = pkg . split names;
pkg . application info . split dependencies = split dependencies;
pkg . application info . splitclassloadernames = new String[num];
for(int I = 0;我& ltnumi++) {
final asset manager split assets = asset loader . getsplitassetmanager(I);
//非核心應用程序的處理
parsplitapk(pkg,I,splitAssets,flags);
}
}
pkg . setcodepath(package dir . getcanonicalpath());
pkg . setuse 32 bitabi(lite . use 32 bitabi);
返回pkg
} catch (IOException e) {
拋出新的PackageParserException(INSTALL _ PARSE _ FAILED _ UNEXPECTED _ EXCEPTION,
無法獲取路徑:“+ lite.baseCodePath,e”);
}最後{
iou tils . closequietly(asset loader);
}
}
再來看看parseMonolithicPackage()。它的功能是解析壹個給定的APK文件,並把它當作壹個單壹的軟件包。
ParseBaseApk()也被調用來進行解析。接下來我們來看看parsebasepk()。
公共包parseMonolithicPackage(文件apkFile,int標誌)拋出PackageParserException {
final package lite = parsimonolicipackagelite(apk file,flags);
如果(monlycrepse){
如果(!lite.coreApp) {
拋出新的PackageParserException(INSTALL _ PARSE _ FAILED _ MANIFEST _ formattered,
"不是core app:"+apk file);
}
}
final SplitAssetLoader asset loader = new DefaultSplitAssetLoader(lite,flags);
嘗試{
//分析核心應用程序
最終包pkg = parseBaseApk(apkFile,asset loader . getbaseassetmanager(),flags);
pkg . setcodepath(apk file . getcanonicalpath());
pkg . setuse 32 bitabi(lite . use 32 bitabi);
返回pkg
} catch (IOException e) {
拋出新的PackageParserException(INSTALL _ PARSE _ FAILED _ UNEXPECTED _ EXCEPTION,
無法獲取路徑:“+ apkFile,e”);
}最後{
iou tils . closequietly(asset loader);
}
}
ParseBaseApk()主要解析AndroidManifest.xml,解析後的所有信息都放在Package對象中。
私有包parseBaseApk(文件apkFile,資產管理器資產,int標誌)
拋出PackageParserException {
最終字符串apk path = apk file . getabsolutepath();
...
XmlResourceParser parser = null
...
final int cookie = assets . findcookieforpath(apk path);
if (cookie == 0) {
拋出新的PackageParserException(INSTALL _ PARSE _ FAILED _ BAD _ MANIFEST,
添加資產路徑失敗:“+apk path”;
}
//獲取XML資源解析對象,解析APK的AndroidManifest.xml文件。
parser = assets . openxmlresourceparser(cookie,ANDROID _ MANIFEST _ FILENAME);
最終資源res =新資源(資產,矩陣,空);
final String[]out error = new String[1];
//調用重載函數parseBaseApk()最後得到parseBaseApkCommon(),解析AndroidManifest.xml後得到壹個Package對象
最終包pkg = parseBaseApk(apkPath,res,parser,flags,out error);
...
pkg . setvolumeuuid(volume uuid);
pkg . setapplicationvolumeuuid(volume uuid);
pkg . setbasecodepath(apk path);
pkg . set signing details(signing details。未知);
返回pkg
...
}
從AndroidManifest.xml中獲取標記名,解析標記中每壹項的內容,並將其存儲在Package對象中。
例如,獲取標簽“應用程序”、“權限”
私有包parseBaseApkCommon(包pkg,集acceptedTags,資源res,
XmlResourceParser parser,int flags,String[] outError)拋出XmlPullParserException,
IOException {
typed array sa = RES . obtain attributes(解析器,
com . Android . internal . r . styleable . Android manifest);
//獲取AndroidManifest.xml中的sharedUserId壹般有“android.uid.system”等信息。
string str = sa . getnonconfigurationstring(
com . Android . internal . r . styleable . Android manifest _ shared userid,0);
while ((type = parser.next())!= XmlPullParser。結束_文檔
& amp& amp(類型!= XmlPullParser。END _ TAG | | parser . get depth()& gt;outerDepth)) {
//從AndroidManifest.xml獲取標記名
string tagName = parser . getname();
//如果讀到AndroidManifest.xml中的標簽是“application”,執行parseBaseApplication()解析。
if(tagname . equals(TAG _ APPLICATION)){
if (foundApp) {
...
}
foundApp = true
//解析“應用”的信息,賦給pkg。
如果(!parseBaseApplication(pkg,res,parser,flags,outError)) {
返回null
}
...
//如果標簽是“權限”
else if(tagname . equals(TAG _ PERMISSION)){
//分析“權限”
如果(!parsePermission(pkg,res,parser,outError)) {
返回null
}
....
}
}
}
}
上面解析了AndroidManifest.xml。
妳會得到諸如“申請”、“覆蓋”、“許可”、“使用-許可”等信息。
我們來分析壹下“應用”,進入parseBaseApplication()函數。
私有布爾parseBaseApplication(包所有者,資源res,
XmlResourceParser解析器,int標誌,String[] outError)
while ((type = parser.next())!= XmlPullParser。結束_文檔
& amp& amp(類型!= XmlPullParser。END _ TAG | | parser . get depth()& gt;innerDepth)) {
//獲取“應用程序”子選項卡的標簽內容。
string tagName = parser . getname();
//如果標簽是“活動”
if(tagname . equals(" activity "){
//解析活動信息,將活動添加到包對象中。
Activity a = parseActivity(owner,res,parser,flags,outError,cachedArgs,false,
owner . base hardware accelerated);
if (a == null) {
mParseError = PackageManager。INSTALL _ PARSE _ FAILED _ MANIFEST _畸形;
返回false
}
hasActivityOrder |= (a.order!= 0);
owner . activities . add(a);
} else if(tagname . equals(" receiver "){
//如果標簽為“receiver”,則獲取接收者信息,添加Package對象。
Activity a = parseActivity(owner,res,parser,flags,outError,cachedArgs,
真,假);
if (a == null) {
mParseError = PackageManager。INSTALL _ PARSE _ FAILED _ MANIFEST _畸形;
返回false
}
hasReceiverOrder |= (a.order!= 0);
owner . receivers . add(a);
} else if(tagname . equals(" service ")){
//如果標簽為“服務”,則獲取服務信息,添加包對象。
Service s = parseService(owner,res,parser,flags,outError,cache dargs);
if (s == null) {
mParseError = PackageManager。INSTALL _ PARSE _ FAILED _ MANIFEST _畸形;
返回false
}
hasServiceOrder |= (s.order!= 0);
owner . services . add;
} else if(tagname . equals(" provider "){
//如果標簽為“提供者”,則獲取提供者信息,添加包對象。
Provider p = parseProvider(owner,res,parser,flags,outError,cache dargs);
if (p == null) {
mParseError = PackageManager。INSTALL _ PARSE _ FAILED _ MANIFEST _畸形;
返回false
}
owner . providers . add(p);
}
...
}
}
在PackageParser掃描APK後,系統根據APK中的AndroidManifest.xml創建了壹個完整的包對象。
下壹步是將包添加到系統中。此時調用的函數是另壹個scanPackageChildLI。
6.4[PackageManagerService.java]scanPackageChildLI()
當平臺初始化時,調用addForInitLI()將包內容添加到內部數據結構中。
私有PackageParser。包scanPackageChildLI(package parser。包裝包裝,
final @ParseFlags int parseFlags,@ScanFlags int scanFlags,long currentTime,
@Nullable UserHandle用戶)
拋出PackageManagerException {
...
//掃描父項
PackageParser。package scanned pkg = addForInitLI(pkg,parseFlags,
scanFlags,currentTime,user);
//掃描孩子
final int child count =(pkg . child packages!= null)?pkg . child packages . size():0;
for(int I = 0;我& lt兒童計數;i++) {
PackageParser。package child package = pkg . child packages . get(I);
//在平臺初始化期間向內部數據結構添加新的包。
//平臺初始化時,將包內容添加到內部數據結構中。
addForInitLI(子包,解析標誌,掃描標誌,
currentTime,用戶);
}
if((scan flags & amp;僅掃描檢查)!= 0) {
返回scanPackageChildLI(pkg,parseFlags,scanFlags,currentTime,user);
}
}
在addForInitLI()中,檢查安裝包,檢查簽名,更新apk,並將包添加到系統中。
私有PackageParser。package addForInitLI(package parser。包裝包裝,
@ParseFlags int parseFlags,@ScanFlags int scanFlags,long currentTime
@Nullable UserHandle用戶)
拋出PackageManagerException {
//判斷系統應用是否需要更新。
同步(多包){
//更新子應用程序
if (isSystemPkgUpdated) {
...
}
if (isSystemPkgBetter) {
//將安裝包更新到系統分區。
同步(多包){
//只從包列表中刪除加載的條目
m packages . remove(pkg setting . name);
}
...
//創建安裝參數InstallArgs
final install args args = createInstallArgsForExisting(
pkgSetting.codePathString,
pkgSetting.resourcePathString,getAppDexInstructionSets(pkg setting));
args . cleanupresourcesli();
同步(多包){
m settings . enablesystempackagelpw(pkg setting . name);
}
}
//安裝包驗證
collectCertificatesLI(pkg setting,pkg,forceCollect,skip verify);
...
try(package freezer freezer = freeze package(pkg . package name,
" scanPackageInternalLI "){
//如果兩個apk簽名不匹配,則調用deletePackageLIF方法清除apk文件及其數據。
deletePackageLIF(pkg . package name,null,true,null,0,null,false,null);
}
...
//更新系統apk程序
install args args = createInstallArgsForExisting(
pkgSetting.codePathString,
pkgSetting.resourcePathString,getAppDexInstructionSets(pkg setting));
同步(最小鎖定){
args . cleanupresourcesli();
}
}
//如果新安裝的系統app會被舊的APP數據覆蓋,就需要隱藏系統APP,重新掃描/data/app目錄。
if (shouldHideSystemApp) {
同步(多包){
m settings . disablesystempackagelpw(pkg . package name,true);
}
}
}
回顧APK的整個掃描過程:
根據核心app >系統app & gt其他應用優先掃描APK,解析AndroidManifest.xml文件,獲取每個標簽的內容。
在PackageParser掃描壹個APK後,系統已經根據APK中的AndroidManifest.xml創建了壹個完整的Package對象,下壹步就是將包添加到系統中。
非系統包掃描失敗,正在刪除文件。