9.1.4 解析和安装pkg
scanPackageLI第四阶段调用scanPackageLI的重载方法scanPackageLI(PackageParser.Package,……),负责对pkg做具体的解析和安装,代码如下:
private PackageParser.Package scanPackageLI(File scanFile,
int parseFlags, int scanMode, long currentTime){
……//省略前三个阶段内容
//调用scanPackageLI(PackageParser.Package,……)重载方法
PackageParser.Package scannedPkg=scanPackageLI(pkg, parseFlags,
scanMode|SCAN_UPDATE_SIGNATURE, currentTime);
if(shouldHideSystemApp){
synchronized(mPackages){
grantPermissionsLPw(pkg, true);
mSettings.disableSystemPackageLPw(pkg.packageName);
}
}
return scannedPkg;
scanPackageLI(PackageParser. Package,……)方法以第三阶段经过过滤的PackageParser.Package为参数,其返回结果仍然是PackageParser.Package类型的scannedPkg。
scanPackageLI(PackageParser. Package,……)方法有近900行代码,接下来分步骤分析该方法的执行流程。
步骤1 判断pkg.mScanPath、pkg.applicationInfo.sourceDir、pkg.applicationInfo.publicSourceDir在上一阶段是否正确设置,如果没有设置,直接返回null。
步骤2 如果是系统级APK,则将FLAG_SYSTEM添加到pkg.applicationInfo.flags标记中。
步骤3 判断pkg的packageName是否为android,包名为android的APK即frameworks-res.apk。
对于frameworks-res.apk,要做如下处理:
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity=new ActivityInfo();
final ResolveInfo mResolveInfo=new ResolveInfo();
ComponentName mResolveComponentName;
PackageParser.Package mPlatformPackage;
……
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime){
……
if(pkg.packageName.equals("android")){
synchronized(mPackages){
if(mAndroidApplication!=null){//设置一次,初始为null
……
return null;
}
//将pkg存入PackageManagerService.mPlatformPackage中
mPlatformPackage=pkg;
pkg.mVersionCode=mSdkVersion;
//将pkg.applicationInfo信息存入mAndroidApplication
mAndroidApplication=pkg.applicationInfo;
mResolveActivity.applicationInfo=mAndroidApplication;
//ResolverActivity.name相当于activity标签的android:name
mResolveActivity.name=ResolverActivity.class.getName();
mResolveActivity.packageName=
mAndroidApplication.packageName;//android
mResolveActivity.processName=
mAndroidApplication.processName;//system
……
//将ResolverActivity的ActivityInfo信息存入mResolveInfo
mResolveInfo.activityInfo=mResolveActivity;
……
mResolveComponentName=new ComponentName(
mAndroidApplication.packageName,
mResolveActivity.name);
}
}
……
以上代码用于设置ResolverActivity,处理一个Intent可以被多个Activity匹配处理的情况下,用户自选Activity的问题。PackageManagerService中查找匹配某个Intent的Activity时,会返回ResolveInfo类型的结果,ResolveInfo.activityInfo中便存储了匹配该Intent的Activity信息。
framework-res. apk在AndroidManifest.xml中声明了application标签,因此上述代码才能使用其pkg.applicationInfo信息,相关部分的代码如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android"coreApp="true"
android:sharedUserId="android.uid.system"
android:sharedUserLabel="@string/android_system_label">
……
<application android:process="system"……>
<activity android:name="com.android.internal.app.ChooserActivity"
android:theme="@style/Theme.Holo.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:multiprocess="true">
<intent-filter>
<action android:name="android.intent.action.CHOOSER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
……
</application>
</manifest>
步骤4 如果PackageManagerService.mPackages或者mSharedLibraries中已经存在该包的信息,说明该包已经安装过。此时直接返回null,不再执行后续处理。
步骤5 根据pkg.applicationInfo.sourceDir和pkg.applicationInfo.publicSourceDir的值初始化安装包及其资源文件的路径信息。
步骤6 判断是否为系统级应用,如果不是,则将pkg.mOriginalPackages、pkg.mRealPackage、pkg.mAdoptPermissions赋值为null。判断是否为系统级应用的依据是其flags标记中是否设置了FLAG_SYSTEM。
进入synchronized(mPackages)同步代码块,执行步骤7~步骤12的操作。
步骤7 检查并映射共享库。
如果pkg.usesLibraries或者pkg.usesOptionalLibraries不为null,首先查询PackageManagerService.mSharedLibraries成员变量中是否存在pkg.usesLibraries中声明使用的共享库。如果不存在该共享库,说明APK中声明使用了一个系统不存在的共享库,此时需要输出错误信息并返回,否则便将共享库信息存入PackageManagerService.mTmpSharedLibraries中。
然后查询PackageManagerService.mSharedLibraries成员变量中是否包含pkg.usesOptionalLibraries中声明使用的共享库。如果缺少某个共享库,则输出警告信息(但不返回)。这样便建立了共享库与其路径之间的映射,最后将这种映射关系存入pkg.usesLibraryFiles变量中。
步骤8 检查并分配APK声明的ShareUserId。
如果pkg.mSharedUserId!=null,则调用mSettings.getSharedUserLPw方法申请指定的共享uid。代码如下:
//参数依次为:pkg.mSharedUserId, pkg.applicationInfo.flags, true
SharedUserSetting getSharedUserLPw(String name, int pkgFlags, boolean create){
SharedUserSetting s=mSharedUsers.get(name);
if(s==null){//如果mSharedUsers中不存在该共享用户信息则创建一个
if(!create){
return null;
}
//创建共享用户ID信息SharedUserSetting
s=new SharedUserSetting(name, pkgFlags);
//分配一个新的UID
s.userId=newUserIdLPw(s);
if(s.userId>=0){
mSharedUsers.put(name, s);//存入mSharedUsers中
}
}
return s;
}
getSharedUserLPw方法首先从Settings.mSharedUsers成员变量中查找是否存在指定的共享UID信息,如果不存在,则创建共享UID信息,该信息以SharedUserSetting表示。创建共享UID信息后,需要为其指定一个user ID,指定user ID由newUserIdLPw方法完成,代码如下:
private int newUserIdLPw(Object obj){
//获取mUserIds的大小
final int N=mUserIds.size();
for(int i=0;i<N;i++){
//找到mUserIds的第一个空位
if(mUserIds.get(i)==null){
mUserIds.set(i, obj);//将SharedUserSetting存入第一个空位
/*FIRST_APPLICATION_UID(10000)加SharedUserSetting的
索引位置便是新的共享UID/
return Process.FIRST_APPLICATION_UID+i;
}
}
//如果没空位置插入,返回错误
if(N>(Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID))
{
return-1;
}
mUserIds.add(obj);
return Process.FIRST_APPLICATION_UID+N;//返回最大UID
}
newUserIdLPw首先将新创建的UID信息存入mSettings.mUserIds的第一个空位置,并以该位置的偏移量i加上FIRST_APPLICATION_UID得到新的user ID返回。
如果mSettings.mUserIds已满,说明user ID已经分配完毕,无法指定新的user ID,此时直接返回-1。FIRST_APPLICATION_UID定义了应用程序的起始UID,其值为10000。MAX_APPLICATION_UIDS定义应用程序可分配UID的最大个数,默认值为1000。
步骤9 处理Original Packages。
如果AndroidManifest.xml中指定了original-package标签,需更改pkg.mOriginalPackages指定的包名,即新包沿用旧包的包名。这里需要与mSettings.mRenamedPackages做比较。
步骤10 调用verifySignaturesLP验证签名。
如果签名验证不通过,即verifySignaturesLP返回false。此时需要做出如下处理:
如果该包没有设置PackageParser.PARSE_IS_SYSTEM_DIR标记,则直接返回null,不再进入后续处理;否则,首先用pkg.mSignatures的新签名信息更新旧的签名信息,然后判断该包是否以共享用户的方式运行,如果是,需要比较共享用户的签名,如果比较失败,同样返回null,不再执行后续处理。
步骤11 检查新包与已安装包的Content Provider是否冲突。
检查pkg.providers中存储的provider是否已经存在于PackageManagerService的mProviders变量成员中,如果已经存在,则记录一个错误信息并返回null。
步骤12 如果当前扫描的包需要采用其他包中定义的权限,则抽取相应包中定义的权限。
抽取权限是根据pkg.mAdoptPermissions的内容判断的,代码如下:
if(pkg.mAdoptPermissions!=null){
for(int i=pkg.mAdoptPermissions.size()-1;i>=0;i—){
final String origName=pkg.mAdoptPermissions.get(i);
/在mSettings.mPackages中查找origName/
final PackageSetting orig=mSettings.peekPackageLPr(origName);
if(orig!=null){
/验证能否adopt permissions/
if(verifyPackageUpdateLPr(orig, pkg)){
/抽取permissions/
mSettings.transferPermissionsLPw(origName, pkg.packageName);
}
}
}
}
在抽取权限前,首先要调用verifyPackageUpdateLPr方法判断能否抽取权限,代码如下:
private boolean verifyPackageUpdateLPr(PackageSetting oldPkg,
PackageParser.Package newPkg){
if((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM)==0){
return false;//非系统级package无法抽取权限
}else if(mPackages.get(oldPkg.name)!=null){
//如果PackageManagerService.mPackages中存在该包也无法抽取权限
return false;
}
return true;
}
下面分析权限的抽取和易主过程,代码如下:
void transferPermissionsLPw(String origPkg, String newPkg){
for(int i=0;i<2;i++){
//遍历mPermissionTrees和mPermissions
HashMap<String, BasePermission>permissions=
i==0?mPermissionTrees:mPermissions;
for(BasePermission bp:permissions.values()){
//找到指定需要抽取权限的包
if(origPkg.equals(bp.sourcePackage)){
//更改该权限的属主信息
bp.sourcePackage=newPkg;
bp.packageSetting=null;
bp.perm=null;
if(bp.pendingInfo!=null){
bp.pendingInfo.packageName=newPkg;
}
bp.uid=0;
bp.gids=null;
}
}
}
}
经过上述步骤的预处理过程,现在便可以真正进入安装过程了。接下来执行步骤13~步骤24,安装APK。
步骤13 创建数据目录。
在安装APK之前首先需要创建其数据目录。对于android包,需要把/data/system的路径赋值给pkg.applicationInfo.dataDir。对于其他包,需要把/data/data/{packageName}的路径赋值给pkg.applicationInfo.dataDir,其中packageName根据具体的包名变化。
如果非android包的数据路径不存在,需调用Installer.install和Installer.createUserData安装该APK;如果数据路径已经存在,需要检查数据目录的所有者UID,如果该UID与pkg.applicationInfo.uid记录的UID匹配,则将所有者数据目录存入pkg.applicationInfo.dataDir,如果不匹配,需要做出如下处理:
1)如果所有者UID为root,则调用Installer.fixUid方法将其还原到pkg.applicationInfo.uid指定的UID,然后将recovered赋值为true。
2)如果recovered为false,并且为系统级App或者设置了SCAN_BOOTING扫描模式,需要首先调用Installer.remove删除其数据,然后调用Installer.install和Installer.createUserData重新建立其数据目录。
3)其他情况下,直接返回null。
完成上述操作后,便创建默认的native库目录:/data/data/<package name>/lib,并将该目录存入pkg.applicationInfo.nativeLibraryDir中。
步骤14 处理包声明的native库。
如果当前扫描的包使用了native动态库,需要根据包的类型,判断是否需要从APK中解压出该动态库,代码如下:
if(pkg.applicationInfo.nativeLibraryDir!=null){
try{
final File nativeLibraryDir=
new File(pkg.applicationInfo.nativeLibraryDir);
final String dataPathString=dataPath.getCanonicalPath();
//未更新的系统级应用,其native库位于/system/lib目录下
if(isSystemApp(pkg)&&!isUpdatedSystemApp(pkg)){
if(NativeLibraryHelper.
removeNativeBinariesFromDirLI(nativeLibraryDir)){
}
}else if(nativeLibraryDir.getParentFile().getCanonicalPath().
equals(dataPathString)){
//只从非系统级应用和更新过的系统级应用中解压native库复制到lib目录
boolean isSymLink;
try{
//判断native库的路径是否是链接
isSymLink=S_ISLNK(Libcore.os.lstat(
nativeLibraryDir.getPath()).st_mode);
}catch(ErrnoException e){
isSymLink=true;
}
if(isSymLink){//如果是链接,需要删除该链接
mInstaller.unlinkNativeLibraryDirectory(dataPathString);
}
//从APK文件中复制native库到lib目录
NativeLibraryHelper.copyNativeBinariesIfNeededLI(
scanFile, nativeLibraryDir);
}else{
mInstaller.linkNativeLibraryDirectory(dataPathString,
pkg.applicationInfo.nativeLibraryDir);
}
}catch(IOException ioe){
Log.e(TAG,"Unable to get canonical file"+ioe.toString());
}
}
步骤15 如果扫描模式中未设置SCAN_NO_DEX标记,则调用performDexOptLI方法对该包强制dexopt。
步骤16 如果当前扫描的包已安装且正在运行,如果flags中设置了INSTALL_REPLACE_EXISTING标记,需要调用ActivityManagerService的killApplicationWithUid方法杀掉其进程。
进入synchronized(mPackages)同步代码块,执行步骤17~步骤24。
步骤17 将安装包信息存入Settings和PackageManagerService,并更新安装时间,代码如下:
//writer
synchronized(mPackages){
//记录当前安装包信息,防止安装失败
if((scanMode&SCAN_MONITOR)!=0){
mAppDirs.put(pkg.mPath, pkg);
}
//将PackageSettings信息存入Settings.mPackages
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
//将pkg存入PackageManagerService.mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
/*mPackagesToBeCleaned中存储已卸载且清空其外部存储数据的包名
从mPackagesToBeCleaned中移除该包信息,防止误删数据/
mSettings.mPackagesToBeCleaned.remove(pkgName);
//更新安装或升级时间
if(currentTime!=0){
if(pkgSetting.firstInstallTime==0){
pkgSetting.firstInstallTime=pkgSetting.lastUpdateTime=currentTime;
}else if((scanMode&SCAN_UPDATE_TIME)!=0){
pkgSetting.lastUpdateTime=currentTime;
}
}else if(pkgSetting.firstInstallTime==0){
pkgSetting.firstInstallTime=pkgSetting.lastUpdateTime
=scanFileTime;
}else if((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR)!=0){
if(scanFileTime!=pkgSetting.timeStamp){
pkgSetting.lastUpdateTime=scanFileTime;
}
}
……
步骤18 遍历pkg.providers。
将取出的PackageParser.Provider类型对象存入PackageManagerService.mProvidersByComponent中。mProvidersByComponent是一个HashMap,定义如下:
final HashMap<ComponentName, PackageParser.Provider>
mProvidersByComponent=
new HashMap<ComponentName, PackageParser.Provider>();
如果Content Provider的authority不为null,需要以authority为键,将该ContentProvider的Provider信息存入PackageManagerService.mProviders中。mProviders是一个HashMap,定义如下:
final HashMap<String, PackageParser.Provider>mProviders=
new HashMap<String, PackageParser.Provider>();
步骤19 遍历pkg.services。
取出其中存储的PackageParser.Service类型的Service对象,并将其存入PackageManagerService.mServices中。mServices定义如下:
final ServiceIntentResolver mServices=new ServiceIntentResolver();
步骤20 遍历pkg.receivers。
取出其中存储的PackageParser.Activity类型的Receiver对象,并将其存入PackageManagerService.mReceivers中。mReceivers定义如下:
final ActivityIntentResolver mReceivers=new ActivityIntentResolver();
步骤21 遍历pkg.activities。
取出其中存储的PackageParser.Activity类型的Activity对象,并将其存入PackageManagerService.mActivities中。mActivities定义如下:
final ActivityIntentResolver mActivities=new ActivityIntentResolver();
步骤22 遍历pkg.permissionGroups。
取出其中存储的PackageParser.PermissionGroup类型的PermissionGroup对象,并将其存入PackageManagerService.mPermissionGroups中。mPermissionGroups定义如下:
final HashMap<String, PackageParser.PermissionGroup>mPermissionGroups=
new HashMap<String, PackageParser.PermissionGroup>();
步骤23 遍历pkg.permissions。
取出其中存储的PackageParser.Permission类型的Permission对象,然后将其与PermissionTrees和PermissionGroups关联。
步骤24 遍历pkg.instrumentation。
取出其中存储的PackageParser.Instrumentation类型的对象,并将其与pkg.applicationInfo关联,最后将其存入PackageManagerService.mInstrumentation中。mInstrumentation的定义如下:
final HashMap<ComponentName, PackageParser.Instrumentation>mInstrumentation=
new HashMap<ComponentName, PackageParser.Instrumentation>();
至此,scanPackageLI(PackageParser.Package pkg,……)执行完毕。