版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangmingbao2016/article/details/53695904
问题描述:
下载三个apk(http://pan.baidu.com/s/1geS25r1):
在android5.0 及以上平台:
1、将331.apk 替换掉系统内置的/system/vendor/app/SkyAppStore/SkyAppStore.apk ,并 rm -rf /data/data/com.tianci.appstore。rm -rf /data/app/com.tianci.appstore-xxx。 然后重启电视
2、pm install -r /mnt/usb/sda1/523.apk,恢复出厂设置:保留应用。问题1:
3、再次pm install -r /mnt/usb/sda1/622.apk,。 问题2:
问题:
1、电视重启后,通过pm path com.tianci.appstore发现,系统用的是system下的,而不是data下的
2、再次安装新的后,pm path com.tianci.appstore 使用的是data/app/com.tianci.appstore-2。而com.tianci.appstore-1还在。
任务:
1、查android源码,找出问题1/2的原因
2、提供解决方案:如何避免com.tianci.appstore-1垃圾的产生。
问题1产生的原因:
系统恢复出厂设置时会删除packages.xml和packages.list文件,这两个文件中记录的是系统已安装的应用信息,包括用户应用和系统应用,系统再次开机时,回会先扫描system下的app安装,之后才会扫描data下面的app进行安装,其中扫描安装过程中会调用一下两个判断(位于PackageManagerService.java):
if (mPackages.containsKey(pkg.packageName) || mSharedLibraries.containsKey(pkg.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,"Application package " +pkg.packageName+ " already installed. Skipping duplicate.");
}
这个判断是检查该apk是否已被安装过,若已被安装过得apk包含这个apk信息,则直接抛出异常不再重复安装,所以对于系统自带的apk,及时data下有也不会重新安装
if (!pkg.applicationInfo.getCodePath().equals(known.codePathString) || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,"Application package " + pkg.packageName + " found at " + pkg.applicationInfo.getCodePath() + " but expected at " + known.codePathString + "; ignoring.");
}
这个判断是检查当前要安装的apk路径与已被安装的apk的配置信息是否一致,若不一致则抛出异常,不再安装次apk,由于系统自带的apk的安装路径是指向system目录的,所以即使data下有该apk也不会更新apk的安装信息。
综上:系统恢复出厂设置后,及时data下也有系统自带apk的安装信息,也还是会使用system下的apk,而不会使用data下的apk。
问题2产生的原因:
private File getNextCodePath(String packageName) {
int suffix = 1;
File result;
do {
result = new File(mAppInstallDir, packageName + "-" + suffix);
suffix++;
} while (result.exists());
return result;
}
pm install -r /mnt/usb/sda1/523.apk会调用以上函数来产生自己的安装文件夹,由次函数可以看出:如果检测到data/app下如果有此app的安装文件夹,则自动将后缀加1(suffix++;),pm install -r /mnt/usb/sda1/523.apk后会在data/app下产生com.tianci.appstore-1的文件夹,恢复出厂设置后,由于系统带有appstore的apk,所以系统会安装自带的apk,而此时data/app下的appstore的安装文件并没有被删掉,此时pm path com.tianci.appstore查看系统使用的app路劲会得到:/system/vendor/app/SkyAppStore/SkyAppStore.apk,即是用的系统apk,当再次pm install -r /mnt/usb/sda1/622.apk时,系统根据packages.xml只检测到了系统自带的apk,没有检测到之前用户自己安装过SkyAppStore.apk,所以再次安装的时候只会卸载掉系统apk(更新packages.xml,而不是把/system/vendor/app/SkyAppStore/SkyAppStore.apk删掉),然后调用getNextCodePath函数在data/app下产生自己的文件夹,此时就产生了com.tianci.appstore-1垃圾。
问题2解决方式一(验证通过):
pm install -r /mnt/usb/sda1/622.apk时,若data/app下已存在该包名的文件夹则直接删掉,修改getNextCodePath函数:
private File getNextCodePath(String packageName) {
int suffix = 1;
File result = new File(mAppInstallDir, packageName + "-" + suffix);
for(;result.exists();)
{
if(result.isDirectory())
{
FileUtils.deleteContents(result);
}
result.delete();
suffix++;
result = new File(mAppInstallDir, packageName + "-" + suffix);
}
result = new File(mAppInstallDir, packageName + "-" + 1);
return result;
}
问题2解决方式二(验证通过):
回复出厂设置时,允许apk重复安装,等同于优先使用data下的apk,,注释掉两个判断即可:
/*
if (mPackages.containsKey(pkg.packageName) || mSharedLibraries.containsKey(pkg.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,"Application package " +pkg.packageName+ " already installed. Skipping duplicate.");
}*/
/*
if (!pkg.applicationInfo.getCodePath().equals(known.codePathString) || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,"Application package " + pkg.packageName + " found at " + pkg.applicationInfo.getCodePath() + " but expected at " + known.codePathString + "; ignoring.");
}*/
潜在的问题:1、系统恢复出厂设置的原则是将系统回复到出厂状态,若使用此修改,相当于系统没有恢复到最原始的状态
2、若用户已安装过系统apk,然后回复出厂设置,再卸载该apk后,系统自带的该apk也不可使用,因为重复安装会直接删除system下的apk安装信息,而不会将其修改为update-package,此时,只有重启电视,系统才会重新安装system下的apk,然后可以正常使用(尝试继续研究解决办法)
问题2解决方式三(验证通过):
系统第一次开机时直接删除安装失败的软件包,mRestoredSettings是判断是否为第一次开机的标志。
// Delete invalid userdata apps
if (((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) || !mRestoredSettings) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
if (file.isDirectory()) {
FileUtils.deleteContents(file);
}
file.delete();
}
潜在问题:若用户在升级到该系统版本前执行操作:pm install -r /mnt/usb/sda1/622.apk,然后恢复出厂设置,此时data/app下存在com.tianci.appstore-1,pm path com.tianci.appstore可以看到系统用的是system下的apk,这是系统升级到该版本,然后pm install -r /mnt/usb/sda1/622.apk会在data/app下产生com.tianci.appstore-2文件夹,需要再恢复一次出厂设置。