Android 4.4 安裝INSTALL_FAILED_SHARED_USER_INCOMPATIBLE後原APK無法使用問題分析

背景:

記錄最近在項目中遇到的一個問題,先說現象:
/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上這樣寫的原因。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章