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,……)执行完毕。