前言
在Android设备的OTA(Over-The-Air)升级过程中,有时会遇到应用未能及时更新的问题。具体表现为,升级后新内置应用不出现,或者应用版本未发生变化,需要用户恢复出厂设置才能正常显示新版本。这种情况往往是由于系统缓存未得到正确刷新导致的。为了解决这个问题,本文提供了一种通过修改系统源代码来强制刷新缓存的方法。
PackageManagerService.java
在Android系统的源代码中,PackageManagerService.java是负责应用包管理的核心服务类。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(PackageManagerServiceInjector injector, boolean factoryTest,
final String partitionsFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
......
// Bug 2258844 - add debug log or trace for PKMS(3/6)
t.traceBegin("prepare package parser cache");
// 准备包解析器缓存目录,该目录的命名与设备的构建fingerprint有关
mCacheDir = PackageManagerServiceUtils.preparePackageParserCache(
mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
// Bug 2258844 - add debug log or trace for PKMS(4/6)
t.traceEnd();
......
}
需要注意的是,由于某些国内版本的Android设备在通过GMS(Google Mobile Services)认证后仍然需要修改系统版本信息以避免被谷歌发现,因此这些设备会将构建指纹(fingerprint)锁定。PackageManagerService(PMS)在每次开机时会根据这个指纹值在 /data/system/package_cache 目录下查找对应的缓存目录。在userdebug版本中,这个目录的名称通常为“1”,而在正常版本中,它是通过SystemProperties.digestOf(“ro.build.fingerprint”)生成的。
正常ota升级的话这个值会变,所以会清空之前的缓存,使用新生成的目录,需要做如下特殊操作使系统在升级完ota后去更新缓存目录
修改PackageManagerServiceUtils.java以实现缓存刷新
frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
public static @Nullable File preparePackageParserCache(boolean forEngBuild,
boolean isUserDebugBuild, String incrementalVersion) {
......
// The base directory for the package parser cache lives under /data/system/.
final File cacheBaseDir = Environment.getPackageCacheDirectory();
if (!FileUtils.createDir(cacheBaseDir)) {
return null;
}
// There are several items that need to be combined together to safely
// identify cached items. In particular, changing the value of certain
// feature flags should cause us to invalidate any caches.
final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
: SystemProperties.digestOf("ro.build.fingerprint");//文件夹的名称是通过这个生成的
// Reconcile cache directories, keeping only what we'd actually use.
for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
if (Objects.equals(cacheName, cacheDir.getName())) {
//boyin
if(SystemProperties.get("persist.sys.ota_done","0").equals("1")){
//添加标志位,需要对ota升级应用做修改,在升级成功后把这个标志位置为1
FileUtils.deleteContentsAndDir(cacheDir);
SystemProperties.set("persist.sys.ota_done","0");
Slog.d(TAG, "delete known cache ,升级成功:" + cacheDir.getName());
}
Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
} else {
Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
FileUtils.deleteContentsAndDir(cacheDir);
}
}
......
}
在这个修改中,我们添加了一个标志位 persist.sys.ota_done 。在OTA升级成功后,我们需要将这个标志位设置为1。然后,在下次系统启动时, PackageManagerServiceUtils.preparePackageParserCache 方法会检查这个标志位。如果标志位为1,它会删除当前的缓存目录及其内容,并重置标志位为0。这样,系统就会使用新的缓存目录,从而确保应用能够得到及时更新。