9.1.3 过滤PackageParser.Package类型的pkg对象
scanPackageLI(File,……)接收到第二阶段返回的pkg后,便进入第三阶段,代码如下:
private PackageParser.Package scanPackageLI(File scanFile,
int parseFlags, int scanMode, long currentTime){
……//省略内容
PackageSetting ps=null;
PackageSetting updatedPkg;
//reader
synchronized(mPackages){
/*mRenamedPackages的类型是HashMap,以键值对的形式存储
已更名应用程序的包信息,其中键为新的包名,值为旧的包名/
String oldName=mSettings.mRenamedPackages.get(pkg.packageName);
//查看mOriginalPackages中是否存在该包的原始包信息
if(pkg.mOriginalPackages!=null&&
pkg.mOriginalPackages.contains(oldName)){
//从Settings.mPackages中查找原始包对应的PackageSetting信息
ps=mSettings.peekPackageLPr(oldName);
}
//无原始包信息,查询Settings.mPackages中新包名对应的PackageSetting
if(ps==null){
ps=mSettings.peekPackageLPr(pkg.packageName);
}
/*查询Settings.mDisabledSysPackages是否存在该包的信息
该变量存储了被替换或升级了的系统应用程序的PackageSetting/
updatedPkg=mSettings.getDisabledSystemPkgLPr(ps!=null?
ps.name:pkg.packageName);
}
//①当前解析的包信息是需要升级的系统级应用
if(updatedPkg!=null&&(parseFlags&PackageParser.PARSE_IS_SYSTEM)!=0){
//②如果APK包的路径发生变化
if(ps!=null&&!ps.codePath.equals(scanFile)){
//系统包路径发生变化且版本低于已安装版本,则直接返回null
if(pkg.mVersionCode<ps.versionCode){
……
return null;
}else{
//否则,先从PackageManagerService的mPackages中删除原始包信息
synchronized(mPackages){
mPackages.remove(ps.name);
}
//创建安装参数
InstallArgs args=createInstallArgs(
packageFlagsToInstallFlags(ps),ps.codePathString,
ps.resourcePathString, ps.nativeLibraryPathString);
synchronized(mInstaller){
args.cleanUpResourcesLI();
}
synchronized(mPackages){
//将当前包由disabled修改为enable
mSettings.enableSystemPackageLPw(ps.name);
}
}
}//if②结束
}//if①结束
if(updatedPkg!=null){
//升级的系统级APK初始没有设置PARSE_IS_SYSTEM标记,这里追加该标记
parseFlags|=PackageParser.PARSE_IS_SYSTEM;
}
//验证certificates
if(!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)){
return null;//验证失败,直接返回
}
boolean shouldHideSystemApp=false;
//当前扫描的是系统包,与已安装的非系统包重名
if(updatedPkg==null&&ps!=null
&&(parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR)!=0
&&!isSystemApp(ps)){
//比较签名信息,如果不同则删除已安装的APK
if(compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
!=PackageManager.SIGNATURE_MATCH){
deletePackageLI(pkg.packageName, true,0,null, false);
ps=null;
}else{
//当前扫描的系统APK版本比已安装版本低,则需要将其隐藏
if(pkg.mVersionCode<ps.versionCode){
shouldHideSystemApp=true;
}else{
//当前扫描的系统APK比已安装版本高,则删除已安装应用,但保留其数据
InstallArgs args=createInstallArgs(
packageFlagsToInstallFlags(ps),ps.codePathString,
ps.resourcePathString, ps.nativeLibraryPathString);
synchronized(mInstaller){
args.cleanUpResourcesLI();
}
}
}
}
……//省略第三阶段一部分内容,后续分析
可见,scanPackageLI第三阶段的主要工作是过滤第二阶段返回的PackageParser.Package类型的pkg对象。scanPackageLI扫描到的APK文件可能是已更名的包、disabled的包、需要升级的包、已安装并且签名冲突的包、被非系统级包替代的系统包等情况。scanPackageLI第三阶段将针对上述情况一一进行处理:要么删除,要么更新。具体操作参考源码中的注释,这里不赘述。
scanPackageLI第三阶段有一个重要操作:验证certificates,该操作由collectCertificatesLI方法完成。其代码如下:
private boolean collectCertificatesLI(PackageParser pp, PackageSetting ps,
PackageParser.Package pkg, File srcFile, int parseFlags){
if(GET_CERTIFICATES){//默认为true
if(ps!=null
&&ps.codePath.equals(srcFile)
&&ps.timeStamp==srcFile.lastModified()){
//如果包路径未变,文件时间戳未修改,说明包没有变化
if(ps.signatures.mSignatures!=null
&&ps.signatures.mSignatures.length!=0){
//复用已有的certificates
pkg.mSignatures=ps.signatures.mSignatures;
return true;
}
}else{
Log.i(TAG, srcFile.toString()+"changed;collecting certs");
}
//包文件有改动,需要重新获取certificates
if(!pp.collectCertificates(pkg, parseFlags)){
mLastScanError=pp.getParseError();
return false;
}
}
return true;
}
由以上代码可知,如果包没有变化,会复用已有的certificates。如果包发生变化,需要调用collectCertificates方法重新获取certificates。接下来分析certificates的获取过程,代码如下:
public boolean collectCertificates(Package pkg, int flags){
pkg.mSignatures=null;
byte[]readBuffer=null;
……
try{
JarFile jarFile=new JarFile(mArchiveSourcePath);
Certificate[]certs=null;
if((flags&PARSE_IS_SYSTEM)!=0){
/系统级APK是可信的,通过AndroidManifest.xml获取签名信息/
JarEntry jarEntry=jarFile.getJarEntry(ANDROID_MANIFEST_FILENAME);
certs=loadCertificates(jarFile, jarEntry, readBuffer);
……
}
}else{//非系统级应用
Enumeration<JarEntry>entries=jarFile.entries();
final Manifest manifest=jarFile.getManifest();
while(entries.hasMoreElements()){
/*遍历APK文件获取签名信息,也是调用loadCertificates方法
这里都是标准算法,有兴趣的读者可以自行研究/
}
}
//将签名信息存入pkg.mSignatures
if(certs!=null&&certs.length>0){
final int N=certs.length;
pkg.mSignatures=new Signature[certs.length];
……
}
collectCertificates方法会根据APK的类型做出不同处理。如果是系统级APK,直接利用AndroidManifest.xml获取签名信息,而不需要验证APK中所有文件。如果是非系统级APK,则需要验证APK中所有文件以获取certificates信息。最终签名信息被存入pkg.mSignatures中。
签名信息验证通过后,返回到scanPackageLI(File,……)方法,继续执行第三阶段后续部分,代码如下:
private PackageParser.Package scanPackageLI(File scanFile,
int parseFlags, int scanMode, long currentTime){
……//省略第一阶段内容
……//省略第二阶段已分析内容
//codePath与resourcePath不同时,需要设置PARSE_FORWARD_LOCK标记
if(ps!=null&&!ps.codePath.equals(ps.resourcePath)){
parseFlags|=PackageParser.PARSE_FORWARD_LOCK;
}
String codePath=null;
String resPath=null;
//如果设置了PARSE_FORWARD_LOCK标记
if((parseFlags&PackageParser.PARSE_FORWARD_LOCK)!=0){
if(ps!=null&&ps.resourcePathString!=null){
resPath=ps.resourcePathString;//设置resPath
}else{//错误处理
Slog.e(TAG,"Resource path not set for pkg:"+pkg.packageName);
}
}else{//如果没有设置PARSE_FORWARD_LOCK标记
resPath=pkg.mScanPath;//resPath与APK文件路径一样
}
codePath=pkg.mScanPath;
/*以codePath设置pkg.mPath、pkg.mScanPath和
*pkg.applicationInfo.sourceDir,以resPath设置
pkg.applicationInfo.publicSourceDir/
setApplicationInfoPaths(pkg, codePath, resPath);
……//省略第四阶段内容
可见scanPackageLI(File,……)方法第三阶段后续处理部分主要处理FORWARD_LOCK类型的安装包的resourcePath和codePath。因为FORWARD_LOCK类型APK的resourcePath和codePath并不相同,需要将这两个变量设为相应值,即resourcePath=/data/app/<pkgname>.zip;codePath=/data/app-private/<pkgname>.apk。其中pkgname为包名。resourcePath和codePath设置成功后调用setApplicationInfoPaths方法更新pkg的pkg.mPath、pkg.mScanPath、pkg.applicationInfo.sourceDir和pkg.applicationInfo.publicSourceDir信息。FORWARD_LOCK类型APK放在后续小节中分析。
接下来开始分析scanPackageLI第四阶段。