背景:
記錄最近在項目中遇到的一個問題,先說現象:
/system/app/下原本有一個可以正常使用的APK,APK本想下發自升級,APK存在問題無法安裝成功,但奇怪的是,原本/system/app/下正常的APK也無法使用了。
從pm命令及ps命令都看到/system/app/下的APK沒有運行。
調試:
1、把問題APK拿到後測試,pm installI -r xxx.apk 手動安裝,返回
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,這個錯誤是因爲APK設置了UIDandroid:sharedUserId="android.uid.system"
,但沒有用系統簽名導致,這是APK方的失誤。
安裝報錯後重啓,就復現問題,pm -lf找不到原本/system/app/下的apk了。
2、使用dumpsys命令調試
dumpsys package com.xxx.xxx
安裝前:
安裝後:
可以看到有兩個異常:
- APK 狀態installed=true,但enable=2, 2的狀態是禁止狀態,就如同 pm disable
- APK的路徑仍然使用的是/system/app/下的路徑,而在下面可以看到,/system/app/下的APK是被隱藏了
分析:
1、
簡單列下PMS安裝流程。
安裝一個APK的流程可以簡單分爲拷貝->裝載這兩部分。
裝載部分的核心函數:
installPackageLI
->>因爲我們是APK更新,所以是replace
replacePackageLI
->>更新系統APK
replaceSystemPackageLI
->>
scanPackageLI
->>
verifySignaturesLP
而INSTALL_FAILED_SHARED_USER_INCOMPATIBLE的報錯則是出現在簽名校驗部分,即verifySignaturesLP
函數中。
2、
跟進代碼,replaceSystemPackageLI
中對新APK校驗之前,會判斷是否應該禁止原來的system app。
在開始會先disableSystemPackageLPw
禁止老的system app,對新app進行校驗,這樣當高版本APK安裝完成後,則會使用/data/app/路徑下的APK,/system/app/下的則被隱藏了。
scanPackageLI:
......省略
synchronized (mPackages) {
if (!mSettings.disableSystemPackageLPw(packageName) && deletedPackage != null) {
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
res.removedInfo.args = createInstallArgs(0,
deletedPackage.applicationInfo.sourceDir,
deletedPackage.applicationInfo.publicSourceDir,
deletedPackage.applicationInfo.nativeLibraryDir);
} else {
res.removedInfo.args = null;
}
}
// Successfully disabled the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
if (newPackage.mExtras != null) {
final PackageSetting newPkgSetting = (PackageSetting)newPackage.mExtras;
newPkgSetting.firstInstallTime = oldPkgSetting.firstInstallTime;
newPkgSetting.lastUpdateTime = System.currentTimeMillis();
}
updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
updatedSettings = true;
}
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
// Re installation failed. Restore old information
// Remove new pkg information
if (newPackage != null) {
removeInstalledPackageLI(newPackage, true);
}
// Add back the old system package
scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user);
// Restore the old system information in Settings
synchronized(mPackages) {
if (updatedSettings) {
mSettings.enableSystemPackageLPw(packageName);
mSettings.setInstallerPackageName(packageName,
oldPkgSetting.installerPackageName);
}
mSettings.writeLPr();
}
}
3、
從上面代碼可以看出,如果verifySignaturesLP
校驗失敗,最終scanPackageLI
會返回null,因爲更新失敗,所以不會走到updateSettingsLI
更新APK的路徑,狀態等。
而失敗的情況下,因爲updatedSettings
爲false,所以也不會重新把/system/app/的APK給重新解放出來,這就導致了重啓後,原本/system/app/下的APK也無法使用了。
4、
因爲對PMS沒有太深入的研究,不太能理解爲什麼Android 4.4上會這麼寫,但問題確實是存在的,查看更高版本的Android系統源碼,這部分已經被修改了,verifySignaturesLP
被提前,校驗失敗則不需要更新APK信息。
解決:
嘗試修改,將INSTALL_FAILED_SHARED_USER_INCOMPATIBLE錯誤的updatedSettings
設置爲true。
在replaceSystemPackageLI
中修改如下代碼,暫時測試正常,仍需要進一步排查影響,畢竟不清楚Android 4.4上這樣寫的原因。