android11 记一次系统OTA升级后,系统预置应用崩溃问题

在一次客户提完新需求,对应用功能模块做完新增后,原本打算当前发布版本就结束下班的,没想到,在测试OTA时,发现应用新增的模块入口界面排版异常,新增模块对应入口点击后直接应用崩溃,这一下把我干沉没了。

老规矩,adb logcat看crash信息,提示信息是 AndroidManifest.xml 内没有配置新增Activity;

通过adb pull下来apk,解包看里边的 AndroidManifest.xml,配置正常

新增文件正常,但系统运行异常,那就只能往系统缓存排查,由于之前没有遇到该问题,只能通过正常开机日志与异常开机日志对比了,推测问题点是PackageManager相关的信息,所以问题日志主找package相关的,通过mtklog抓取正常升级的ota日志与该次异常升级的开机日志,查找里边package对比两边package相关的解析信息

还真找到了一个异常信息提示

通过对比正常升级与该次异常升级,在异常升级的流程中,出现了不同流程log

正常升级

PackageManager: Destroying unknown cache 77feeab01bc36155135abef0849fcf90be953d08

异常升级

PackageManager: Keeping known cache fb0639540709b7cea68ea0d057dff21c5caebbaf

大胆推测,就是该cache未更新导致apk运行异常

查找出处如下:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#preparePackageParserCache

    private static @Nullable File preparePackageParserCache() {
        if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
            if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
                return null;
            }

            // Disable package parsing on eng builds to allow for faster incremental development.
            if (Build.IS_ENG) {
                return null;
            }

            if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
                Slog.i(TAG, "Disabling package parser cache due to system property.");
                return null;
            }
        }

        // 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",
                        StorageManager.PROP_ISOLATED_STORAGE,
                        StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT
                );

        // Reconcile cache directories, keeping only what we'd actually use.
        for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
            if (Objects.equals(cacheName, cacheDir.getName())) {
                Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
            } else {
                Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
                FileUtils.deleteContentsAndDir(cacheDir);
            }
        }

        // Return the versioned package cache directory.
        File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);

        if (cacheDir == null) {
            // Something went wrong. Attempt to delete everything and return.
            Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
            FileUtils.deleteContentsAndDir(cacheBaseDir);
            return null;
        }

        // The following is a workaround to aid development on non-numbered userdebug
        // builds or cases where "adb sync" is used on userdebug builds. If we detect that
        // the system partition is newer.
        //
        // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
        // that starts with "eng." to signify that this is an engineering build and not
        // destined for release.
        if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) {
            Slog.w(TAG, "Wiping cache directory because the system partition changed.");

            // Heuristic: If the /system directory has been modified recently due to an "adb sync"
            // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
            // in general and should not be used for production changes. In this specific case,
            // we know that they will work.
            File frameworkDir = new File(Environment.getRootDirectory(), "framework");
            if (cacheDir.lastModified() < frameworkDir.lastModified()) {
                FileUtils.deleteContents(cacheBaseDir);
                cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
            }
        }

        return cacheDir;
    }

由于还未确认是哪个数据导致系统认为没有更新,为了快速搞定,临时采取的措施是在判断区域内增加系统版本号的匹配条件,通过如果版本号不一致,则强制要求删除缓存,重新解析包数据,改动如下:

版本检测代码:

    private static final String LAST_VERSION_FILE = "/data/system/last_ota_version";
    private String mCurrentVersion;
    private static boolean hasOtaUpdate = false;

    private String readNowVersion() {
        Slog.e(TAG, "fanhanxi-pkg readNowVersion");

        return SystemProperties.get("ro.build.display.id");
    }

    private boolean checkOtaUpgrade() {
        String lastVersion = readLastVersion();
        mCurrentVersion = readNowVersion();

        Slog.e(TAG, "fanhanxi-pkg checkOtaUpgrade lastVersion = " + lastVersion);
        Slog.e(TAG, "fanhanxi-pkg checkOtaUpgrade mCurrentVersion = " + mCurrentVersion);

        if (mCurrentVersion != null && lastVersion != null){
            boolean hasUpdate = !(mCurrentVersion.equals(lastVersion));
            saveCurrentVersion();

            return hasUpdate;
        }else{
            return false;
        }
    }

    private String readLastVersion() {
        Slog.e(TAG, "fanhanxi-pkg readLastVersion");

        try (FileInputStream fis = new FileInputStream(LAST_VERSION_FILE)) {
            Properties props = new Properties();
            props.load(fis);
            return props.getProperty("version", "");
        } catch (IOException e) {
            return "";
        }
    }

    private void saveCurrentVersion() {
        Slog.e(TAG, "fanhanxi-pkg saveCurrentVersion");

        try (FileOutputStream fos = new FileOutputStream(LAST_VERSION_FILE)) {
            Properties props = new Properties();
            props.setProperty("version", mCurrentVersion);
            props.store(fos, "Last OTA Version");
        } catch (IOException e) {
            Slog.e(TAG, "Failed to save version", e);
        }
    }


//
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
        ...
        hasOtaUpdate = checkOtaUpgrade();
        ....

        mCacheDir = preparePackageParserCache();
        ...
}

//
private static @Nullable File preparePackageParserCache() {
.....
 // Reconcile cache directories, keeping only what we'd actually use.
        for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
            if (!hasOtaUpdate && Objects.equals(cacheName, cacheDir.getName())) {
                Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
            } else {
                Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
                FileUtils.deleteContentsAndDir(cacheDir);
            }
        }
.....
}

在PackageManager初始化的时候,检查当前系统版本是否存在变动,有存在变动的话,则在preparePackageParserCache中强制删除cache

再编译软件升级,运行正常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值