PMS
PackageMangerService源碼閱讀總結
簡介
PackageMangerService(簡稱PMS)是Android系統(tǒng)中負責管理所有Apk的安裝、卸載更新等工作,其運行在SystemServer進程,維護管理系統(tǒng)中所有的Apk包管理,那它是如何管理眾多APK的呢?它怎么知道系統(tǒng)中有哪些APK應用,這些Apk在什么時候安裝、卸載和更新,PMS如何感知?就算它知道了系統(tǒng)中所有APK的存在,如何去建立一個維護呢?
帶著以上諸多疑問,從源碼的角度,來探尋這些問題,感悟PMS的精髓所在!
淺析自己設計一個PMS包管理
管理對象 — 靜態(tài)存儲在磁盤上的APK文件
管理者 — PMS管理器,系統(tǒng)中是唯一的
目標對象 — 活的應用程序,界面上能顯示,能交互
首先,我作為PMS管理者,系統(tǒng)開機BOOT時,要掃描系統(tǒng)中的文件,尋找哪些是APK文件,然后針對處理,但是不可能掃描系統(tǒng)中所有的路徑,肯定要事先做好約束,APK文件只能放在某些特定的目錄下;還有一點,系統(tǒng)開機后,用戶自行安裝apk文件時,這個時候PMS不可能又去掃描一遍,最好的方法是提供一個命令接口,用戶安裝時通過調(diào)用這個命令接口,這樣PMS就可以拿到用戶安裝的APK文件,然后分析之,以下幾個路徑是PMS開機啟動時會掃描的幾個路徑:
system/app 系統(tǒng)自帶的應用程序
data/app 用戶程序安裝的目錄
vendor/app 廠商定制app目錄
system/priv-app/ 系統(tǒng)私有App,其中service可以保活,一旦被殺死就會立即恢復,缺點無法正常升級
其次,PMS拿到APK文件后,要解析分享APK內(nèi)容,拿到其中重要的東西,然后再保存到內(nèi)存或數(shù)據(jù)庫中,內(nèi)存中用一系列賦值的數(shù)據(jù)模型建立好每個APK文件的映射,提供給Launcher程序,Launcher根據(jù)數(shù)據(jù)模型可以清楚指定有哪些應用,將其繪制到桌面上去,難點在于解析APK這塊,你需要知道APK需要哪些權限、四大組件有哪些,lib動態(tài)庫,使用的資源res文件等,解析后用內(nèi)存數(shù)據(jù)模型維護好它們
實質(zhì)上分析APK內(nèi)容主要就是AndroidManifest.xml文件,然后把其中的內(nèi)容映射到PackageParser.Package、PackageSetting中
最后,PMS在自己的進程里面,需要提供安裝、卸載更新接口,其他用戶進程通過binder跨進程來調(diào)用即可
PMS源碼瞧瞧
按照我們上面的設計內(nèi)容,從源碼中去看看它是如何工作的?
PMS啟動切入點
位于SystemServer的run方法中,其中調(diào)用startBootstrapServices,啟動Boot相關的服務:
private void startBootstrapServices() {....mOnlyCore表示是加密參數(shù),true表示只解析加密的APKmPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);.... }進入main方法:
public static PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {// 自檢初始化配置參數(shù)PackageManagerServiceCompilerMapping.checkProperties();PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);m.enableSystemUserPackages();把PMS加入到binder服務列表中去,名為packageServiceManager.addService("package", m);return m; }這里就正式進入PackageManager的領域了!
PMS構造方法
為什么要講他的構造方法?首先,大量的工作內(nèi)容都是在構造方法中完成的;其次,后續(xù)的安裝、更新工作也是按照構造方法中這個套路的來執(zhí)行的,所以它構造方法很重要!
構造方法中,有以下幾個事件階段,如下:
- BOOT_PROGRESS_PMS_START
- BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
- BOOT_PROGRESS_PMS_DATA_SCAN_START
- BOOT_PROGRESS_PMS_SCAN_END
- BOOT_PROGRESS_PMS_READY
BOOT_PROGRESS_PMS_START
START階段做了很多事情,依次闡述,源碼如下,精簡了部分:
Settings初始化工作,PMS中相關的配置工作類
Settings是整個系統(tǒng)的APK配置管理類,里面維護了所有apk的內(nèi)存存儲模型,mPakcages作為參數(shù)傳遞 進去,mPackages類型為ArrayMap<String, PackageParser.Package>,key為packagename mSettings = new Settings(mPackages); 添加sharedId內(nèi)容,擁有相同sharedid可以共享資源,也可以在同一個進程運行,后面會講到 mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.log", LOG_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); SystemConfig systemConfig = SystemConfig.getInstance(); 解析相應文件(/system/etc/permission和(framework/base/data/etc/下的文件), 包括platform.xml和系統(tǒng)支持的各種硬件模塊的feature, mGlobalGids = systemConfig.getGlobalGids(); mSystemPermissions = systemConfig.getSystemPermissions(); mAvailableFeatures = systemConfig.getAvailableFeatures();SystemConfig主要工作內(nèi)容是:
建立底層的uid和 group id同行上層permission之間的映射,可以指定一個權限與幾個id的對應。當一個APK被授予這個權限時,它也同時屬于這幾個組。
例如:platform.xml文件內(nèi)容有 <permission name="android.permission.BLUETOOTH" ><group gid="net_bt" /></permission>即字符串藍牙權限屬于用戶組net_bt,SystemConfig讀取到藍牙權限時,會用PermissionEntry實體保存,name為android.permission.BLUETOOTH,而gid是字符串,會用Process將gid字符串轉(zhuǎn)換為整型數(shù)字保存在gids數(shù)組;也就是如下:public static final class PermissionEntry { public final String name;public int[] gids;}當我們應用程序在AndroidManifest.xml聲明了BLUETOOTH權限時,實質(zhì)就是將我們加入到gids中某個用戶組里面,這樣就能夠訪問相應的文件給一些底層用戶分配權限,如給shell授予各種permission權限,把一個權限賦予uid,當進程只是用這個uid運行時,就具備了這個權限。
libary,系統(tǒng)增加的一些應用需要link擴展的jar。
feature,系統(tǒng)每增加一個硬件,都要添加相應的feature,將解析結果放入mSystemPermissions,mShareLibrariest,mSettings.mPermissions,mAvailableFeatures等幾個集合供系統(tǒng)查詢和權限配置使用。
然后看看Settings是個什么東西?其構造方法如下:
//傳遞進入mPackage當做一把鎖 Settings(Object lock) {參數(shù)一返回的/data目錄this(Environment.getDataDirectory(), lock); } Settings(File dataDir, Object lock) {mLock = lock;mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);mSystemDir = new File(dataDir, "system");mSystemDir.mkdirs();FileUtils.setPermissions(mSystemDir.toString(),FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH|FileUtils.S_IXOTH,-1, -1); 創(chuàng)建一些xml文件mSettingsFilename = new File(mSystemDir, "packages.xml");mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");mPackageListFilename = new File(mSystemDir, "packages.list");FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);final File kernelDir = new File("/config/sdcardfs");mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;// Deprecated: Needed for migrationmStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); }小結:
從以上源碼看,settings主要在data目錄下創(chuàng)建一些xml文件,并且后續(xù)掃描解析apk后,會把apk信息保存到這些文件中去,
插入一個SharedId知識點:
即AndroidManifest.xml里面的sharedUserId,兩個應用聲明相同的sharedUserId,就可以共享資源,如果簽名相同還會運行在同一個進程下面;UID即用戶User ID,Android中每個應用程序都有自己的一個UID,不同的UID應用程序之間是進程隔離的,但是如果我們在AndroidManifest.xml里面manifest標簽里面聲明相同的shareuserid,并且使用相同的簽名,則兩個應用程序是可以共享資源,并且運行在同一進程里面;
回到本文源碼mSettings.addSharedUserLPw(“android.uid.system”, Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED),我們跟進去看看:
final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<String, SharedUserSetting>(); //存放大于10000小于19999的UID的sharedUserSetting private final ArrayList<Object> mUserIds = new ArrayList<Object>(); //同上,只是保存小于10000的UIDsharedUidSetting private final SparseArray<Object> mOtherUserIds =new SparseArray<Object>(); SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) { map獲取value SharedUserSetting s = mSharedUsers.get(name);if (s != null) {userId是SharedUserSetting成員,就是UID,這里指如果添加進來的UID和保存的相同就直接返回if (s.userId == uid) {return s;}PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate shared user, keeping first: " + name);return null;}創(chuàng)建一個新的SharedUserSettings = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);s.userId = uid;并且把這個uid的應用添加到mOtherUserIds或mUserIds結構體中去if (addUserIdLPw(uid, s, name)) {mSharedUsers.put(name, s);return s;}return null; }private boolean addUserIdLPw(int uid, Object obj, Object name) {last為19999 if (uid > Process.LAST_APPLICATION_UID) {return false;}first為10000 如果UID大于10000的應用,添加到mUserIds結構中if (uid >= Process.FIRST_APPLICATION_UID) {int N = mUserIds.size();final int index = uid - Process.FIRST_APPLICATION_UID;while (index >= N) {mUserIds.add(null);N++;}if (mUserIds.get(index) != null) {PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate user id: " + uid+ " name=" + name);return false;}mUserIds.set(index, obj);小于10000的屬于系統(tǒng)應用,添加到mOtherUserIds結構體中去} else {if (mOtherUserIds.get(uid) != null) {PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate shared id: " + uid+ " name=" + name);return false;}mOtherUserIds.put(uid, obj);}return true;}總結一下,Settings類里面有三個總要成員:
ArrayMap<String, SharedUserSetting> mSharedUsers:string保存了sharedId的描述符,SharedUserSetting保存應用信息
ArrayList<Object> mUserIds:保存UID大于10000的應用程序,通過源碼得知,Object保存實質(zhì)就是SharedUserSetting
SparseArray<Object> mOtherUserIds:保存UID小于10000的應用程序
這里有一個問題?相同sharedID的不同應用在這三個數(shù)據(jù)模型內(nèi)是如何保存的?答案就在SharedUserSetting
如上圖,userId是用戶ID值,name是其描述,ArraySet<PackageSetting>是擁有相同sharedId的應用,每個應用對應一個PackageSetting,這樣系統(tǒng)就知道哪些app擁有相同的sharedId,綜上在Settings里面的三個成員數(shù)據(jù)結構模型可歸納為下圖:
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
掃描啟動階段,它會掃描某些特定目錄,解析目錄下存在的APK文件,并解析至,獲得PackageParser.Package,每個APK對應一個PackageParser.Package,最后在轉(zhuǎn)換生成PackageSetting存儲在Settings里面去;過程就是這樣,難點在于他如何去解析每一個APK,APK還會分單個APK和集群式APK,分別有不同的解析方法,解析完成后還涉及與舊版本APK對比更新等等!
掃描的路徑主要是:
/vendor/overlay /system/framework /system/priv-app /system/app /vendor/app /data/app /data/app-private什么是集群式APK?
集群式APK其APK文件不止一個,一般由一個base.apk做基礎,其他功能模塊由不同split.apk組成,如Google提出App bundle技術就是這類型的apk,無論是單個Apk還是集群式Apk最終解析到內(nèi)存數(shù)據(jù)模型都是為一個PackageParser.Package對象
APK解析過程parser
認清目標,解析對象是什么?最終的結果是什么?如下圖:
源碼中,解析流程主要是:
a. PackageManagerService.scanDirTracedLI() -> ParallelPackageParser.submit() -> PackageParser.parsePackage()
b. return PackageParser.Package到PackageManagerService
c. PackageManagerService.scanPackageLI(PackageParser.Package) 并得到PackageSetting
d. Settings保存PackageSetting
重點看一下a步驟里面PackageParser.parsePackage() 方法,方法內(nèi)部又調(diào)用了PackageParser.parsePackage方法:
public Package parsePackage(File packageFile, int flags, boolean useCaches) throws PackageParserException {有緩存就取緩存 Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;if (parsed != null) {return parsed;} 如果file是一個目錄,說明是集群式APP,使用parseClusterPackage去解析if (packageFile.isDirectory()) {parsed = parseClusterPackage(packageFile, flags);} else {單個App使用parseMonolithicPackage解析parsed = parseMonolithicPackage(packageFile, flags);} 緩存結果cacheResult(packageFile, flags, parsed);return parsed; }這里我們看parseClusterPackage是如何實現(xiàn)的?
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {解析APK精簡內(nèi)容,只解析apk的Androidmanifest中manifest、Application、use-split和package-verify標簽下的屬性final PackageLite lite = parseClusterPackageLite(packageDir, 0);if (mOnlyCoreApps && !lite.coreApp) {throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,"Not a coreApp: " + packageDir);}// Build the split dependency tree.SparseArray<int[]> splitDependencies = null;final SplitAssetLoader assetLoader;if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {try {每個包apk依賴了其他另外的包,建立好他們的映射關系,即用到了use-split標簽splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);} catch (SplitAssetDependencyLoader.IllegalDependencyException e) {throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());}} else {assetLoader = new DefaultSplitAssetLoader(lite, flags);}try {//就是當前基礎路徑packageDirfinal File baseApk = new File(lite.baseCodePath);//解析baseApk中的AndroidManifest所有標簽final Package pkg = parseBaseApk(baseApk, assets, flags);if (pkg == null) {throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,"Failed to parse base APK: " + baseApk);}if (!ArrayUtils.isEmpty(lite.splitNames)) {final int num = lite.splitNames.length;pkg.splitNames = lite.splitNames;pkg.splitCodePaths = lite.splitCodePaths;pkg.splitRevisionCodes = lite.splitRevisionCodes;pkg.splitFlags = new int[num];pkg.splitPrivateFlags = new int[num];pkg.applicationInfo.splitNames = pkg.splitNames;pkg.applicationInfo.splitDependencies = splitDependencies;for (int i = 0; i < num; i++) {final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);//依次解析每個分包apk的AndroidManiesf文件,split apk中主要解析四大組件,其他如權限方面在parseBaseApk中已經(jīng)解析//并且base apk和splitapk中組件都是放在Package的成員中,沒有特地分開parseSplitApk(pkg, i, splitAssets, flags);}}pkg.setCodePath(packageDir.getAbsolutePath());pkg.setUse32bitAbi(lite.use32bitAbi);return pkg;} finally {IoUtils.closeQuietly(assetLoader);}}在看看parseBaseApk方法是什么樣子?
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,String[] outError) throws XmlPullParserException, IOException {final String splitName;final String pkgName;try {Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);//解析出來的是基礎包名和分包包名pkgName = packageSplit.first;splitName = packageSplit.second;//基礎包base-apk的分包包名必須為空if (!TextUtils.isEmpty(splitName)) {outError[0] = "Expected base APK, but found split " + splitName;mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;return null;}} catch (PackageParserException e) {mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;return null;}if (mCallback != null) {//回調(diào)到PackageManagerService獲取到overlay資源,加入到自己的資源路徑,用于替換//overlay機制見https://blog.csdn.net/azhengye/article/details/49050631String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);if (overlayPaths != null && overlayPaths.length > 0) {for (String overlayPath : overlayPaths) {res.getAssets().addOverlayPath(overlayPath);}}}final Package pkg = new Package(pkgName);TypedArray sa = res.obtainAttributes(parser,com.android.internal.R.styleable.AndroidManifest);//解析基礎包的manifest標簽下的基本屬性code和name等pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(com.android.internal.R.styleable.AndroidManifest_versionCode, 0);pkg.baseRevisionCode = sa.getInteger(com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);pkg.mVersionName = sa.getNonConfigurationString(com.android.internal.R.styleable.AndroidManifest_versionName, 0);if (pkg.mVersionName != null) {pkg.mVersionName = pkg.mVersionName.intern();}pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);sa.recycle();一下方法主要是解析AndroidMainfest.xml中的四大組件權限等標簽return parseBaseApkCommon(pkg, null, res, parser, flags, outError);上面mCallback.getOverlayPaths方法,主要是為了實現(xiàn)Android的Overlay機制,實現(xiàn)動態(tài)替換資源,具體Overlay簡介請點擊,最后parseBaseApkCommon就主要是機械AndroidMainfest中相通的一些內(nèi)容,如四大組件以及權限等
小結:
從源碼上來看解析集群式APK主要是解析每個APK的AndroidManifest.xml中的內(nèi)容,并建立好各個模塊APK之間的依賴關系,解析四大組件以及權限等、讀取代碼位置、安裝路徑等,最后存放到Package變量中去,所以,最終我們理解透這個Package中的內(nèi)容即可,它屬于PackageParser的內(nèi)部類,如下
上訴代碼中提到的Activity、Service等并不是我們開發(fā)中使用到的,而是PackageParser的內(nèi)部類,想想也可以理解,這里如Activity需要保存Activity的所有信息,如名字、啟動模式、Intent過濾等以及和ApplicationInfo之間的關系,這些都不是一個Activity所能保存的;
在重點看一下cd步驟,如何處理解析后的Package變量? 解析內(nèi)容太過于復雜,重點參考這篇文章,會先判斷apk的簽名,是否已經(jīng)安裝等,最后在保存到Settings實例中去,保存到Settings主要是一個PackageSetting對象,主要涉及文章前面提到的mPackages、mSharedUser、mOtherUserIds和mUserIds這三個成員中,調(diào)用最終邏輯在:
PackageManagerService.commitPackageSettings() -> Settings.insertPackageSettingLPw() -> Settings.addPackageSettingLPw()將把此次APK的PackageSetting添加到Settings的mPackages里面去,同時根據(jù)sharedId描述符添加到對應的mSharedUser里面去,最后根據(jù)當前APK的appId大小,將SharedUserSetting替換對應的mOtherUserIds和mUserIds成員中去
這里也是重點看一下PackageSetting是一個什么樣的對象:
從它的構造方法可以看出來,主要包含了一個APP的基本信息,如安裝位置,lib位置、資源位置、版本信息、包名等
BOOT_PROGRESS_PMS_DATA_SCAN_START
這個步驟和上一個步驟類似,掃描/data/app和/data/app-private目錄下的APK
BOOT_PROGRESS_PMS_SCAN_END
掃描結束,當前系統(tǒng)下所有APK文件已經(jīng)寫入到內(nèi)存結構模型中去了,主要在Settings的mPackages、mSharedUser、mUserId和mOtherUserId幾個成員,現(xiàn)在將要把相關信息寫入到文檔packages.xml中持久化,所以在PackageManagerService的BOOT_PROGRESS_PMS_SCAN_END步驟會有如下:
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis());writeLPr將會寫入信息到文檔packages.xml mSettings.writeLPr();void writeLPr() { ....try {mSettingsFilename變量是Settings構造方法創(chuàng)建的package.xml文件FileOutputStream fstr = new FileOutputStream(mSettingsFilename);BufferedOutputStream str = new BufferedOutputStream(fstr);//XmlSerializer serializer = XmlUtils.serializerInstance();XmlSerializer serializer = new FastXmlSerializer();serializer.setOutput(str, StandardCharsets.UTF_8.name());serializer.startDocument(null, true);serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);serializer.startTag(null, "packages");xml序列化所有PackageSettingfor (final PackageSetting pkg : mPackages.values()) {writePackageLPr(serializer, pkg);}for (final PackageSetting pkg : mDisabledSysPackages.values()) {writeDisabledSysPackageLPr(serializer, pkg);}for (final SharedUserSetting usr : mSharedUsers.values()) {serializer.startTag(null, "shared-user");serializer.attribute(null, ATTR_NAME, usr.name);serializer.attribute(null, "userId",Integer.toString(usr.userId));usr.signatures.writeXml(serializer, "sigs", mPastSignatures);writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissionStates());serializer.endTag(null, "shared-user");} str.flush();FileUtils.sync(fstr);str.close();// New settings successfully written, old ones are no longer// needed.mBackupSettingsFilename.delete();FileUtils.setPermissions(mSettingsFilename.toString(),FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP|FileUtils.S_IWGRP,-1, -1);return;} catch(XmlPullParserException e) {Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "+ "current changes will be lost at reboot", e);} catch(java.io.IOException e) {Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "+ "current changes will be lost at reboot", e);} }以上就把所有apk配置信息寫入到文件中去了,后續(xù)其他應用需要包信息都可以從這里找到,也可以從Settings的成員變量中找到
BOOT_PROGRESS_PMS_READY
創(chuàng)建安裝服務PackageInstallerService,接收來自安裝程序如adb、packageinstaller等安裝請求
PMS安裝APK
什么是安裝?
第一步:拷貝文件到指定的目錄:
在Android系統(tǒng)中,apk安裝文件是會被保存起來的,默認情況下,用戶安裝的apk首先會被拷貝到/data/app目錄下,/data/app目錄是用戶有權限訪問的目錄,在安裝apk的時候會自動選擇該目錄存放用戶安裝的文件,而系統(tǒng)出場的apk文件則被放到了/system分區(qū)下,包括/system/app,/system/vendor/app,以及/system/priv-app等等,該分區(qū)只有ROOT權限的用戶才能訪問,這也就是為什么在沒有Root手機之前,我們沒法刪除系統(tǒng)出場的app的原因了。
第二步:解壓縮apk,寶貝文件,創(chuàng)建應用的數(shù)據(jù)目錄
為了加快app的啟動速度,apk在安裝的時候,會首先將app的可執(zhí)行文件dex拷貝到/data/dalvik-cache目錄,緩存起來。然后,在/data/data/目錄下創(chuàng)建應用程序的數(shù)據(jù)目錄(以應用的包名命名),存放在應用的相關數(shù)據(jù),如數(shù)據(jù)庫、xml文件、cache、二進制的so動態(tài)庫等。
第三步:解析apk的AndroidManifest.xml文件
第四步:顯示快捷方式
如果這些應用程序在PackageManagerService服務注冊好了,如果我們想要在Android桌米上看到這些應用程序,還需要有一個Home應用程序,負責從PackageManagerService服務中把這些安裝好的應用程序取出來,并以友好的方式在桌面上展現(xiàn)出來,例如以快捷圖標的形式。在Android系統(tǒng)中,負責把系統(tǒng)中已經(jīng)安裝的應用程序在桌面中展現(xiàn)出來的Home應用就是Launcher了。
PackageHandler
PackageHandler屬于PMS的內(nèi)部類,是一個Handler,主要用于接收binder客戶端,如PackageInstaller或adb的安裝程序,發(fā)起跨進程調(diào)用;對于PMS暴露出來的安裝接口,如:
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,int installFlags, String installerPackageName, int userId) {final Message msg = mHandler.obtainMessage(INIT_COPY);final VerificationInfo verificationInfo = new VerificationInfo(null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,null /*packageAbiOverride*/, null /*grantedPermissions*/,null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));msg.obj = params;Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",System.identityHashCode(msg.obj));Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(msg.obj));發(fā)送給PackageHandlermHandler.sendMessage(msg); }PackageHandler屬于PMS的子線程,專門處理安裝程序!PMS內(nèi)部的安裝流程就不在分析!
備注一個重要信息,如果我們編譯了系統(tǒng)應用apk后,希望我們的apk能默認獲取一些權限,而不是安裝時,或者運行彈出權限提醒,人工點擊允許的話!可以在以下這個地方添加默認權限
在PMS啟動完成后,會由Zygote調(diào)用他的systemRead()方法,在此方法內(nèi)部會調(diào)用mDefaultPermissionPolicy.grantDefaultPermissions(userId),也就是給系統(tǒng)app添加默認權限的,進入這個類:
本文博客參考很多博主隔壁老李頭博客,更多細節(jié)可以參考其博客!感謝!
總結
- 上一篇: win7系统下连网络打印机打印反应很慢解
- 下一篇: 名人名言摘选-李嘉诚