本文轉載於:http://www.iloveandroid.net/2016/06/20/Android_PackageManagerService-2/
前面介紹了PMS是如何啓動的,現在介紹Android系統是如何安裝一個APK的。
前面介紹PMS時,已經確定了PMS會註冊成爲一個service,而Android系統中需要使用一個service時,通常要找到其客戶端代理,然後通過其代理使用PMS提供的功能。
如上圖所示:
-
IPackageManager使用了Android的AIDL語言定義了server要提供的業務函數,然後AIDL編譯器會自動生成IPackageManager接口代碼,其子類Stub繼承Binder且實現了IPackageManager接口
-
PMS繼承Stub,所以可以作爲Server端
-
Stub中的一個內部類Proxy中有一個IBinder的成員變量mRemote,利用mRemote可以和Server端通信
-
client端在使用的時候是使用Context.getPackageManager函數返回的ApplicationPackageManager對象來處理,ApplicationPackageManager內部成員變量mPM指向Proxy類型的對象。也就意味着可以和PMS通信了。
這是一個典型的Binder服務模型,Android系統很多關鍵服務都是採用binder服務模型。最終上層獲取到的PMS的代理是PackageManager這個抽象類的實現類ApplicationPackageManager對象。
如何獲得PMS代理對象
使用下面的代碼,可以獲取到PMS的一個代理對象:
1 2 |
Context ct = getApplicationContext(); PackageManager pm = ct.getPackageManager(); |
其中getPackageManager源碼位置:
1
|
Android-6/frameworks/base/core/java/android/app/ContextImpl.java
|
實現代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public PackageManager getPackageManager() { if (mPackageManager != null) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); } return null; } |
ActivityThread.getPackageManager源碼位置:
1
|
Android-6/frameworks/base/core/java/android/app/ActivityThread.java
|
1 2 3 4 5 6 7 8 9 10 11 |
public static IPackageManager getPackageManager() { if (sPackageManager != null) { //Slog.v("PackageManager", "returning cur default = " + sPackageManager); return sPackageManager; } IBinder b = ServiceManager.getService("package"); //Slog.v("PackageManager", "default service binder = " + b); sPackageManager = IPackageManager.Stub.asInterface(b); //Slog.v("PackageManager", "default service = " + sPackageManager); return sPackageManager; } |
只要瞭解Android binder框架的,就會馬上發現,這無非就是向SM查詢名爲”package”的服務,SM查詢成功的話,返回名爲”package”服務的一個引用對象,然後使用asInterface將其轉換一個代理對象。
PMS在向SM註冊時,名字確實是”package”。
1
|
Android-6/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
|
1 2 3 4 5 6 7 |
public static PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); ServiceManager.addService("package", m); return m; } |
對PackageManager的調用,最終都轉換爲對PMS的調用。
安裝apk
可以使用如下代碼,調用去安裝一個apk文件已經在設備中的apk。
1 2 3 4 5 |
File apkfile = new File("/data/local/tmp/demo.apk"); Uri uri = Uri.fromFile(apkfile); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(uri,"application/vnd.android.package-archive"); startActivity(intent); |
源碼路徑:
1
|
Android-6/packages/apps/PackageInstaller
|
系統應用PackageInstaller將會響應這個intent.
它的AndroidMainifest.xml中:
1 2 3 4 5 6 7 8 9 10 11 |
<activity android:name=".PackageInstallerActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:excludeFromRecents="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.INSTALL_PACKAGE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" /> <data android:mimeType="application/vnd.android.package-archive" /> </intent-filter> ....... |
也就是說PackageInstallerActivity會真正響應這個intent.
PackageInstallerActivity中有兩個重要的成員
1 2 |
PackageManager mPm; PackageInstaller mInstaller; |
其中mPm是PMS的代理對象,mInstaller是PackageInstallerService的代理對象。PMS類之後內聚了PackageInstallerService,在PMS啓動的時候,初始化了改變量。
當PackageInstallerActivity這個Activity啓動的時候
1 2 3 4 5 6 7 |
protected void onCreate(Bundle icicle) { super.onCreate(icicle); mPm = getPackageManager(); mInstaller = mPm.getPackageInstaller(); .............. |
一開始便獲取到了這兩個對象。
繼續PackageInstallerActivity的onCreate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) { ............... } else { //走的這個分支 mSessionId = -1; mPackageURI = intent.getData(); mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER); } ....................... if ("package".equals(mPackageURI.getScheme())) { ................. } else { // 走的這個分支 mInstallFlowAnalytics.setFileUri(true); final File sourceFile = new File(mPackageURI.getPath()); PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile); // Check for parse errors if (parsed == null) { Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation"); showDialogInner(DLG_PACKAGE_ERROR); setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK); mInstallFlowAnalytics.setPackageInfoObtained(); mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO); return; } mPkgInfo = PackageParser.generatePackageInfo(parsed, null, PackageManager.GET_PERMISSIONS, 0, 0, null, new PackageUserState()); mPkgDigest = parsed.manifestDigest; as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile); } |
代碼邏輯還是比較好理解的,從intent獲取到要安裝的apk的路徑。調用PackageUtil.getPackageInfo解析。
其中PackageUtil.getPackageInfo:
1 2 3 4 5 6 7 8 9 10 |
public static PackageParser.Package getPackageInfo(File sourceFile) { final PackageParser parser = new PackageParser(); try { PackageParser.Package pkg = parser.parseMonolithicPackage(sourceFile, 0); parser.collectManifestDigest(pkg); return pkg; } catch (PackageParserException e) { return null; } } |
是不是有種熟悉的感覺,在PMS啓動的時候,掃描解析APK不就用到了PackageParser嗎。
1
|
Android-6/frameworks/base/core/java/android/content/pm/PackageParser.java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { if (mOnlyCoreApps) { .................... } final AssetManager assets = new AssetManager(); try { final Package pkg = parseBaseApk(apkFile, assets, flags); pkg.codePath = apkFile.getAbsolutePath(); return pkg; } finally { IoUtils.closeQuietly(assets); } } |
就是調用parseBaseApk來解析傳入的apk文件,這個過程實際上就是在解析apk中AndroidMainfest.xml文件,將其中的信息村粗,並把解析結果存入PackageParser.Package對象中返回。
然後將解析得到的Package對象,通過generatePackageInfo()方法轉換爲一個PackageInfo對象。
繼續PackageInstallerActivity的onCreate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Block the install attempt on the Unknown Sources setting if necessary. if (!requestFromUnknownSource) { initiateInstall(); return; } // If the admin prohibits it, or we're running in a managed profile, just show error // and exit. Otherwise show an option to take the user to Settings to change the setting. final boolean isManagedProfile = mUserManager.isManagedProfile(); if (!unknownSourcesAllowedByAdmin || (!unknownSourcesAllowedByUser && isManagedProfile)) { showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES); mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING); } else if (!unknownSourcesAllowedByUser) { // Ask user to enable setting first showDialogInner(DLG_UNKNOWN_SOURCES); mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING); } else { initiateInstall(); } |
requestFromUnknownSource爲true,因爲我們確實是從未知來源安裝的,沒有通過內置的應用商店安裝。
這裏做一些檢查,例如如果沒有在設置中打開允許安裝位置來源,這樣就要彈出一個提示框,然後用戶去設置中打開允許位置來源安裝。
最終都是調用initiateInstall這個方法的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
private void initiateInstall() { String pkgName = mPkgInfo.packageName; // Check if there is already a package on the device with this name // but it has been renamed to something else. String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName }); if (oldName != null && oldName.length > 0 && oldName[0] != null) { pkgName = oldName[0]; mPkgInfo.packageName = pkgName; mPkgInfo.applicationInfo.packageName = pkgName; } // Check if package is already installed. display confirmation dialog if replacing pkg try { // This is a little convoluted because we want to get all uninstalled // apps, but this may include apps with just data, and if it is just // data we still want to count it as "installed". mAppInfo = mPm.getApplicationInfo(pkgName, PackageManager.GET_UNINSTALLED_PACKAGES); if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { mAppInfo = null; } } catch (NameNotFoundException e) { mAppInfo = null; } mInstallFlowAnalytics.setReplace(mAppInfo != null); mInstallFlowAnalytics.setSystemApp( (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)); startInstallConfirm(); } |
代碼邏輯也很簡單,主要是判斷當前系統中是否已經安裝這個app,安裝過的話,設置替換flag等信息,然後調用startInstallConfirm,會彈出一個對話框,詢問是否需要安裝此應用嗎,以及這個app將獲得哪些權限等。
因爲前面已經解析過該apk了,所以顯示其有哪些權限就很簡單。
當點擊確定按鈕的時候,執行下面的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public void onClick(View v) { if (v == mOk) { if (mOkCanInstall || mScrollView == null) { mInstallFlowAnalytics.setInstallButtonClicked(); if (mSessionId != -1) { ................. } else { // 走這個分支 startInstall(); } } else { mScrollView.pageScroll(View.FOCUS_DOWN); } } else if(v == mCancel) { ........................ } } |
主要工作由startInstall()完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
private void startInstall() { // Start subactivity to actually install the application Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); newIntent.setClass(this, InstallAppProgress.class); newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest); newIntent.putExtra( InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics); String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME); if (mOriginatingURI != null) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI); } if (mReferrerURI != null) { newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI); } if (mOriginatingUid != VerificationParams.NO_UID) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid); } if (installerPackageName != null) { newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName); } if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); } if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); startActivity(newIntent); finish(); } } |
這裏創建一個Intent,設置數據和哪個發送給哪個class,調用 startActivity(newIntent),啓動InstallAppProgress.