APK安裝流程系列文章整體內容如下:
- APK安裝流程詳解0——前言
- APK安裝流程詳解1——有關"安裝ing"的實體類概述
- APK安裝流程詳解2——PackageManager簡介
- APK安裝流程詳解3——PackageManager與PackageManagerService
- APK安裝流程詳解4——安裝中關于so庫的哪些事
- APK安裝流程詳解5——PackageInstallerService和Installer
- APK安裝流程詳解6——PackageManagerService啟動前奏
- APK安裝流程詳解7——PackageManagerService的啟動流程(上)
- APK安裝流程詳解8——PackageManagerService的啟動流程(下)
- APK安裝流程詳解9——PackageParser解析APK(上)
- APK安裝流程詳解10——PackageParser解析APK(下)
- APK安裝流程詳解11——普通應用安裝簡介
- APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)
- APK安裝流程詳解13——PackageManagerService中的新安裝流程下(裝載)
- APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充
- APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補充
- APK安裝流程詳解16——Android包管理總結
- 1、設計思想
- 2、PackageManagerService的抽象理解
- 3、PackageManagerService里面的數據結構
- 4、PackageManagerService的三大流程
- 5、PackageManagerService的體系結構
一、設計思想
如果你是Android 系統中的架構師,讓你設計一個Android的安裝系統中的PackageManagerService,你會怎么設計? 既然要設計,咱們要首先弄清幾個問題,我希望大家看下面的問題的時候,多想兩個問題:1、如果讓你設計,你怎么設計。這個"類"存在意義是什么?
- 1、為什么關機的時候手機是磚頭,而開機后,所有APP都可以運行了,這是為什么?
- 2、Android系統是通過什么手段來加載已經安裝到手機上應用的?
- 3、既然是加載,按照科學的架構設計,是不是應該存在一個管理者,來全局管理,那個這個類是什么?
- 4、在安裝一個APK的時候,APK是"死的",Android系統是怎么把它變成一個"活的"APP,他是怎么加載到內存中去的
那我們就來依次來看下這幾個問題
1、為什么關機的時候手機是磚頭,而開機后,所有APP都可以運行了,它是怎么加載的?
- 首先明確一點,手機關機以后,就是一個冰冷的磚頭,只能用來"砸核桃",那開機后,你點擊桌面上的任何一個圖片,都能開啟一個APP,這說明在開機過程中,系統把已經安裝好的APP加載到內存中,這到底是怎么做的?所以我們反推斷,在安卓系統中肯定存在這么一塊區域,用于存放已經安裝的APP的信息,在開機的時候,通過系統掃描,這塊區域,把對應的內容加載到內存中去。
- 其次,通過上面的分析,我們知道了在Android系統中存在這樣一塊區域,在開機的的時候,加載這塊區域的信息,從而實現加載在內存中去。那么我們繼續反推斷,那這塊區域的信息,是怎么來的?應該在安裝這個APK的時候,把這個APK的信息寫入到該區域的。這樣就可以實現了在安卓系統一次安裝后,在刪除APK文件后,還可以運行APP了
其實上面的解答是基本上所有操作的系統的安裝思路,大家可以想一下在Windows下是不是也是如此。
上面說的Android區域其實就是:“/data目錄”下的system目錄,這個目錄用來保存很多系統文件。主要工作是創建了5個位于目錄/data/system的File對象,分別是:
- packages.xml:記錄了系統中所有安裝的應用信息,包括基本信息、簽名和權限
- pakcages-back.xml:packages.xml文件
- pakcages-stoped.xml:記錄系統中被強制停止的運行的應用信息。系統在強制停止某個應用的時候,會將應用的信息記錄在該文件中。
- pakcages-stoped-backup.xml:pakcages-stoped.xml文件的備份
- 保存普通應用的數據目錄和uid等信息
這個5個文件中pakcages-back.xml和pakcages-stoped-backup.xml是備份文件。當Android對文件packages.xml和pakcages-stoped.xml寫之前,會先把它們備份,如果寫文件成功了,再把備份文件刪除。如果寫的時候,系統出問題了,重啟后在需要讀取這兩個文件時,如果發現備份文件存在,會使用備份文件的內容,因為源文件可能已經損壞了。其中packages.xml是PackageManagerServcie啟動時,需要用到的文件。
我把我的Nexus 6P手機Root后,在/data/system 截屏如下:
我把packages.xml導出來,文件內容太大,我就直接截屏了,內容如下:
圖片看不清,可以看下面的縮減版
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
<version sdkVersion="23" databaseVersion="3" fingerprint="google/angler/angler:6.0.1/MTC20L/3230295:user/release-keys" />
<version volumeUuid="primary_physical" sdkVersion="23" databaseVersion="23" fingerprint="google/angler/angler:6.0.1/MTC19T/2741993:user/release-keys" />
<permission-trees>
<item name="com.google.android.googleapps.permission.GOOGLE_AUTH" package="com.google.android.gsf" />
</permission-trees>
<permissions>
<item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
<item name="android.permission.REMOTE_AUDIO_PLAYBACK" package="android" protection="2" />
.....
<item name="com.android.voicemail.permission.ADD_VOICEMAIL" package="android" protection="1" />
</permissions>
<package name="com.google.android.youtube" codePath="/system/app/YouTube" nativeLibraryPath="/system/app/YouTube/lib" primaryCpuAbi="arm64-v8a" publicFlags="945307205" privateFlags="0" ft="11e9134c000" it="11e9134c000" ut="11e9134c000" version="107560144" userId="10075">
<sigs count="1">
<cert index="0" key="30820252308201bb02044934987e300d06092a864886f70d01010405003070310b3009060355040613025553310b3009060355040813024341311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c652c20496e6331143012060355040b130b476f6f676c652c20496e633110300e06035504031307556e6b6e6f776e301e170d3038313230323032303735385a170d3336303431393032303735385a3070310b3009060355040613025553310b3009060355040813024341311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c652c20496e6331143012060355040b130b476f6f676c652c20496e633110300e06035504031307556e6b6e6f776e30819f300d06092a864886f70d010101050003818d00308189028181009f48031990f9b14726384e0453d18f8c0bbf8dc77b2504a4b1207c4c6c44babc00adc6610fa6b6ab2da80e33f2eef16b26a3f6b85b9afaca909ffbbeb3f4c94f7e8122a798e0eba75ced3dd229fa7365f41516415aa9c1617dd583ce19bae8a0bbd885fc17a9b4bd2640805121aadb9377deb40013381418882ec52282fc580d0203010001300d06092a864886f70d0101040500038181004086669ed631da4384ddd061d226e073b98cc4b99df8b5e4be9e3cbe97501e83df1c6fa959c0ce605c4fd2ac6d1c84cede20476cbab19be8f2203aff7717ad652d8fcc890708d1216da84457592649e0e9d3c4bb4cf58da19db1d4fc41bcb9584f64e65f410d0529fd5b68838c141d0a9bd1db1191cb2a0df790ea0cb12db3a4" />
</sigs>
<perms>
<item name="com.google.android.c2dm.permission.RECEIVE" granted="true" flags="0" />
<item name="android.permission.USE_CREDENTIALS" granted="true" flags="0" />
<item name="com.google.android.providers.gsf.permission.READ_GSERVICES" granted="true" flags="0" />
<item name="com.google.android.youtube.permission.C2D_MESSAGE" granted="true" flags="0" />
<item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
<item name="android.permission.NFC" granted="true" flags="0" />
<item name="android.permission.CHANGE_NETWORK_STATE" granted="true" flags="0" />
<item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
<item name="com.google.android.gms.permission.AD_ID_NOTIFICATION" granted="true" flags="0" />
<item name="android.permission.INTERNET" granted="true" flags="0" />
<item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
<item name="android.permission.VIBRATE" granted="true" flags="0" />
<item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
<item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="11" />
<domain-verification packageName="com.google.android.youtube" status="0">
<domain name="youtu.be" />
<domain name="m.youtube.com" />
<domain name="youtube.com" />
<domain name="www.youtube.com" />
</domain-verification>
</package>
上面是我手機packages.xml的一個片段。我們看下里面的"youtube"應用。通過標簽<package>記錄了一個應用的基本信息,簽名和聲明的權限。
(1)<package>表示包信息,下面我們就來解釋下標簽<package>中的屬性
- name表示應用的包名
- codePath表示的是apk文件的路徑
- nativeLibraryPath表示應用的native庫的存儲路徑
- flags是指應用的屬性,如FLAG_SYSTEM、FLAG_PERSISTENT等
- it表示應用安裝的時間
- ut表示應用最后一次修改的時間
- version表示應用的版本號
- userId表示所屬于的id
(2)<sign>表示應用的簽名,下面我們就來解釋下 標簽<sign>中的屬性
- count表示標簽中包含有多少個證書
- cert表示具體的證書的值
(3)<perms>表示應用聲明使用的權限,每一個子標簽代表一項權限
通過上面的內容,我們知道Android系統通過packages.xml文件來存儲應用信息的,所以我們舉一反三,新安裝的APK,肯定是把新安裝的APK相關信息寫入這個packages.xml文件中,那么怎么把這個xml文件,映射到內存中的? 那我們就來看第二個問題
2、Android系統是通過什么手段來加載已經安裝到手機上應用的
上面提到了,應用的信息都存儲在packages.xml中的<package>標簽里面,那我們是怎么加載到內存中去的?大家平時是存儲數據庫的時候都是怎么做的?對的,一般都是一個實體類對應數據庫中的一個表;其中每一個對象對應的是數據庫中的一條數據。同理,Android系統也是這樣設計的,<package>標簽里面記錄的包信息其實是一一對應的PackageSetting類。
PackageSetting繼承了PackageSettingBase類,PackageSettingBase類繼承自GrantedPremisson類。應用的基本信息保存在PackageSettingBase類的成員變量中,聲明的權限保存在GrantedPremissions類,簽名則保存在SharedUserSetting類的成員變量signatures中。標簽<package>所標識的應用PackageSetting對象都保存在Setting的mPackages中,定義如下:
// com.android.server.pm.Settings.java
final HashMap<String, PackageSetting> mPackages =
new HashMap<String, PackageSetting>();
在packages.xml中除了標簽<package>,還有<updated-package>、<cleaning-package>和<renamed-package> 這三種標簽。
- <updated-package> 標簽表示升級包覆蓋的系統應用,對應的是PackageSetting,在Settings里面同樣用mPackages 變量表示
- <cleaning-package> 標簽用來記錄那些已經刪除,但是數據目錄還暫時保留的應用的信息。對應的是PackageCleanItem。在Settings里面用mPackagesToBeCleaned變量表示
- <renamed-package> 標簽用來記錄系統中改名的應用。它的記錄信息都插入到mSettings的mRenamedPackages對象中。
其中mPackagesToBeCleaned和mRenamedPackages在mSettings.java的定義如下:
// com.android.server.pm.Settings.java
// Packages that have been uninstalled and still need their external
// storage data deleted.
final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
// Packages that have been renamed since they were first installed.
// Keys are the new names of the packages, values are the original
// names. The packages appear everwhere else under their original
// names.
final HashMap<String, String> mRenamedPackages = new HashMap<String, String>();
上面用大量的文筆說Settings,那么它是什么東西?下面就讓我繼續來看下一個問題
3、既然是加載,按照科學的架構設計,是不是應該存在一個管理者,來全局管理,那個這個類是什么?
這個類就是Settings
Settings是Android的包的全局管理者,用于協助PackageManagerService保存所有的安裝包信息,同時用于存儲系統執行過程中的一些設置,PackageManagerService和Settings之間的類圖關系如下:
大圖地址1
Settings里面有3個重要的成員變量:mShareUsers,mPackages,mSharedUsers 。如下:
final ArrayMap<String, SharedUserSetting> mSharedUsers =
new ArrayMap<String, SharedUserSetting>();
final ArrayMap<String, PackageSetting> mPackages =
new ArrayMap<String, PackageSetting>();
final ArraySet<String, SharedUserSetting> mSharedUsers =
new ArraySet<String, SharedUserSetting>();
- mShareUsers是一個以String類型的name為"key",ShareUserSetting對象為"value"的ArrayMap。
- mPackages是一個以String類型的name為"key",PackageSetting對象為"value"的ArrayMap。
- mSharedUsers 是一個以String類型的name(比如"android.uid.system")為"key",以SharedUserSetting 對象為"value"的HashMap
其中ShareUserSetting類繼承自GrantedPermissions ,內部包含一個ArraySet類型的packages ,這個packages保存了聲明相同的shareUserId的Package的權限設置信息(PackageSetting )通過上面的問題,我們知道PackageSetting繼承自PackageSettingBase,同時PackageSetting中保存著package的多種信息。
如下圖:
上面提到了一個概念是SharedUserSetting,那么ShareUserSetting的作用什么是什么?那我們就來看下:
SharedUserSetting用來描述具有相同的sharedUserId的應用信息,它的成員變量packages保存了所有具有相同sharedUserId的應用信息引用。這些應用的簽名時相同的,所有只需要在成員變量signatures中保存一份。通過這個對象,Android運行時很容易檢索到某個應用擁有相同的sharedUserId的其他應用。其中應用的簽名保存在ShardUserSetting類的成員變量signatures中。
我們在看系統應用的AndroidManifest.xml中會發現
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.settings" coreApp="true"
android:sharedUserId="android.uid.system"
android:versionCode="1"
android:versionName="1.0" >
</manifest>
shareUserId與UID相關,作用是:
- 1、兩個或多個APK或者進程聲明了同一種sharedUserId的APK可以共享批次的數據,并且可以在運行在同一進程中(相當于進程是系統的用戶,某些進程可以歸為同一用戶使用,相當于Linux系統中的GroupId)
- 2、通過聲明特定的sharedUserId,該APK所在的進程將被賦予指定的
2.通過聲明特定的sharedUserId,該APK所在的進程將被賦予指定的UID,將被賦予該UID特定的權限。
在Settings中和用戶有關的還有兩個重要變量,即mUserIds 和mOtherUserIds
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
private final SparseArray<Object> mOtherUserIds =
new SparseArray<Object>();
他們都是以UID為索引,得到對應的ShardUserSetting對象。更多的關于Android系統中關于"用戶"的信息,在后面"用戶模塊"再單獨講解
4、在安裝一個APK的時候,APK是"死的",Android系統是怎么把它變成一個"活的"APP,他是怎么加載到內存中去的
這里就不得不提一下PackageParser這個類,這個類負責在APK文件安裝的時候,解析AndroidManifest。
在Android中,Settings提供可持續化的包信息管理,PackageSetting是一個存儲單元,表示一個pkg信息。我們在解析APK安裝包的時候,會用到PackageParser,在PackageParser里面有一個字段是PackageParse.Package。這個PackageParse.Package其實是對應的上面packages.xml里面的<package>標簽。同時PackageParse.Package也可以理解為pkg信息在內存中的一個實時信息,關機后變消失,重啟后重新生成,所以PackageParse.Package中的信息一致保證最新。PackageParse.Packag、Settings和PackageSetting三者的關系如下:
Settings中保存了一個包名和PackagesSetting的映射表,PackageParse.Package中的mExtras引用指向了對應的PackageSetting實例,而PackageParse中保存了一個PackageParse.Package列表
從上到下,介紹如下:
1:PackageParser.Package對應一個apk完整的原始數據
2:PackageSetting包含一個PackageParser對象實例,說明它也對應一個apk包的數據,不同的是,它還包括apk相關配置數據,比如apk內部哪些component是被disable等。
3:Settings包含了PackageSetting對象列表,也就是說它包含了系統所有apk數據,還有就是PackageParser,顧名思義,負責APK數據解析
4:PackageManagerService是全局的包管理器
5、補充一點:
Settings里面的主要關聯關系如下圖:
二、PackageManagerService的抽象理解
上面說了很多,我們再上升一個高度,PackageManagerService到底應該怎么去理解它?
每一個組織結構,都有一套自己的管理機制,比如任何一家公司,都會存在下面三個元素:管理者(經理)、被管理者(員工)、管理機制(公司的規章制度及KPI考核等)。同理在Android的系統的世界里面,也有一家公司叫"包管理"。如果要研究Android的包管理機制,同樣可以從以下幾個角度來思考?
- 管理者是誰,他的職責是什么?
- 被管理者是誰,他的職責是什么?
- 管理機制是什么,它是如何運轉的?
所謂包,其實就是一種文件的格式,比如APK包,JAR包等,在Android中存活著很多包,所有的應用程序都是APK包,很多構成Android運行環境的都是JAR包,還有一些以so為后綴的庫文件,包管理者很重要的一個職責就是識別不同的包,統一維護這些包的信息。當有一個包進入(安裝)或者離開(卸載)Android世界,都需要向包管理者申報,其他管理部分要獲取包的具體信息,也都需要向包管理者申請。
如同一家公司是由人與人協作工作的,不同包之間也需要進行協作。既然有協作,自然就有協作的規范,一個包可以干什么,不可以干什么,都需要有一個明確的范圍界定,這就是包管理中的權限設計。涉及到的內容非常廣泛,Android的權限管理、SELinux,都是包管理中權限設計的組成部分。同理Android的世界就像一個井然有序的一家公司,既有包管理部門,也有其他各種管理部門,比如電源管理部門,窗口管理部門等等。大家不僅各司其職,而且也有來往。比如在APK安裝到Activity的顯示,看著很簡單的過程,其實卻需要大量的管理部門參與進來,不斷地進行數據解析、封裝、傳遞、呈現,其內部機制十分復雜。
現在大家想一下上面三個問題的答案,我詳細大部分人的前兩個答案是一致的,管理者是PackageManagerService,被管理是各種"包",最后一個答案是各有千秋,這里是沒有標準答案的,希望大家能自己找到自己的答案。
三、PackageManagerService里面的數據結構
PackageManagerService涉及的數據結構非常多,在分析源碼時,很容易陷入各種數據結構之間的關系,難以自拔,以至于看不到包管理的全貌。我在這里簡單的總結了一下各個數據結構的職能如下:
- PackageManangerService :包管理的核心服務
- com.android.server.pm.Settings :所有包的管理信息
- com.android.server.pm.PackageSetting :單一包的信息
- com.android.server.pm.BasePermission :系統中已有的權限
- com.android.server.pm.PermissionState :授權狀態
—————————————分隔符—————————————
- PackageParser:包解析器
- PackageParser.Package :解析得到的包信息
- PackageParser.Component :組件的基類,其子類對應到AndroidManifest.xml中定義的不同組件
- PackageParser.Activity 對應AndroidManifest.xml中定義<Activity>和<Receiver>標簽
- PackageParser.Service :對應AndroidManifest.xml中定義<Service/>標簽
- PackageParser.Provider :對應AndroidManifest.xml中定義<Provider/> 標簽
- PackageParser.Instrumentation :對應AndroidManifest.xml中定義<Instrumentation/> 標簽
- PackageParser.Permission :對應AndroidManifest.xml中定義<permission/> 標簽
- PackageParser.PermissionGroup :對應AndroidManifest.xml中定義<permission-group/> 標簽
- PackageLite :輕量的包信息
- ApkLite :輕量級的APK信息
- IntentInfo :組件所定義的<intent-filter/>信息,保存了每個<intent-filter/>節點的信息,是基類,它的子類是ActivityIntentInfo、ServiceIntentInfo和ProviderIntentInfo
- ActivityIntentInfo :保存<activity/>和<Receiver/>節點下的<intent-filter/>節點
- ServiceIntentInfo :保存<service/>節點下的<intent-filter/>節點
- ProviderIntentInfo:保存<provider/> 節點下的<intent-filter/>節點
—————————————分隔符—————————————
- PackageInfo :跨進程傳遞的包數據,包解析時生成
- PackageItemInfo :一個應用包內所有組件項和通用信息的基類。提供最基本的屬性集,如:label、icon、meta-data等。
- ApplicationInfo:代表一個特定應用的基本信息,對應AndroidManifest里面的<application>
- InstrumentationInfo:用作進行instrumentation的測試的片段,對應AndroidManifest里面的<instrumentation>
- PermissionInfo:代表一個特定的權限,對應AndroidManifest里面的<permission/>
- PermissionGroupInfo :一個特定的權限組,對應AndroidManifest里面的<permission-group/>
- ComponentInfo:代表一個應用內組件(如activityInfo、serviceInfo、ProviderInfo)通用信息的基類。一般不會直接使用該類,它設計為了不同應用的組件共享統一的定義。
- ActivityInfo :對應AndroidManifest.xml里面的注冊的<activity/>標簽和<receiver/>標簽。代表一個Activity或者receiver
- ServiceInfo :對應AndroidManifest.xml里面的注冊的<service/>標簽。代表一個service
- ProviderInfo :對應AndroidManifest.xml里面的注冊的<service/>標簽。代表一個Provider
- Intent:根據特定的條件找到匹配的組件
- IntentFilter :Intent過濾器
- ResolveInfo
- IntentResolver :Intent解析器,其子類用于不同組件的Intent解析
保存了所有<activity/>或者<receiver/>節點信息。(Activity或者BroadcastReceiver信息就是用該自定義類保存的)
- ActivityIntentResolver :保存所有<activity/>和<receiver/>節點信息。(Activity或者BroadcastReceiver信息就是用該自定義類保存的)
保存了所有<service/>節點信息。(Service信息就是用該自定義類保存的)。- ServiceIntentResolver :保存了所有<service /> 節點信息。(Service信息就是用該自定義類保存的)
- ProviderIntentReslover:保存了所有 <provider /> 節點信息
- PackageHandler :包管理的消息處理器
- HandlerParams :消息的數據載體
- InstallParams : 用于APK的安裝
- MeasureParams:用于查詢某個已安裝的APK占據存儲空間的大小(例如在設置程序中得到某個APK的緩存文件大小)
- MoveParams :用于已安裝APK的位置移動
- InstallArgs :APK的安裝參數
- FileInstallArgs :針對是安裝在內部存儲的APK
- AsecInstallArgs :針對安裝在SD卡上的APK
- MoveInfoArgs : 移動APK
這么龐大的數據結構,其各個數據結構的類圖如下:
大圖地址3
四、PackageManagerService的三大過程組
如果大家想對Android系統有一個大致的了解,就必須要要了解PackageManagerService的三大流程
- 1、包掃描的過程組:
即Android將一個APK文件的靜態信息轉化為可以管理的數據結構- 2、包安裝的過程組:
即包管理接納一個新成員的體現。- 3、包查詢的過程組:
即Intent的定義和解析是包查詢的核心,通過包查詢服務可以獲取到一個包的信息
下面我們來一一進行簡單的介紹
(一)、包掃描過程組——即開機掃描過程
1、為什么要進行包掃描?
掃描目錄的目的:
掃描Android系統的幾個目標文件中的APK,從而建立合適的數據結構以及管理諸如Package信息、四大組件、授權信息等各種信息。抽象的地看,PackageManagerService像一個工廠,它解析實際的物理文件(APK文件),以及生成符合自己要求的產品。比如PackageManagerService將解析APK包中的AndroidManifest.xml,并根據其中聲明的Activity標簽來創建與此對應的對象,并保存到PackageParser.Package類型的變量中,然后通過PackageManagerService的scanPackageDirtyLI()方法將解析后的組件數據統計到PackageManagerService的本地變量中,用于管理查詢調用,當系統中任意某個APK的package發生改變時,如卸載,升級等操作都會更新package的統計數據到PackageManagerService,PackageManagerService正式基于擁有系統中所有的Package的信息才能勝任"包管理"這個管理者的角色。PackageManagerService的工作流程相對簡單,復雜的是其中用于保存各種信息的數據結構和它們的關聯關系,以及對應影響結果的策略控制(比如系統應用和普通應用)
2、包掃描過程組的不同理解
如果把包掃描過程組看成一件事,那么這件事就是:
調用PackageManagerService類的靜態方法main()方法來獲取PackageManagerService對象
如果把包掃描過程組看成兩件事,那么這兩件事就是
1、創建PackageManagerService對象
2、將PackageManagerService向ServiceManager注冊,即加入SMS,方便后續其他進程或者app通過ServiceManager獲得PackageManagerService服務。
如果把包掃描過程組看成三件事,那么這三件事是:
1、先讀取保存在packages.xml中記錄的系統關機前記錄所有安裝的APP信息, 將其保存在PackageManagerServiced中mSettings中的mPackages中。
2、掃描指定的若干目錄中的app,并把信息記錄在PackageManagerServiced的mPackages中。
3、最后上面的兩者進行對比,看是否有升級的APP,然后進行相關處理,最后寫入package.xml中
當然換一個角度,以掃描角度來看,也可以把包掃描分解成另外三個階段:
- 掃描目標文件夾之前的準備工作
- 掃描目標文件夾
- 掃描目標文件夾之后的工作
如果把包掃描過程組看成四件事,那么這四件事是:
1、讀取響應的配置文件
2、優化APK和Jar包
3、掃描系統中所有安裝的應用
4、把掃描出的所有應用信息進行保存
如果把包掃描過程組劃分的更細,則我將其分為6大步驟
- 1、變量初始化,包括mSettings,mInstaller,mPackageDexOptimizer等等
- 2、讀取配置文件
- 3、掃描系統Package,包含Dex優化
- 4、保存掃描信息
- 5、掃描非系統應用
- 6、更新數據
如果把包掃描過程組劃分的更細,則我將其分為9大步
第一步:創建Settings對象,并調用其addSharedUserLPw()方法,保存ShareUserSetting信息
第二步:創建Installer對象,用于Native進程installd交互
第三步:創建ThreadHandler線程,并以其Looper為參數創建PackageHandler對象,用于程序的安裝和卸載
第四步:根據Installer對象和/data/user文件對象創建UserManager對象,用于多用戶管理
第五步:調用readPermissions()方法,從/system/etc/permissions目錄下的XML文件讀取權限信息
第六步:調用Settings對象的readLPw()方法解析/data/system目錄下的文件:
第七步:掃描/system/frameworks目錄以及BOOTCLASSPATH和platform.xml定義的系統目錄下的jar和APK文件是否需要dex優化,如果需要則調用Installer.dexopt()方法來發送消息給installd讓它優化;如果任意一個文件執行了dex優化操作,刪除/data/dalvik-cache目錄下的緩存文件
第八步:創建AppDirObserver對象監聽/system/frameworks、/system/app、/vendor/app(廠商定制)、/data/app、/data/app-private5個目錄,并調用scanDirLI()方法掃描其中的APK文件:
第九步:匯總上面掃描XML和APK得到的信息,并寫入文件;
3、如果把包掃描過程組劃分為"方法級"的流程,如下圖:
大圖地址3
4、溫馨提醒
- 在packages.xml中<package>標簽記錄的APP的安裝信息。有獨立uid的APP,后面再反序列化的時候,會映射為PackageSetting對象,保存在mSettings的mPackages中;有sharedUid的APP,后面反序列化的時候,會映射為PendingPackage對象,保存在mSettings的mPendingPackages中。
- 對于<share-user>標簽記錄的的share uid信息,封裝為SharedUserSetting對象,保存到mSettings里面的mSharedUser中,在此過程中遇到的uid和 shared uid都保存在mUserIds中,并讓每個uid指向與之關聯的PackageSetting對象,或者SharedUserSetting對象
5、小結
- PackageManagerService是伴隨著系統進程啟動而啟動的,最終會構造一個PackageManagerService對象,此后,PackageManagerService將成為Android世界的包管理者,對外提供包的增、刪、改、查的操作
- 在PackageManagerService的啟動過程中,最重要的是對所有靜態APK文件進行掃描,生成一個在內存中的數據結構Package,PackageManagerService實際上就是維護這所有在內存中的數據結構。已有的包的歷史信息會寫入磁石,PackageManagerService的Settings專門來管理寫入磁盤的包信息。
- 所有包的信息掃描完成后,需要對應用進行授權,這是Android權限管理的一部分。隨著 Android版本的升級,授權機制略有區別,總體框架是:每個APK都可以聲明權限,并為權限設定保護級別,其他APK需要使用這些權限的時候,需要先申請,再由系統判定是否進行授權。
(二)、包安裝的過程組——即安裝一個新的APK
安裝一個APK的其大致流程如下:
通常,安裝一個APK 通常分為以下4種方式
- 安裝系統應用
- 網絡下載應用安裝
- ADB工具安裝
- 第三方應用安裝
下面我們就依次介紹下
1、 安裝系統應用
系統的應用的安裝主要在PackageManagerService的main方法里面進行操作的
其順序如下:
第一步:PackageManagerService.main()初始化注冊
第二步:建立Java層的installer與C層的intalld的socket聯接
第三步:建立PackageHandler消息循環
第四步:調用成員變量mSettings的readLPw()方法恢復上一次的安裝信息
第五步:.jar文件的detopt優化
第六步:scanDirLI函數掃描特定目錄的APK文件解析
第七步:updatePermissionsLPw()函數分配權限
第八步:調用mSettings.writeLPr()保存安裝信息
2、 網絡下載應用安裝
其順序如下:
第一步:調用PackageManagerService的installPackage方法
第二步:上面的方法調用installPackageWithVerfication(),進行權限校驗,發送INIT_COPY的msg
第三步:進入PackageManagerService的doHandleMessage方法的INIT_COPY分支
第四步:成功綁定了com.android.defcontainer.DefaultContainerService服務,進入MCS_BOUND分支
第五步:里面調用PackageManagerService中內部抽象類HandlerParams的子類InstallParams的startCopy方法。
第六步:抽象類的HandlerParams的startCopy方法調用了HandlerParams子類的handleStartCopy和handlerReturnCode兩個方法
第七步:handlesStartCopy方法調用了InstallArgs的子類copyApk,它負責將下載的APK文件copy到/data/app
第八步:handleReturnCode調用handleReturnCode方法
第九步:調用PackageManagerService服務的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法進行APK掃描。
第十步:上面的方法判斷是否APP應安裝,調用installNewPackageLI或replacePackageLI方法
第十一步:調用updateSettingsLI方法進行更新PackageManagerService的Settings
第十二步:發送what值為POST_INSTALL的Message給PackageHandler進行處理
第十三步:發送what值為MCS_UNBIND的Message給PackageHandler,進而調用PackageHandler.disconnectService()中斷連接
3、 ADB工具安裝
Android Debug Bridge (adb)是SDK自帶的管理設備的工具,通過ADB命令的方式也可以為手機或者模擬器安裝應用,其入口函數為pm.java
Android Debug Bridge (adb) 是SDK自帶的管理設備的工具,通過ADB命令行的方式也可以為手機或模擬器安裝應用,其入口函數源文件為pm.java
其順序如下:
第一步:pm.java的runInstall()方法
第二步:參數不對會調用showUsage方法,彈出使用說明
第三步:正常情況runInstall會調用mPm變量的installPackageWithVerification方法
第四步:由于pm.java中的變量mPm是PackageManagerService的實例,所以實際上是調用PackageManagerService的installPackageWithVerfication()方法
第五步:進入PackageManagerService的doHandleMessage方法的INIT_COPY分支
第六步:成功綁定了com.android.defcontainer.DefaultContainerService服務,進入MCS_BOUND分支
第七步:里面調用PackageManagerService中內部抽象類HandlerParams的子類InstallParams的startCopy方法。
第八步:抽象類的HandlerParams的startCopy方法調用了HandlerParams子類的handleStartCopy和handlerReturnCode兩個方法
第九步:handlesStartCopy方法調用了InstallArgs的子類copyApk,它負責將下載的APK文件copy到/data/app
第十步:handleReturnCode調用handleReturnCode方法
第十一步:調用PackageManagerService服務的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法進行APK掃描。
第十二步:上面的方法判斷是否APP應安裝,調用installNewPackageLI或replacePackageLI方法
第十三步:調用updateSettingsLI方法進行更新PackageManagerService的Settings
第十四步:發送what值為POST_INSTALL的Message給PackageHandler進行處理
第十五步:發送what值為MCS_UNBIND的Message給PackageHandler,進而調用PackageHandler.disconnectService()中斷連接
4、 第三方應用安裝
第一步:調用PackageInstallerActivity的onCreate方法初始化安裝界面
第二步:初始化界面以后調用initiateInstall方法
第三步:上面的方法調用startInstallConfirm方法,彈出確認和取消安裝的按鈕
第四步:點擊確認按鈕,打開新的activity:InstallAppProgress
第五步:InstallAppProgress類初始化帶有進度條的界面之后,調用PackageManager的installPackage方法
第六步:PackageManager是PackageManagerService實例,所以就是調用PackageManagerService的installPackage方法
第七步:調用PackageManagerService的installPackage方法
第八步:上面的方法調用installPackageWithVerfication(),進行權限校驗,發送INIT_COPY的msg
第九步:進入PackageManagerService的doHandleMessage方法的INIT_COPY分支
第十步:成功綁定了com.android.defcontainer.DefaultContainerService服務,進入MCS_BOUND分支
第十一步:里面調用PackageManagerService中內部抽象類HandlerParams的子類InstallParams的startCopy方法。
第十二步:抽象類的HandlerParams的startCopy方法調用了HandlerParams子類的handleStartCopy和handlerReturnCode兩個方法
第十三步:handlesStartCopy方法調用了InstallArgs的子類copyApk,它負責將下載的APK文件copy到/data/app
第十四步:handleReturnCode調用handleReturnCode方法
第十五步:調用PackageManagerService服務的installPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法進行APK掃描。
第十六步:上面的方法判斷是否APP應安裝,調用installNewPackageLI或replacePackageLI方法
第十七步:調用updateSettingsLI方法進行更新PackageManagerService的Settings
第十八步:發送what值為POST_INSTALL的Message給PackageHandler進行處理
第十九步:發送what值為MCS_UNBIND的Message給PackageHandler,進而調用PackageHandler.disconnectService()中斷連接
點擊放大查看高清無碼大圖
小結:
- 1、安裝和卸載都是通過PackageManager,實質上是實現了PackageManagerService來完成具體的操作,所有細節和邏輯均可以在PackageManagerService中跟蹤查看。
- 2、所有安裝方式殊途同歸,最終就是回到PackageManagerService中,然后調用底層本地代碼的installd來完成的。
- 3、APK的安裝過程主要分為以下幾步:
- 拷貝到apk文件到指定目錄
- 解壓縮apk,拷貝文件,創建應用的數據目錄
- 解析apk的AndroidManifest.xml文件
- 向Launcher應用申請添加創建快捷方式
(三)、包查詢的過程組——即解析Intent并找到其配備的組件
1、包管理是以什么樣的形式對外提供服務那?
在寫應用程序時,我們通常會利用應用自身的上下文環境Context來獲取包管理服務,如下:
// 獲取一個PackageManager的對象實例
PackageManager pm = context.getPackageManager();
// 通過PackageManager對象獲取指定包名的包信息
PackageInfo pi = pm.getPackageInfo("com.android.contacts", 0);
這么一段簡單的代碼,其實蘊含很多的深意
- 1、上面已經講解過了PackageManagerService和其管理的各種數據結構,都是運行在系統進程之中。在應用進程中獲取的PackageManager對象,只是PackageManagerService在應用進程中的一個代理,不同的應用進程都有不同的代理,意味著不同應用進程中的PackageManager是不同的,但是管理者PackageManagerService有且只有一個
- 2、運行在應用進程中的PackageManager要與運行在系統進程中的PackageManagerService進行通信,通信手段是Android中最常見的Binder機制。因此會有一個IPackageManager.aidl文件,用于描兩者通信的接口。另外,應用進程中的PackageInfo對象。PackageInfo其實就是由系統進程傳遞到應用進程的對象
PackageManagerService作為包管理的最核心組成部分,伴隨著系統的啟動而創建,并一直運行系統進程中。當應用程序需要獲取包管理服務時,會生成一個PackageManager對PackageManagerService進行通信。在包解析時就會生成包信息,即XXInfo這一類數據結構,PackageManagerService將這些數據傳遞給需要的應用進程。
管理者對內設計了復雜的管理機制,對外封裝了簡單的使用接口。這種設計在Android中大量出現,比如ActivityManagerService、WindowManagerService、PowerManagerService等,基本所有的系統服務都遵循這種設計規范。對于應用程序而言,不需關心管理者的實現原理,只需要理解接口的使用場景
Android在全局定義了IPackageManager,接口,描述了包管理者對外提供的功能,運行在系統進程中的PackageManagerService實現了IPackageManager接口,作為包管理的服務端,客戶端通過IPackageManager接口請求包服務。為了方便客戶端進行包服務,Android做了多層的封裝。應用進程作為客戶端,通過PackageManager便可使用包服務,客戶端實際存在的對象是ApplicationPackageManager,它封裝了IPackageManager的所有接口。在應用進程來看,客戶端和服務端的概念是模糊的,明確的只有運行環境的概念,即Context。包服務就存在于應用進程的運行環境中,需要時直接拿出來使用即可。
“運行環境(Context)”是Android的設計哲學之一,Android有意弱化進程,強化運行環境,這是面向應用開發者的設計。運行環境是什么并不是一個很好回到的問題。可以將其類比為我們的工作環境,當我們需要辦公設備時,只需要向管理部門申請,并不需要關心辦公設備如何采購,辦公設備對一般的工作人員而言,就像是工作環境中天然存在的東西。
2、包管理的具體服務形式——Intent的解析:
在Android中,使用Intent來表達意圖,最終會有一個響應者。當系統產生一個Intent后,如何找到它的響應者?這需要對Intent進行解析。作為所有包信息管理者的中樞,PackageManagerService自然有義務承擔解析Intent的責任。要解析Intent,就需要了解Intent的結構,標識了一個Intent身份的信息由兩部分構成:
- 主要信息:主要信息Action和Data。Action用于表明Intent所要執行的操作,譬如ACTION_VIEW,ACTION_EDIT;Data用于表明執行操作的數據,譬如聯系人數據,數據是以URI來表達的。再舉兩個Action和Data成對出現的例子:
- ACTION_VIEW:content://contacts/people/1 :標識查看聯系人數據庫中,ID為1的聯系人信息
- ACTION_DIAL:tel:119 :表達撥打電話給119
上面兩個例子的URI并不一樣,完整的URI格式為scheme://host:port/path。
- 次要信息:除了主要標記的信息,Intent還可以附加很多額外的信息,比如Category,Type,Componten和Extra:
- Category:標識Intent的類別,譬如CATEGROY_LAUNCHER標識對屬于左面的圖標這一類的對象執行操作
- Type:標識Intent所有操作的數據類型,就是MIMEType,譬如要操作PNG圖片,那Type就可以設置為png
- Component:標識Intent要操作的對象
- Extra:標識Intent所傳遞的數據
上述這些數據都實現了Parcelable接口。
之所以Intent信息的主次之分,是因為解析Intent的規則需要有一個依據,主要信息是最能表達意圖的,而次要信息則是解析規則的一個補充。這就像大家在做自我介紹的時候,總是先說姓名、籍貫這些主要的信息,再額外補充愛好、特長這些次要信息,這樣一來在和其他人交朋友的時候,其他人就可以先根據籍貫、姓名鎖定我。如果我們只介紹愛好、特長,那么別人鎖定的范圍就比較廣,因為有相同愛好或者特長的人比較多。
Intent身份信息,其實就是Android的一種設計語言,譬如"打電話給119",只需要發出Action為ACTION_DIAL,URI為“tel:119”的Intent即可,剩下的就交給Android系統去理解這個意圖。任何組件只要按照規則發生,都會被Android系統正確的理解。
而根據Intent的方式不同,可以將Intent分為兩類:
- 顯示(Explicit):明確指明需要誰來響應Intent。這一類Intent的解析過程比較簡單
- 隱式(Implicit):有系統找出合適的目標來響應Intent。這一類Intent的解析過程比較復雜,由于目標明確,所以需要經過層層篩選才能找到最合適的響應者。
之所以Intent有顯式和隱式之分,是因為解析Intent的方式不同,如果我指定要和某某交朋友,那么發出的這一類請求,就是顯式Intent;如果沒有指定交朋友的對象,只是說找到跟我愛好相同的人,那發出的這一類請求,就是隱式的Intent。對待這兩種Intent顯然有不同的解析方式。
如果和"運行環境Context"一樣,Intent也是面向應用程序設計,同樣是弱化了了進程的概念。應用程序只需表明"我想要什么",不需要關心索要的東西在什么地方,如何找到索要的東西。Intent是Android通信的手段之一,可以承載要傳遞的信息,至于信息怎么從發起進程傳遞到目標進程,應用程序可以毫不關心。
Intent最后一個響應者是一個Android組件,Android組件都可以定義IntentFilter,前面說了包解析器的時候,說到了每一個Component類中都有一個IntentInfo對象的數組,而IntentInfo則是IntentFilter的子類。既然一個Android組件可以定義多個IntentFilter,那么Intent想要匹配到最終的組件,則需要通過組件所定義的所有IntentFilter:
多個IntentFilter之間是"或"的關系,哪怕其他所有IntentFilter都匹配失敗,只要有一個IntentFilter通過,最終Intent還是找到了可以響應的組件。
每一個IntentFilter就像是一個定義了白名單規則的過濾器,只有滿足白名單的要求才會放行。Intent的過濾規則,其實就是針對Intent的身份信息的匹配規則,當Intent的身份信息與IntentFilter所規定的要求匹配上,則允許通過;否則,Intent就被過濾掉了。IntentFilter的過濾規則包含以下三個方面:
- Action:每個IntentFilter可以定義零個或多個<action標簽>,如果Intent想要通過這個IntentFilter,則Intent所轄的Action需要匹配其中至少一個。
- Category:每一個IntentFilter可以定義零個或者多個<category>標簽,如果Intent想要通過這個IntentFilter,則Intent所轄的Categroy必須是IntentFilter所定義的Category的子集,才能通過IntentFileter。譬如Intent設置了兩個Category:CATEGORY_LAUNCHER和CATEGORY_MAIN,只有那些至少定義了兩項Category的IntentFilter,才會放行該Intent。啟動Activity時,會為Intent設置默認的Category,即CATEGORY_DEFAULT。目標Activity需要添加一個category偽CATEGORY_DEFAULT的IntentFilter來匹配這一類隱式的Intent。
- Data:每一個IntentFilter可以定義零個或多個<data>,數據可以通過類型(MIMEType)和位置(URI)來描述,如果Intent想要通過這個IntentFilter,則Intent所轄的Data需要匹配其中至少一個。
在了解Intent的身份信息和IntentFilter的規則定義之后,就可以介紹Intent解析的過程了,PackageManagerService有四大組件的Intent解析器,分別是ActivityIntentResolver用于解析發往Activity或Broadcast的Intent,ServiceIntentResolver用于解析發往Service的Intent,ProviderIntentResolver用于解析發往Provider的Intent,系統每收到一個Intent的解析請求時,就會使用對應的解析器,他們都是IntentResolver的子類。
IntentResolver的職能就是解析Intent,它包含了所有IntentFilter,同時有一個重要的成員函數queryIntent(),接受Intent作為參數,返回查詢結構:一個ResolveInfo對象的數據。因為可能有多個組件來響應一個Intent,所以返回結果是一個數組。可想而知,該函數就是針對輸入的Intent,按照前面所述的過濾規則,逐個與IntentFilter進行匹配,直到找到最終的響應者,便加入返回結果的列表。
- ResolveInfo是最終的Intent解析結果的數據結構,并不復雜,就是各類組件信息的一個包裝。需要注意的是,其中的組件信息ActivityInfo、ProviderInfo、ServiceInfo只有一個不為空,這樣就可以區分不同組件的解析結果。
- 解析"顯式"的Intent,如果Intent中有設置Component,則說明已經顯式的指明由誰來響應Intent。根據Component可以獲取到對應的 ActivityInfo,再封裝成ResolveInfo便可以作為結果返回了。
- 解析"隱式"的Intent,該處邏輯比較復雜,后面講解Activity的啟動流程時再詳細講解。
3、小結
- 包管理對外提供服務的形式基于Bidner機制,服務端是運行在系統進程中的PackageManagerService,包查詢服務是使用范圍很廣的一類服務,很多其他服務都需要用到包信息,都是通過PackageManagerService獲取的。
- 包查詢服務的核心是Intent解析,PackageManagerService中實現了不同組件的解析器。針對一個輸入的Intent,解析得到可以響應的組件。Android為此設計了IntentFilter機制,定義了Intent匹配規則,最終解析實現在IntentResolver.queryIntent()函數中
五、PackageManagerService的體系結構
大圖地址
六、總結
本片文章主要講解了包管理的三個大的過程:包掃描過程、包查詢過程、包安裝過程,其中重點的是包安裝的過程。我們再來復習一下:
APK的安裝流程如下:
復制APK安裝包到/data/app目錄下,解壓縮并掃描安裝包,向資源管理器注入APK資源,解析AndroidManifest文件,并在/data/data目錄下創建對應的應用數據目錄,然后針對Dalvik/ART環境優化dex文件,保存到dalvik-cache目錄,將AndroidManifest文件解析出的組件、權限注冊到PackageManagerService并發送廣播
具體流程如下:
├── PMS.installPackage()
└── PMS.installPackageAsUser()
|傳遞 InstallParams 參數
PackageHandler.doHandleMessage().INIT_COPY
|
PackageHandler.doHandleMessage().MCS_BOUND
├── HandlerParams.startCopy()
│ ├── InstallParams.handleStartCopy()
│ │ └──InstallArgs.copyApk()
│ └── InstallParams.handleReturnCode()
│ └── PMS.processPendingInstall()
│ ├── InstallArgs.doPreInstall()
│ ├── PMS.installPackageLI()
│ │ ├── PackageParser.parsePackage()
│ │ ├── PackageParser.collectCertificates()
│ │ ├── PackageParser.collectManifestDigest()
│ │ ├── PackageDexOptimizer.performDexOpt()
│ │ ├── InstallArgs.doRename()
│ │ │ └── InstallArgs.getNextCodePath()
│ │ ├── replacePackageLI()
│ │ │ ├── shouldCheckUpgradeKeySetLP()
│ │ │ ├── compareSignatures()
│ │ │ ├── replaceSystemPackageLI()
│ │ │ │ ├── killApplication()
│ │ │ │ ├── removePackageLI()
│ │ │ │ ├── Settings.disableSystemPackageLPw()
│ │ │ │ ├── createInstallArgsForExisting()
│ │ │ │ ├── deleteCodeCacheDirsLI()
│ │ │ │ ├── scanPackageLI()
│ │ │ │ └── updateSettingsLI()
│ │ │ └── replaceNonSystemPackageLI()
│ │ │ ├── deletePackageLI()
│ │ │ ├── deleteCodeCacheDirsLI()
│ │ │ ├── scanPackageLI()
│ │ │ └── updateSettingsLI()
│ │ └── installNewPackageLI()
│ │ ├── scanPackageLI()
│ │ └── updateSettingsLI()
│ ├── InstallArgs.doPostInstall()
│ ├── BackupManager.restoreAtInstall()
│ └── sendMessage(POST_INSTALL)
│ |
│ PackageHandler.doHandleMessage().POST_INSTALL
│ ├── grantRequestedRuntimePermissions()
│ ├── sendPackageBroadcast()
│ └── IPackageInstallObserver.onPackageInstalled()
└── PackageHandler.doHandleMessage().MCS_UNBIND
└── PackageHandler.disconnectService()
至此,整個APK安裝流程詳解全部說完,謝謝!