Introduce a new APK attribute: updatableSystem, default true.
Specified in the manifest.
If a preloaded APK is marked updatableSystem=false, any request for an update is rejected.
If an APK marked updatableSystem=false is being installed, regardless of the updatableSystem state of the version it's replacing, the install is rejected.
Disregard this restriction adb running as root.
!!This also marks Shell as such package!!!
Bug: 266131956
Test: atest PackageManagerTest
Test: atest PackageManagerServiceUnitTest
Change-Id: I86622e48a368bed260949b62c558c3e6ee0df931
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index f3194be..4990a27 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -140,6 +140,11 @@
private final boolean mIsSdkLibrary;
/**
+ * Indicates if this system app can be updated.
+ */
+ private final boolean mUpdatableSystem;
+
+ /**
* Archival install info.
*/
private final @Nullable ArchivedPackageParcel mArchivedPackage;
@@ -154,7 +159,7 @@
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
Set<String> requiredSplitTypes, Set<String> splitTypes,
- boolean hasDeviceAdminReceiver, boolean isSdkLibrary) {
+ boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem) {
mPath = path;
mPackageName = packageName;
mSplitName = splitName;
@@ -188,6 +193,7 @@
mRollbackDataPolicy = rollbackDataPolicy;
mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
mIsSdkLibrary = isSdkLibrary;
+ mUpdatableSystem = updatableSystem;
mArchivedPackage = null;
}
@@ -225,6 +231,7 @@
mRollbackDataPolicy = 0;
mHasDeviceAdminReceiver = false;
mIsSdkLibrary = false;
+ mUpdatableSystem = true;
mArchivedPackage = archivedPackage;
}
@@ -535,6 +542,14 @@
}
/**
+ * Indicates if this system app can be updated.
+ */
+ @DataClass.Generated.Member
+ public boolean isUpdatableSystem() {
+ return mUpdatableSystem;
+ }
+
+ /**
* Archival install info.
*/
@DataClass.Generated.Member
@@ -543,10 +558,10 @@
}
@DataClass.Generated(
- time = 1694792109463L,
+ time = 1699587291575L,
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 @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 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 5f86742..4626679 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -424,6 +424,7 @@
0);
int revisionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "revisionCode", 0);
boolean coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
+ boolean updatableSystem = parser.getAttributeBooleanValue(null, "updatableSystem", true);
boolean isolatedSplits = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
"isolatedSplits", false);
boolean isFeatureSplit = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
@@ -505,14 +506,18 @@
continue;
}
- if (TAG_PROFILEABLE.equals(parser.getName())) {
- profilableByShell = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
- "shell", profilableByShell);
- } else if (TAG_RECEIVER.equals(parser.getName())) {
- hasDeviceAdminReceiver |= isDeviceAdminReceiver(
- parser, hasBindDeviceAdminPermission);
- } else if (TAG_SDK_LIBRARY.equals(parser.getName())) {
- isSdkLibrary = true;
+ switch (parser.getName()) {
+ case TAG_PROFILEABLE:
+ profilableByShell = parser.getAttributeBooleanValue(
+ ANDROID_RES_NAMESPACE, "shell", profilableByShell);
+ break;
+ case TAG_RECEIVER:
+ hasDeviceAdminReceiver |= isDeviceAdminReceiver(parser,
+ hasBindDeviceAdminPermission);
+ break;
+ case TAG_SDK_LIBRARY:
+ isSdkLibrary = true;
+ break;
}
}
} else if (TAG_OVERLAY.equals(parser.getName())) {
@@ -614,7 +619,7 @@
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
- hasDeviceAdminReceiver, isSdkLibrary));
+ hasDeviceAdminReceiver, isSdkLibrary, updatableSystem));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 9f99dc9..f8546b7 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1592,6 +1592,12 @@
<!-- Whether attributions provided are meant to be user-visible. -->
<attr name="attributionsAreUserVisible" format="boolean" />
+ <!-- If a preloaded APK is marked updatableSystem = false, any request for an update will be rejected.
+ If an APK marked updatableSystem = false is being installed, regardless of the updatableSystem state
+ of the version it's replacing, the install will be rejected.
+ This is a private attribute, used without android: namespace. -->
+ <attr name="updatableSystem" format="boolean" />
+
<!-- Specify the type of foreground service. Multiple types can be specified by ORing the flags
together. -->
<attr name="foregroundServiceType">
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ed03d94..c897a3c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -19,6 +19,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.shell"
coreApp="true"
+ updatableSystem="false"
android:sharedUserId="android.uid.shell"
>
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1be28ca..f723178 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2241,31 +2241,39 @@
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean isInstallationAllowed(PackageStateInternal psi) {
+ if (psi == null || psi.getPkg() == null) {
+ return true;
+ }
+ if (psi.getPkg().isUpdatableSystem()) {
+ return true;
+ }
+ if (mOriginalInstallerUid == Process.ROOT_UID) {
+ Slog.w(TAG, "Overriding updatableSystem because the installer is root: "
+ + psi.getPackageName());
+ return true;
+ }
+ return false;
+ }
+
/**
* Check if this package can be installed archived.
*/
- private static boolean isArchivedInstallationAllowed(String packageName) {
- final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(packageName);
- if (existingPkgSetting == null) {
+ private static boolean isArchivedInstallationAllowed(PackageStateInternal psi) {
+ if (psi == null) {
return true;
}
-
return false;
}
/**
* Checks if the package can be installed on IncFs.
*/
- private static boolean isIncrementalInstallationAllowed(String packageName) {
- final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(packageName);
- if (existingPkgSetting == null || existingPkgSetting.getPkg() == null) {
+ private static boolean isIncrementalInstallationAllowed(PackageStateInternal psi) {
+ if (psi == null || psi.getPkg() == null) {
return true;
}
-
- return !existingPkgSetting.isSystem()
- && !existingPkgSetting.isUpdatedSystemApp();
+ return !psi.isSystem() && !psi.isUpdatedSystemApp();
}
/**
@@ -3364,6 +3372,16 @@
"Split " + apk.getSplitName() + " was defined multiple times");
}
+ if (!apk.isUpdatableSystem()) {
+ if (mOriginalInstallerUid == Process.ROOT_UID) {
+ Slog.w(TAG, "Overriding updatableSystem because the installer is root for: "
+ + apk.getPackageName());
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Non updatable system package can't be installed or updated");
+ }
+ }
+
// Use first package to define unknown values
if (mPackageName == null) {
mPackageName = apk.getPackageName();
@@ -3438,8 +3456,17 @@
}
}
+ final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(mPackageName);
+
+ if (!isInstallationAllowed(existingPkgSetting)) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID,
+ "Installation of this package is not allowed.");
+ }
+
if (isArchivedInstallation()) {
- if (!isArchivedInstallationAllowed(mPackageName)) {
+ if (!isArchivedInstallationAllowed(existingPkgSetting)) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_SESSION_INVALID,
"Archived installation of this package is not allowed.");
@@ -3455,7 +3482,7 @@
}
if (isIncrementalInstallation()) {
- if (!isIncrementalInstallationAllowed(mPackageName)) {
+ if (!isIncrementalInstallationAllowed(existingPkgSetting)) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_SESSION_INVALID,
"Incremental installation of this package is not allowed.");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 440823c..bd38699 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4970,6 +4970,10 @@
pw.print(prefix); pw.print(" privateFlags="); printFlags(pw,
privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
}
+ if (!pkg.isUpdatableSystem()) {
+ pw.print(prefix); pw.print(" updatableSystem=false");
+ pw.println();
+ }
if (pkg.hasPreserveLegacyExternalStorage()) {
pw.print(prefix); pw.print(" hasPreserveLegacyExternalStorage=true");
pw.println();
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 056aae4..225c53c 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -396,7 +396,7 @@
// an APK targeting <R that doesn't contain an <application> tag. That code would be skipped
// and never assign this, so initialize this to true for those cases.
private long mBooleans = Booleans.ENABLED;
- private long mBooleans2;
+ private long mBooleans2 = Booleans2.UPDATABLE_SYSTEM;
@NonNull
private Set<String> mKnownActivityEmbeddingCerts = emptySet();
// Derived fields
@@ -3450,6 +3450,11 @@
}
@Override
+ public boolean isUpdatableSystem() {
+ return getBoolean2(Booleans2.UPDATABLE_SYSTEM);
+ }
+
+ @Override
public boolean isFactoryTest() {
return getBoolean(Booleans.FACTORY_TEST);
}
@@ -3521,6 +3526,11 @@
}
@Override
+ public PackageImpl setUpdatableSystem(boolean value) {
+ return setBoolean2(Booleans2.UPDATABLE_SYSTEM, value);
+ }
+
+ @Override
public PackageImpl setFactoryTest(boolean value) {
setBoolean(Booleans.FACTORY_TEST, value);
return this;
@@ -3731,10 +3741,12 @@
@LongDef({
STUB,
APEX,
+ UPDATABLE_SYSTEM,
})
public @interface Flags {}
private static final long STUB = 1L;
private static final long APEX = 1L << 1;
+ private static final long UPDATABLE_SYSTEM = 1L << 2;
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
index aeaff6d..85f8f76 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
@@ -73,6 +73,8 @@
ParsedPackage setApex(boolean isApex);
+ ParsedPackage setUpdatableSystem(boolean value);
+
ParsedPackage markNotActivitiesAsNotExportedIfSingleUser();
ParsedPackage setOdm(boolean odm);
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 91854fd..b2fed8d 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -1393,6 +1393,13 @@
/** @hide */
boolean isApex();
+
+ /**
+ * @see R.styleable#AndroidManifestApplication_updatableSystem
+ * @hide
+ */
+ boolean isUpdatableSystem();
+
/**
* @see ApplicationInfo#enabled
* @see R.styleable#AndroidManifestApplication_enabled
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 699ccbd..711b4cf 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -345,6 +345,8 @@
ParsingPackage setStaticSharedLibraryVersion(long staticSharedLibraryVersion);
+ ParsingPackage setUpdatableSystem(boolean value);
+
ParsingPackage setLargeScreensSupported(int supportsLargeScreens);
ParsingPackage setNormalScreensSupported(int supportsNormalScreens);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 8240c47..d51415b 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -1001,12 +1001,16 @@
return sharedUserResult;
}
+ final boolean updatableSystem = parser.getAttributeBooleanValue(null /*namespace*/,
+ "updatableSystem", true);
+
pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
R.styleable.AndroidManifest_installLocation, sa))
.setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
- .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
+ .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
+ .setUpdatableSystem(updatableSystem);
boolean foundApp = false;
final int depth = parser.getDepth();
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 edab1d6..170faf6 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
@@ -270,7 +270,8 @@
AndroidPackage::getMinAspectRatio,
AndroidPackage::hasPreserveLegacyExternalStorage,
AndroidPackage::hasRequestForegroundServiceExemption,
- AndroidPackage::hasRequestRawExternalStorageAccess
+ AndroidPackage::hasRequestRawExternalStorageAccess,
+ AndroidPackage::isUpdatableSystem
)
override fun extraParams() = listOf(