PackageInstaller 原理簡述
應用安裝是智能機的主要特點,即用戶可以把各種應用(如游戲等)安裝到手機上,並可以對其進行卸載等管理操作。APK是Android Package的縮寫,即Android安裝包。APK是類似Symbian Sis或Sisx的文件格式。通過將APK文件直接傳到Android模擬器或Android手機中執行即可安裝。
Android應用安裝有如下四種方式
1. 系統應用安裝――開機時完成,沒有安裝界面
2. 網絡下載應用安裝――通過market應用完成,沒有安裝界面
3. ADB工具安裝――沒有安裝界面。
4. 第三方應用安裝――通過SD卡里的APK文件安裝,有安裝界面,由packageinstaller.apk應用處理安裝及卸載過程的界面。
應用安裝的流程及路徑
應用安裝涉及到如下幾個目錄:
system/app 系統自帶的應用程序,無法刪除
data/app 用戶程序安裝的目錄,有刪除權限。
安裝時把apk文件復制到此目錄
data/data 存放應用程序的數據
Data/dalvik-cache 將apk中的dex文件安裝到dalvik-cache目錄下(dex文件是dalvik虛擬機的可執行文件,其大小約為原始apk文件大小的四分之一)
安裝過程:復制APK安裝包到data/app目錄下,解壓並掃描安裝包,把dex文件(Dalvik字節碼)保存到dalvik-cache目錄,並data/data目錄下創建對應的應用數據目錄。
卸載過程:刪除安裝過程中在上述三個目錄下創建的文件及目錄。
一、系統應用安裝:
PackageManager Service處理各種應用的安裝,卸載,管理等工作,開機時由systemServer啟動此服務
(源文件路徑:android/frameworks/base/services/java/com/android/server/ PackageManagerService.java)
PackageManager Service服務啟動的流程:
1. 首先掃描安裝“system/framework”目錄下的jar包
1. scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM,
scanMode | SCAN_NO_DEX);
2.第二步掃描安裝“system/app”目錄下的各個系統應用
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
3.第三步掃描“data/app”目錄,即用戶安裝的第三方應用
scanDirLI(mAppInstallDir, 0, scanMode);
4.第四步掃描" data/app-private"目錄,即安裝DRM保護的APK文件(目前沒有遇到過此類的應用)。
scanDirLI(mDrmAppPrivateInstallDir, 0, scanMode | SCAN_FORWARD_LOCKED);
安裝應用的過程
1.scanDirLI(File dir, int flags, int scanMode) 遍歷安裝指定目錄下的文件
2.scanPackageLI(File scanFile,
File destCodeFile, File destResourceFile, int parseFlags,
int scanMode) 安裝package文件
3.scanPackageLI(
File scanFile, File destCodeFile, File destResourceFile,
PackageParser.Package pkg, int parseFlags, int scanMode)
通過解析安裝包parsePackage獲取到安裝包的信息結構
4.mInstaller.install(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.uid); 實現文件復制的安裝過程
(源文件路徑:frameworks/base/cmds/installd/installd.install)
二、從market上下載應用:
Google Market應用需要使用gmail賬戶登錄才可以使用,選擇某一應用后,開始下載安裝包,此過程中,在手機的信號區有進度條提示,下載完成后,會自動調用Packagemanager的接口安裝,調用接口如下:
public void installPackage(final Uri packageURI, final IPackageInstallObserver observer, final int flags)
final Uri packageURI:文件下載完成后保存的路徑
final IPackageInstallObserver observer:處理返回的安裝結果
final int flags:安裝的參數,從market上下載的應用,安裝參數為-r (replace)
installPackage接口函數的安裝過程:
1.public void installPackage(
final Uri packageURI, final IPackageInstallObserver observer, final int flags,
final String installerPackageName)
final String installerPackageName:安裝完成后此名稱保存在settings里,一般為null,不是關鍵參數
2.File tmpPackageFile = copyTempInstallFile(packageURI, res);
把apk文件復制到臨時目錄下的臨時文件
3.private void installPackageLI(Uri pPackageURI,
int pFlags, boolean newInstall, String installerPackageName,
File tmpPackageFile, PackageInstalledInfo res)
解析臨時文件,獲取應用包名pkgName = PackageParser.parsePackageName(
tmpPackageFile.getAbsolutePath(), 0);
4.判斷如果帶有參數INSTALL_REPLACE_EXISTING,則調用replacePackageLI(pkgName,
tmpPackageFile,
destFilePath, destPackageFile, destResourceFile,
pkg, forwardLocked, newInstall, installerPackageName,
res)
5.如果沒有,則調用installNewPackageLI(pkgName,
tmpPackageFile,
destFilePath, destPackageFile, destResourceFile,
pkg, forwardLocked, newInstall, installerPackageName,
res);
6.private PackageParser.Package scanPackageLI(
File scanFile, File destCodeFile, File destResourceFile,
PackageParser.Package pkg, int parseFlags, int scanMode)
scanPackageLI以后的流程,與開機時的應用安裝流程相同。
三、從ADB工具安裝
Android Debug Bridge (adb) 是SDK自帶的管理設備的工具,通過ADB命令行的方式也可以為手機或模擬器安裝應用,其入口函數源文件為pm.java
(源文件路徑:android/frameworks/base/cmds/pm/src/com/android/commands/pm/pm.java)
ADB命令行的形式為adb install <path_to_apk> ,還可以帶安裝參數如:"-l" "-r" "-i" "-t"
函數runInstall()中判斷參數
"-l"――INSTALL_FORWARD_LOCK
"-r"—— INSTALL_REPLACE_EXISTING
"-i" ——installerPackageName
"-t"——INSTALL_ALLOW_TEST
我們常用的參數為-r,表示覆蓋安裝手機上已安裝的同名應用。從market上下載的應用,也是直接傳入這個參數安裝的。
runInstall與market調用同樣的接口完成應用安裝。
public void installPackage(android.net.Uri packageURI, android.content.pm.IPackageInstallObserver observer, int flags, java.lang.String installerPackageName)
四、第三方應用安裝――通過SD卡里的APK文件安裝
把APK安裝包保存在SD卡中,從手機里訪問SD卡中的APK安裝包,點擊就可以啟動安裝界面,系統應用Packageinstaller.apk處理這種方式下的安裝及卸載界面流程,如下圖:
PackageInstallerActivity負責解析包,判斷是否是可用的Apk文件
創建臨時安裝文件/data/data/com.android.packageinstaller/files/ApiDemos.apk
並啟動安裝確認界面startInstallConfirm,列出解析得到的該應用基本信息。如果手機上已安裝有同名應用,則需要用戶確認是否要替換安裝。
確認安裝后,啟動InstallAppProgress,調用安裝接口完成安裝。
pm.installPackage(mPackageURI, observer, installFlags);
其它:
1. PackageManagerService.java的內部類AppDirObserver實現了監聽app目錄的功能:當把某個APK拖到app目錄下時,可以直接調用scanPackageLI完成安裝。
2.手機數據區目錄“data/system/packages.xml”文件中,包含了手機上所有已安裝應用的基本信息,如安裝路徑,申請的permission等信息。
------------------------------------------------------華麗的分割線---------------------------------------------------------------
PackageManagerService注記
PackageManagerService的方法大致可以分成這樣幾類,
1)從apk, xml中載入pacakge信息, 存儲到內部成員變量中, 用於后面的查找. 關鍵的方法是scanPackageLI().
2)各種查詢操作, 包括query Intent操作.
3)install package和delete package的操作. 關鍵的方法是installPackageLI().
4)其它操作, 包括permission, signature, freeStorage等等.
首先看看PacakgeManagerService用到的幾個重要的支撐類, 然后羅列幾個PackageManagerService的幾個關鍵內部數據成員變量. 最后注記幾個關鍵流程.
PackageManagerService用到了幾個重要的支撐類:
PackageParser, 這個類主要用於解析apk, 分析其AndroidManifest.xml得到package的各種信息. 前已有注記文章,此不贅述. 特別的PackageParser.Package這個類用於容納解析出的信息.
PackageManagerService.Settings, 用於容納當前有效的package信息, 它是動態的. 例如, user id, shareUser, permission, signature以及origPackage(也就是mRenamedPackages)相關信息. 所謂的install package就包括從要安裝的package中抽取信息更新PackageManagerService.Settings中的內容. 特別的, Settings針對shareUser和origPackage做了特別的關照. 另外, 為了加速啟動速度, Settings的內容會寫入到/data/system/packages.xml, packages-backup.xml, 和packages.list中, 下次啟動時會直接載入.
Installer, 這個類協助安裝過程, 更多的是將針對文件/路徑的操作放到了c/cpp實現. 真正的工作是是由installd承擔的, Installer只是通過named socket "installd"連接installd, 使用簡單的cmd-respond協議指揮installd完成工作. 在其'install'命令中可以看到, 其實只是創建了/data/data/<pkgName>目錄而已.
PackageManagerService中的幾個關鍵成員變量:
// (pkgName => Package), package is the one installed.
final HashMap<String, PackageParser.Package> mPackages =
new HashMap<String, PackageParser.Package>();
// current package settings info, such as userid, origPackage
// , shareUser, permission, signature, etc
final Settings mSettings;
// (system uid => permission), permissions read from /system/etc/permissions/<files> are stored here.
// especially /system/etc/permissions/platform.xml
final SparseArray<HashSet<String>> mSystemPermissions =
new SparseArray<HashSet<String>>();
//(pkgName => sharedLib), corresponding to <library> tag
final HashMap<String, String> mSharedLibraries =
new HashMap<String, String>();
// All available activities, for resolving intent
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
// All available receivers, for resolving intent
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
// All available services, for resolving intent
final ServiceIntentResolver mServices =
new ServiceIntentResolver();
幾個關鍵流程,
初始化過程
--------------
PackageManagerService由SystemServer在創建ActivityManagerService后調用main創建, 是單實例的.
Slog.i(TAG, "Power Manager");
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);
Slog.i(TAG, "Activity Manager");
context = ActivityManagerService.main(factoryTest);
Slog.i(TAG, "Telephony Registry");
ServiceManager.addService("telephony.registry", new TelephonyRegistry(context));
AttributeCache.init(context);
Slog.i(TAG, "Package Manager");
pm = PackageManagerService.main(context,
factoryTest != SystemServer.FACTORY_TEST_OFF);
ActivityManagerService.setSystemProcess();
mContentResolver = context.getContentResolver();
在構造函數中, PackageManagerService會做這些工作,
1)啟動自己的handlerThread, 生成自己的mHandler.
2)從/system/etc/permissions/的所有xml文件中, 尤其是platform.xml, 讀入systemPermissions. 這些是系統默認的permission配置.
3)掃描/system/framework/, /system/app/, /data/app/, 和/data/app-private/下的apk文件, 收集package各種信息, 更新到內部成員變量中. 這些將在PackageManagerService執行各種功能時用到. 尤其是query intent.
更詳盡的筆記,
PackageManagerService
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
readPermissions();
mRestoredSettings = mSettings.readLP();
--/system/framework/
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_NO_DEX);
--/system/app/
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode);
--Prune any system packages that no longer exist.
--clean up any incomplete package installations
--delete tmp files
--/data/app/
scanDirLI(mAppInstallDir, 0, scanMode);
--/data/app-private/
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode);
mSettings.writeLP();
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanMode)
PackageParser pp = new PackageParser(scanPath);
pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags);
collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)
setApplicationInfoPaths(pkg, codePath, resPath);
scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
--set mAndroidApplication and mResolveActivity to'Android' package
--Check all shared libraries and map to their actual file path.
--check pkg.usesLibraries are contained in mSharedLibraries.
--fill in pkg.usesLibraryFiles according to pkg.usesLibraries and pkg.usesOptionalLibraries
--check pkg.reqFeatures in mAvailableFeatures
--if not in mSettings, create one ShareUserSettings and insert into mSettings.
--Check and note if we are renaming from an original package name
pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.flags, true, false);
verifySignaturesLP(pkgSetting, pkg);
--Verify that this new package doesn't have any content providers that conflict with existing packages.
--get data dir. if not exists, install or create the data dir. if exists but uid not correct, reinstall.
--Perform shared library installation and dex validation and optimization, if this is not a system app.
--Request the ActivityManager to kill the process(only for existing packages)
mSettings.insertPackageSettingLP(pkgSetting, pkg); --Add the new setting to mSettings
mPackages.put(pkg.applicationInfo.packageName, pkg);
--set mProvidersByComponent and mProviders according to pkg.providers
--set mServices according to pkg.services
--set mReceivers according to pkg.receivers
--set mActivites according to pkg.activities
--set mPermissionGroups according to pkg.permissionGroups
--set mSettings.mPermissionTrees or mSettings.mPermissions according to pkg.permissions
--set mInstrumentation according to pkg.instrumentation
--set mProtectedBroadcasts according to pkg.protectedBroadcasts
install package過程
--------------
install package的入口是installPackage(). install package通常是個耗時的過程, 因此會使用到android的handler機制.
首先, 參數封裝成INIT_COPY message, 發到handlerThread.
handlerThread收到message后, 將參數排隊到mPendingInstalls中. 隨后,MCS_BOUND流程將會處理這個隊列, 執行安裝.
MCS_BOUND的整個安裝流程借助了幾個InstallParams和InstallArgs完成其中的參數和安裝結果的傳遞. 最終會調用processPendingInstall(), 進而調用到install過程的核心 installPackageLI().
installPackageLI()的復雜性很大程度上是考慮了1)新安裝還是升級情況, 2)origPackage情況
-----------------------------------華麗的分割線--------------------------------------------------------------------
PackageParser和AndroidManifest.xml注記
apk包中的AndroidManifest.xml文件包含了package的各種描述信息. 分析和獲取這些信息的工作是由PackageParser完成的. 這里簡述之. 首先我們先羅列AndroidManifest.xml文件的簡要結構, 其次在大致整理PackageParser的邏輯.
AndroidManifest.xml的簡要結構
====================================
AndroidManifest.xml的內容在frameworks/base/core/res/res/values/attrs_manifest.xml中定義. 這里簡要羅列, 按照層進關系, xml element用 "tagName, Name"的形式標記, 對於某些xml element的重要屬性(attribute), 用"attr NAME"標記.
AndridManifest.xml
manifest, AndroidManifest
attr versionCode
attr versionName
attr sharedUserId
attr sharedUserLabel
attr installLocation
permission, AndroidManifestPermission
attr name, label, icon
attr permissionGroup
attr protectionLevel
permission-group, AndroidManifestPermissionGroup
attr name, label, icon
permission-tree, AndroidManifestPermissionTree
attr name, label, icon
uses-sdk, AndroidManifestUsesSdk
uses-permission, AndroidManifestUsesPermission
attr name
uses-configuration, AndroidManifestUsesConfiguration
attr reqTouchScreen
attr requKeyboardType
attr reqHardKeyboard
attr reqNavigation
attr reqFiveWayNav
application, AndroidManifestApplication
attr name, label, icon
attr permission
attr process
attr taskAffinity
attr persistent
service, AndroidManifestService
attr name, label, icon
attr permission
attr process
attr enabled
attr exported
receiver, AndroidManifestReceiver
attr name, label, icon
attr permission
attr process
attr enabled
attr exported
provider, AndroidManifestProvider
attr name, label, icon
attr process, authorities, syncable
attr readPermission, writePermission, grantUriPermissions
attr permission
attr mulitprocess
attr enabled
attr exported
grant-uri-permission, AndroidManifestGrantUriPermission
attr path, pathPrefix, pathPattern
path-permission, AndroidManifestPathPermission
attr path, pathPrefix, pathPattern
attr permission, readPermission, writePermission
activity, AndroidManifestActivity
attr name, label, icon
attr theme, launchMode, screenOrientation
attr configChanges, permission, multiprocess
attr process, taskAffinity, allowTaskReparenting
attr finishOnTaskLaunch, finishOnCloseSystemDialogs
attr clearTaskOnLauch, noHistory
attr alwaysRetainTaskState, stateNotNeeded
attr excludeFromRecents
attr enabled, exported
attr windowSoftInputMode
activity-alias, AndroidManifestActivityAlias
attr name, label, icon
attr targetActivity
attr permission
attr enabled, exported
uses-library, AndroidManifestUsesLibrary
attr name
attr required
instrumentation, AnroidManifestInstrumentation
attr name, label, icon
attr targetPackage, handleProfiling
attr functionalTest
uses-feature, AndroidManifestUsesFeature
attr glEsVersion
attr name
attr required
supports-screens, AndroidManifestSupportsScreens
attr smallScreens, normalScreens, largeScreens
attr resizeable, anyDensity
protected-broadcast, AndroidManifestProtectedBroadcast
attr name
adopt-permissions, AndroidManifestOriginalPackage
attr name
還有幾個特別的xml element, 可以有多個parent element.
application
activity, receiver, provider, service
permission, permissionGroup
instrumentation
meta-data, AndroidManifestMetaData
attr name, value, resource
activity, receiver, service
intent-filter, AndroidManifestIntentFilter
attr label, icon, priority
action, AndroidManifestAction
attr name
data, AndroidManifestData
attr mimeType, scheme, host
attr port, path, pathPrefix
attr pathPattern
category, AndroidManifestCategory
attr name
intent, Intent
attr action, data, mimeType
attr targetPackage, targetClass
category, IntentCategory
attr name
extra, Extra
attr name, value
PackageParser的邏輯
====================================
PackageParser的parsePackage()方法會讀取apk包中的AndroidManifest.xml文件, 調用各子par***XX()方法, 解析出包信息. 同時, PackageParser定義了一些class, 容納這些解析得到的信息. 下面是對應的關系.
class xml elements
------------- --------------------------------------
Package package
Permission permission, permission-tree
PermissionGroup permission-group
Activity activity, activity-alias, receiver
Service service
Provider provider
Instrumentation instrumentation
ActivityIntentInfo intent-filter @ activity or receiver
ServiceIntentInfo intent-filter @ service
PacakgeParser的結果主要被PackageManagerService使用.