Introduce a new APK attribute: emergencyInstaller
Allow app stores in the system image to designate in their manifest the package name of another system app which can serve as an “emergency installer”.
See more details here: go/android-firebrick-emergency-installer-1pager
Bug: 321080601
Test: m
Change-Id: Ibaac0c7dc2d038e9d08f3543d54ea94178387d15
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 4990a27..74ce62c 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -145,6 +145,11 @@
private final boolean mUpdatableSystem;
/**
+ * Name of the emergency installer for the designated system app.
+ */
+ private final @Nullable String mEmergencyInstaller;
+
+ /**
* Archival install info.
*/
private final @Nullable ArchivedPackageParcel mArchivedPackage;
@@ -159,7 +164,8 @@
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
Set<String> requiredSplitTypes, Set<String> splitTypes,
- boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem) {
+ boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem,
+ String emergencyInstaller) {
mPath = path;
mPackageName = packageName;
mSplitName = splitName;
@@ -194,6 +200,7 @@
mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
mIsSdkLibrary = isSdkLibrary;
mUpdatableSystem = updatableSystem;
+ mEmergencyInstaller = emergencyInstaller;
mArchivedPackage = null;
}
@@ -232,6 +239,7 @@
mHasDeviceAdminReceiver = false;
mIsSdkLibrary = false;
mUpdatableSystem = true;
+ mEmergencyInstaller = null;
mArchivedPackage = archivedPackage;
}
@@ -550,6 +558,14 @@
}
/**
+ * Name of the emergency installer for the designated system app.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getEmergencyInstaller() {
+ return mEmergencyInstaller;
+ }
+
+ /**
* Archival install info.
*/
@DataClass.Generated.Member
@@ -558,10 +574,10 @@
}
@DataClass.Generated(
- time = 1699587291575L,
+ time = 1706896661616L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 69f9a7d..ffb69c0 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -435,6 +435,7 @@
boolean isSplitRequired = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
"isSplitRequired", false);
String configForSplit = parser.getAttributeValue(null, "configForSplit");
+ String emergencyInstaller = parser.getAttributeValue(null, "emergencyInstaller");
int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
@@ -644,7 +645,7 @@
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
- hasDeviceAdminReceiver, isSdkLibrary, updatableSystem));
+ hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index 83acc47..d433ca3 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -227,6 +227,9 @@
private String requiredAccountType;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
+ private String mEmergencyInstaller;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
private String overlayTarget;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
@@ -1275,6 +1278,12 @@
return restrictedAccountType;
}
+ @Nullable
+ @Override
+ public String getEmergencyInstaller() {
+ return mEmergencyInstaller;
+ }
+
@Override
public int getRoundIconResourceId() {
return roundIconRes;
@@ -2336,6 +2345,12 @@
}
@Override
+ public PackageImpl setEmergencyInstaller(@Nullable String emergencyInstaller) {
+ this.mEmergencyInstaller = emergencyInstaller;
+ return this;
+ }
+
+ @Override
public PackageImpl setRoundIconResourceId(int value) {
roundIconRes = value;
return this;
@@ -3105,6 +3120,7 @@
dest.writeString(this.mBaseApkPath);
dest.writeString(this.restrictedAccountType);
dest.writeString(this.requiredAccountType);
+ dest.writeString(this.mEmergencyInstaller);
sForInternedString.parcel(this.overlayTarget, dest, flags);
dest.writeString(this.overlayTargetOverlayableName);
dest.writeString(this.overlayCategory);
@@ -3255,6 +3271,7 @@
this.mBaseApkPath = in.readString();
this.restrictedAccountType = in.readString();
this.requiredAccountType = in.readString();
+ this.mEmergencyInstaller = in.readString();
this.overlayTarget = sForInternedString.unparcel(in);
this.overlayTargetOverlayableName = in.readString();
this.overlayCategory = in.readString();
diff --git a/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
index 7ef0b48..66cfb69 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
@@ -75,6 +75,11 @@
ParsedPackage setUpdatableSystem(boolean value);
+ /**
+ * Sets a system app that is allowed to update another system app
+ */
+ ParsedPackage setEmergencyInstaller(String emergencyInstaller);
+
ParsedPackage markNotActivitiesAsNotExportedIfSingleUser();
ParsedPackage setOdm(boolean odm);
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index 6c09b7c..ef106e0 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -347,6 +347,11 @@
ParsingPackage setUpdatableSystem(boolean value);
+ /**
+ * Sets a system app that is allowed to update another system app
+ */
+ ParsingPackage setEmergencyInstaller(String emergencyInstaller);
+
ParsingPackage setLargeScreensSupported(int supportsLargeScreens);
ParsingPackage setNormalScreensSupported(int supportsNormalScreens);
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index f483597..e0fdbc6 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -952,6 +952,8 @@
final boolean updatableSystem = parser.getAttributeBooleanValue(null /*namespace*/,
"updatableSystem", true);
+ final String emergencyInstaller = parser.getAttributeValue(null /*namespace*/,
+ "emergencyInstaller");
pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
R.styleable.AndroidManifest_installLocation, sa))
@@ -959,7 +961,8 @@
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
.setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
- .setUpdatableSystem(updatableSystem);
+ .setUpdatableSystem(updatableSystem)
+ .setEmergencyInstaller(emergencyInstaller);
boolean foundApp = false;
final int depth = parser.getDepth();
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index adb0c69..096f246 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -273,6 +273,13 @@
String getRestrictedAccountType();
/**
+ * @see R.styleable#AndroidManifestApplication_emergencyInstaller
+ * @hide
+ */
+ @Nullable
+ String getEmergencyInstaller();
+
+ /**
* @see ApplicationInfo#roundIconRes
* @see R.styleable#AndroidManifestApplication_roundIcon
*/
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 65c4d9f..d910940 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1608,6 +1608,10 @@
This is a private attribute, used without android: namespace. -->
<attr name="updatableSystem" format="boolean" />
+ <!-- Allows each installer in the system image to designate another app in the system image to
+ update the installer. -->
+ <attr name="emergencyInstaller" format="string" />
+
<!-- Specify the type of foreground service. Multiple types can be specified by ORing the flags
together. -->
<attr name="foregroundServiceType">
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 27c3dad..9767338 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -936,6 +936,21 @@
getInstallSource().mInstallerPackageName, mInstallerUid);
}
+ private boolean isEmergencyInstallerEnabled(String packageName, Computer snapshot) {
+ final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName);
+ if (ps == null || ps.getPkg() == null || !ps.isSystem()) {
+ return false;
+ }
+ String emergencyInstaller = ps.getPkg().getEmergencyInstaller();
+ if (emergencyInstaller == null || !ArrayUtils.contains(
+ snapshot.getPackagesForUid(mInstallerUid),
+ emergencyInstaller)) {
+ return false;
+ }
+ return (snapshot.checkUidPermission(Manifest.permission.EMERGENCY_INSTALL_PACKAGES,
+ mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+ }
+
private static final int USER_ACTION_NOT_NEEDED = 0;
private static final int USER_ACTION_REQUIRED = 1;
private static final int USER_ACTION_PENDING_APK_PARSING = 2;
@@ -1020,6 +1035,8 @@
final boolean isUpdateOwner = TextUtils.equals(existingUpdateOwnerPackageName,
getInstallerPackageName());
final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
+ final boolean isEmergencyInstall =
+ isEmergencyInstallerEnabled(packageName, snapshot);
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && isUpdate)
|| (isSelfUpdatePermissionGranted && isSelfUpdate)
@@ -1036,7 +1053,7 @@
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
final boolean noUserActionNecessary = isInstallerRoot || isInstallerSystem
- || isInstallerDeviceOwnerOrAffiliatedProfileOwner();
+ || isInstallerDeviceOwnerOrAffiliatedProfileOwner() || isEmergencyInstall;
if (noUserActionNecessary) {
return userActionNotTypicallyNeededResponse;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 04e8205..5575f52 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5044,6 +5044,10 @@
pw.print(prefix); pw.print(" updatableSystem=false");
pw.println();
}
+ if (pkg.getEmergencyInstaller() != null) {
+ pw.print(prefix); pw.print(" emergencyInstaller=");
+ pw.println(pkg.getEmergencyInstaller());
+ }
if (pkg.hasPreserveLegacyExternalStorage()) {
pw.print(prefix); pw.print(" hasPreserveLegacyExternalStorage=true");
pw.println();
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index ef9c62f..cfe701f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -272,7 +272,8 @@
AndroidPackage::hasPreserveLegacyExternalStorage,
AndroidPackage::hasRequestForegroundServiceExemption,
AndroidPackage::hasRequestRawExternalStorageAccess,
- AndroidPackage::isUpdatableSystem
+ AndroidPackage::isUpdatableSystem,
+ AndroidPackage::getEmergencyInstaller
)
override fun extraParams() = listOf(