summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/Binder.java19
-rw-r--r--core/java/android/os/Parcel.java17
-rw-r--r--core/jni/AndroidRuntime.cpp11
-rw-r--r--core/jni/android_util_Binder.cpp5
-rw-r--r--core/res/AndroidManifest.xml8
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--core/tests/coretests/src/android/os/ParcelTest.java89
-rw-r--r--core/tests/fuzzers/ParcelFuzzer/Android.bp1
-rw-r--r--core/tests/fuzzers/java_service_fuzzer/Android.bp1
-rw-r--r--libs/androidfw/include/androidfw/ObbFile.h6
-rw-r--r--native/android/storage_manager.cpp2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java2
-rw-r--r--services/core/java/com/android/server/DynamicSystemService.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java4
-rw-r--r--tools/aapt2/cmd/Link.h5
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp5
-rw-r--r--tools/aapt2/link/ManifestFixer.h8
-rw-r--r--tools/aapt2/link/ManifestFixer_test.cpp29
20 files changed, 195 insertions, 40 deletions
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 0cb7df73f9b7..5d0a723241b0 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -325,7 +325,24 @@ public class Binder implements IBinder {
* @hide
*/
@CriticalNative
- public static final native boolean isDirectlyHandlingTransaction();
+ public static final native boolean isDirectlyHandlingTransactionNative();
+
+ private static boolean sIsHandlingBinderTransaction = false;
+
+ /**
+ * @hide
+ */
+ public static final boolean isDirectlyHandlingTransaction() {
+ return sIsHandlingBinderTransaction || isDirectlyHandlingTransactionNative();
+ }
+
+ /**
+ * This is Test API which will be used to override output of isDirectlyHandlingTransactionNative
+ * @hide
+ */
+ public static void setIsDirectlyHandlingTransactionOverride(boolean isInTransaction) {
+ sIsHandlingBinderTransaction = isInTransaction;
+ }
/**
* Returns {@code true} if the current thread has had its identity
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8d07a861a8c2..cdc8268fa7a1 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -63,6 +63,7 @@ import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -1551,6 +1552,9 @@ public final class Parcel {
}
} catch (ArithmeticException e) {
Log.e(TAG, "ArithmeticException occurred while multiplying dimensions " + e);
+ BadParcelableException badParcelableException = new BadParcelableException("Estimated "
+ + "array length is too large. Array Dimensions:" + Arrays.toString(dimensions));
+ SneakyThrow.sneakyThrow(badParcelableException);
}
ensureWithinMemoryLimit(typeSize, totalObjects);
}
@@ -1562,19 +1566,24 @@ public final class Parcel {
} catch (ArithmeticException e) {
Log.e(TAG, "ArithmeticException occurred while multiplying values " + typeSize
+ " and " + length + " Exception: " + e);
+ BadParcelableException badParcelableException = new BadParcelableException("Estimated "
+ + "allocation size is too large. typeSize: " + typeSize + " length: " + length);
+ SneakyThrow.sneakyThrow(badParcelableException);
}
boolean isInBinderTransaction = Binder.isDirectlyHandlingTransaction();
if (isInBinderTransaction && (estimatedAllocationSize > ARRAY_ALLOCATION_LIMIT)) {
Log.e(TAG, "Trying to Allocate " + estimatedAllocationSize
+ " memory, In Binder Transaction : " + isInBinderTransaction);
+ BadParcelableException e = new BadParcelableException("Allocation of size "
+ + estimatedAllocationSize + " is above allowed limit of 1MB");
+ SneakyThrow.sneakyThrow(e);
}
}
@Nullable
public final boolean[] createBooleanArray() {
int N = readInt();
- // Assuming size of 4 byte for boolean.
ensureWithinMemoryLimit(SIZE_BOOLEAN, N);
// >>2 as a fast divide-by-4 works in the create*Array() functions
// because dataAvail() will never return a negative number. 4 is
@@ -1618,7 +1627,6 @@ public final class Parcel {
@Nullable
public short[] createShortArray() {
int n = readInt();
- // Assuming size of 2 byte for short.
ensureWithinMemoryLimit(SIZE_SHORT, n);
if (n >= 0 && n <= (dataAvail() >> 2)) {
short[] val = new short[n];
@@ -1658,7 +1666,6 @@ public final class Parcel {
@Nullable
public final char[] createCharArray() {
int N = readInt();
- // Assuming size of 2 byte for char.
ensureWithinMemoryLimit(SIZE_CHAR, N);
if (N >= 0 && N <= (dataAvail() >> 2)) {
char[] val = new char[N];
@@ -1697,7 +1704,6 @@ public final class Parcel {
@Nullable
public final int[] createIntArray() {
int N = readInt();
- // Assuming size of 4 byte for int.
ensureWithinMemoryLimit(SIZE_INT, N);
if (N >= 0 && N <= (dataAvail() >> 2)) {
int[] val = new int[N];
@@ -1736,7 +1742,6 @@ public final class Parcel {
@Nullable
public final long[] createLongArray() {
int N = readInt();
- // Assuming size of 8 byte for long.
ensureWithinMemoryLimit(SIZE_LONG, N);
// >>3 because stored longs are 64 bits
if (N >= 0 && N <= (dataAvail() >> 3)) {
@@ -1776,7 +1781,6 @@ public final class Parcel {
@Nullable
public final float[] createFloatArray() {
int N = readInt();
- // Assuming size of 4 byte for float.
ensureWithinMemoryLimit(SIZE_FLOAT, N);
// >>2 because stored floats are 4 bytes
if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1816,7 +1820,6 @@ public final class Parcel {
@Nullable
public final double[] createDoubleArray() {
int N = readInt();
- // Assuming size of 8 byte for double.
ensureWithinMemoryLimit(SIZE_DOUBLE, N);
// >>3 because stored doubles are 8 bytes
if (N >= 0 && N <= (dataAvail() >> 3)) {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6919a76b84ba..7c8ea93894a5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -646,9 +646,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
char saveResolvedClassesDelayMsOptsBuf[
sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
char profileMinSavePeriodOptsBuf[sizeof("-Xps-min-save-period-ms:")-1 + PROPERTY_VALUE_MAX];
- char profileMinFirstSaveOptsBuf[
- sizeof("-Xps-min-first-save-ms:")-1 + PROPERTY_VALUE_MAX];
- char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
+ char profileMinFirstSaveOptsBuf[sizeof("-Xps-min-first-save-ms:") - 1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeVdex[
sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeOdex[
@@ -861,13 +859,8 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
jitprithreadweightOptBuf,
"-Xjitprithreadweight:");
- parseRuntimeOption("dalvik.vm.jittransitionweight",
- jittransitionweightOptBuf,
+ parseRuntimeOption("dalvik.vm.jittransitionweight", jittransitionweightOptBuf,
"-Xjittransitionweight:");
- /*
- * Madvise related options.
- */
- parseRuntimeOption("dalvik.vm.madvise-random", madviseRandomOptsBuf, "-XX:MadviseRandomAccess:");
/*
* Use default platform configuration as limits for madvising,
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index b24dc8a9e63b..8bc52b874ae0 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -974,7 +974,7 @@ static jint android_os_Binder_getCallingUid()
return IPCThreadState::self()->getCallingUid();
}
-static jboolean android_os_Binder_isDirectlyHandlingTransaction() {
+static jboolean android_os_Binder_isDirectlyHandlingTransactionNative() {
return getCurrentServingCall() == BinderCallType::BINDER;
}
@@ -1082,7 +1082,8 @@ static const JNINativeMethod gBinderMethods[] = {
// @CriticalNative
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
// @CriticalNative
- { "isDirectlyHandlingTransaction", "()Z", (void*)android_os_Binder_isDirectlyHandlingTransaction },
+ { "isDirectlyHandlingTransactionNative", "()Z",
+ (void*)android_os_Binder_isDirectlyHandlingTransactionNative },
// @CriticalNative
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
// @CriticalNative
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 277bdf388188..35dc8e37aaab 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2328,6 +2328,8 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.NFC_TRANSACTION_EVENT"
+ android:description="@string/permdesc_nfcTransactionEvent"
+ android:label="@string/permlab_nfcTransactionEvent"
android:protectionLevel="normal" />
<!-- Allows applications to receive NFC preferred payment service information.
@@ -4237,11 +4239,11 @@
android:protectionLevel="internal|preinstalled" />
<!-- Allows an application to subscribe to keyguard locked (i.e., showing) state.
- <p>Protection level: internal|role
- <p>Intended for use by ROLE_ASSISTANT only.
+ <p>Protection level: signature|role
+ <p>Intended for use by ROLE_ASSISTANT and signature apps only.
-->
<permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
- android:protectionLevel="internal|role"/>
+ android:protectionLevel="signature|role"/>
<!-- Must be required by a {@link android.service.autofill.AutofillService},
to ensure that only the system can bind to it.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8a1b74d33065..5a1e0e8d6d47 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1604,6 +1604,12 @@
with Near Field Communication (NFC) tags, cards, and readers.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_nfcTransactionEvent">Secure Element transaction event</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_nfcTransactionEvent">Allows the app to receive information about
+ transactions happening on a Secure Element.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_disableKeyguard">disable your screen lock</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_disableKeyguard">Allows the app to disable the
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index e2fe87b4cfe3..4b993fadc1e0 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -246,4 +246,93 @@ public class ParcelTest {
assertThrows(IllegalArgumentException.class, () -> Parcel.compareData(pA, -1, pB, iB, 0));
assertThrows(IllegalArgumentException.class, () -> Parcel.compareData(pA, 0, pB, -1, 0));
}
+
+ /***
+ * Tests for b/205282403
+ * This test checks if allocations made over limit of 1MB for primitive types
+ * and 1M length for complex objects are not allowed.
+ */
+ @Test
+ public void testAllocationsOverLimit_whenOverLimit_throws() {
+ Binder.setIsDirectlyHandlingTransactionOverride(true);
+ Parcel p = Parcel.obtain();
+ p.setDataPosition(0);
+ p.writeInt(Integer.MAX_VALUE);
+
+ p.setDataPosition(0);
+ assertThrows(BadParcelableException.class, () ->p.createBooleanArray());
+
+ p.setDataPosition(0);
+ assertThrows(BadParcelableException.class, () ->p.createCharArray());
+
+ p.setDataPosition(0);
+ assertThrows(BadParcelableException.class, () ->p.createIntArray());
+
+ p.setDataPosition(0);
+ assertThrows(BadParcelableException.class, () ->p.createLongArray());
+
+ p.setDataPosition(0);
+ assertThrows(BadParcelableException.class, () ->p.createBinderArray());
+
+ int[] dimensions = new int[]{Integer.MAX_VALUE, 100, 100};
+ p.setDataPosition(0);
+ assertThrows(BadParcelableException.class,
+ () -> p.createFixedArray(int[][][].class, dimensions));
+
+ p.setDataPosition(0);
+ assertThrows(BadParcelableException.class,
+ () -> p.createFixedArray(String[][][].class, dimensions));
+
+ p.setDataPosition(0);
+ assertThrows(BadParcelableException.class,
+ () -> p.createFixedArray(IBinder[][][].class, dimensions));
+
+ p.recycle();
+ Binder.setIsDirectlyHandlingTransactionOverride(false);
+ }
+
+ /***
+ * Tests for b/205282403
+ * This test checks if allocations made under limit of 1MB for primitive types
+ * and 1M length for complex objects are allowed.
+ */
+ @Test
+ public void testAllocations_whenWithinLimit() {
+ Binder.setIsDirectlyHandlingTransactionOverride(true);
+ Parcel p = Parcel.obtain();
+ p.setDataPosition(0);
+ p.writeInt(100000);
+
+ p.setDataPosition(0);
+ p.createByteArray();
+
+ p.setDataPosition(0);
+ p.createCharArray();
+
+ p.setDataPosition(0);
+ p.createIntArray();
+
+ p.setDataPosition(0);
+ p.createLongArray();
+
+ p.setDataPosition(0);
+ p.createBinderArray();
+
+ int[] dimensions = new int[]{ 100, 100, 100 };
+
+ p.setDataPosition(0);
+ int[][][] data = new int[100][100][100];
+ p.writeFixedArray(data, 0, dimensions);
+ p.setDataPosition(0);
+ p.createFixedArray(int[][][].class, dimensions);
+
+ p.setDataPosition(0);
+ IBinder[][][] parcelables = new IBinder[100][100][100];
+ p.writeFixedArray(parcelables, 0, dimensions);
+ p.setDataPosition(0);
+ p.createFixedArray(IBinder[][][].class, dimensions);
+
+ p.recycle();
+ Binder.setIsDirectlyHandlingTransactionOverride(false);
+ }
}
diff --git a/core/tests/fuzzers/ParcelFuzzer/Android.bp b/core/tests/fuzzers/ParcelFuzzer/Android.bp
index b71a06e3572e..eff19853fc7e 100644
--- a/core/tests/fuzzers/ParcelFuzzer/Android.bp
+++ b/core/tests/fuzzers/ParcelFuzzer/Android.bp
@@ -34,6 +34,7 @@ java_fuzz {
"smoreland@google.com",
"waghpawan@google.com",
],
+ triage_assignee: "cobark@google.com", // TODO(b/280770893)
// Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
hotlists: ["4637097"],
},
diff --git a/core/tests/fuzzers/java_service_fuzzer/Android.bp b/core/tests/fuzzers/java_service_fuzzer/Android.bp
index 6acb19852210..97538a5035f3 100644
--- a/core/tests/fuzzers/java_service_fuzzer/Android.bp
+++ b/core/tests/fuzzers/java_service_fuzzer/Android.bp
@@ -42,6 +42,7 @@ java_fuzz {
"smoreland@google.com",
"waghpawan@google.com",
],
+ triage_assignee: "cobark@google.com", // TODO(b/261539788)
// Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
hotlists: ["4637097"],
},
diff --git a/libs/androidfw/include/androidfw/ObbFile.h b/libs/androidfw/include/androidfw/ObbFile.h
index 3dbf997dc367..38ece5c1546f 100644
--- a/libs/androidfw/include/androidfw/ObbFile.h
+++ b/libs/androidfw/include/androidfw/ObbFile.h
@@ -43,10 +43,6 @@ public:
bool removeFrom(const char* filename);
bool removeFrom(int fd);
- const char* getFileName() const {
- return mFileName;
- }
-
const String8 getPackageName() const {
return mPackageName;
}
@@ -127,8 +123,6 @@ private:
/* The encryption salt. */
unsigned char mSalt[8];
- const char* mFileName;
-
size_t mFooterStart;
bool parseObbFile(int fd);
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 9e0a6eb476d3..294ca9cfd3b4 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -86,7 +86,7 @@ protected:
return nullptr;
}
- String16 fileName(obbFile->getFileName());
+ String16 fileName(canonicalPath);
String16 packageName(obbFile->getPackageName());
size_t length;
const unsigned char* salt = obbFile->getSalt(&length);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 879181f0fbd6..2d958a919793 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -6,6 +6,7 @@ import android.annotation.SuppressLint;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -162,14 +163,21 @@ public class BluetoothUtils {
resources, ((BitmapDrawable) pair.first).getBitmap()), pair.second);
}
+ int hashCode;
+ if ((cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID)) {
+ hashCode = new Integer(cachedDevice.getGroupId()).hashCode();
+ } else {
+ hashCode = cachedDevice.getAddress().hashCode();
+ }
+
return new Pair<>(buildBtRainbowDrawable(context,
- pair.first, cachedDevice.getAddress().hashCode()), pair.second);
+ pair.first, hashCode), pair.second);
}
/**
* Build Bluetooth device icon with rainbow
*/
- public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
+ private static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
int hashCode) {
final Resources resources = context.getResources();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 6748e9cdb52e..6ef5d7347d31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1502,8 +1502,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
refresh();
}
- return new Pair<>(BluetoothUtils.buildBtRainbowDrawable(
- mContext, pair.first, getAddress().hashCode()), pair.second);
+ return BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, this);
}
void releaseLruCache() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index a16f30475654..8658c64e7993 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -52,7 +52,7 @@ public class KeyguardSimPinViewController
private ProgressDialog mSimUnlockProgressDialog;
private CheckSimPin mCheckSimPinThread;
- private int mRemainingAttempts;
+ private int mRemainingAttempts = -1;
// Below flag is set to true during power-up or when a new SIM card inserted on device.
// When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
// be displayed to inform user about the number of remaining PIN attempts left.
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 27215b2fbf30..b0c1d05b0dd4 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -44,7 +44,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
private static final String TAG = "DynamicSystemService";
private static final long MINIMUM_SD_MB = (30L << 10);
private static final int GSID_ROUGH_TIMEOUT_MS = 8192;
- private static final String PATH_DEFAULT = "/data/gsi/";
+ private static final String PATH_DEFAULT = "/data/gsi/dsu/";
private Context mContext;
private String mInstallPath, mDsuSlot;
private volatile IGsiService mGsiService;
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 42a5af7fdce3..3a8ef3370a53 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -71,6 +71,7 @@ public final class UsbAlsaManager {
private static final int USB_VENDORID_SONY = 0x054C;
private static final int USB_PRODUCTID_PS4CONTROLLER_ZCT1 = 0x05C4;
private static final int USB_PRODUCTID_PS4CONTROLLER_ZCT2 = 0x09CC;
+ private static final int USB_PRODUCTID_PS5CONTROLLER = 0x0CE6;
private static final int USB_DENYLIST_OUTPUT = 0x0001;
private static final int USB_DENYLIST_INPUT = 0x0002;
@@ -93,6 +94,9 @@ public final class UsbAlsaManager {
USB_DENYLIST_OUTPUT),
new DenyListEntry(USB_VENDORID_SONY,
USB_PRODUCTID_PS4CONTROLLER_ZCT2,
+ USB_DENYLIST_OUTPUT),
+ new DenyListEntry(USB_VENDORID_SONY,
+ USB_PRODUCTID_PS5CONTROLLER,
USB_DENYLIST_OUTPUT));
private static boolean isDeviceDenylisted(int vendorId, int productId, int flags) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 0170c4a4c54b..2f5d8d19ebe5 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -206,6 +206,11 @@ class LinkCommand : public Command {
AddOptionalFlag("--compile-sdk-version-name",
"Version name to inject into the AndroidManifest.xml if none is present.",
&options_.manifest_fixer_options.compile_sdk_version_codename);
+ AddOptionalSwitch(
+ "--no-compile-sdk-metadata",
+ "Suppresses output of compile SDK-related attributes in AndroidManifest.xml,\n"
+ "including android:compileSdkVersion and platformBuildVersion.",
+ &options_.manifest_fixer_options.no_compile_sdk_metadata);
AddOptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
&shared_lib_);
AddOptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib_);
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 42191912775a..f8e734724018 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -635,7 +635,7 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) {
root->InsertChild(0, std::move(uses_sdk));
}
- if (options_.compile_sdk_version) {
+ if (!options_.no_compile_sdk_metadata && options_.compile_sdk_version) {
xml::Attribute* attr = root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersion");
// Make sure we un-compile the value if it was set to something else.
@@ -647,10 +647,9 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) {
// Make sure we un-compile the value if it was set to something else.
attr->compiled_value = {};
attr->value = options_.compile_sdk_version.value();
-
}
- if (options_.compile_sdk_version_codename) {
+ if (!options_.no_compile_sdk_metadata && options_.compile_sdk_version_codename) {
xml::Attribute* attr =
root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename");
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index a8707d9d8623..70bfcfc1365a 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -68,11 +68,12 @@ struct ManifestFixerOptions {
std::optional<std::string> revision_code_default;
// The version of the framework being compiled against to set for 'android:compileSdkVersion' in
- // the <manifest> tag.
+ // the <manifest> tag. Not used if no_compile_sdk_metadata is set.
std::optional<std::string> compile_sdk_version;
// The version codename of the framework being compiled against to set for
- // 'android:compileSdkVersionCodename' in the <manifest> tag.
+ // 'android:compileSdkVersionCodename' in the <manifest> tag. Not used if no_compile_sdk_metadata
+ // is set.
std::optional<std::string> compile_sdk_version_codename;
// Whether validation errors should be treated only as warnings. If this is 'true', then an
@@ -85,6 +86,9 @@ struct ManifestFixerOptions {
// Whether to replace the manifest version with the the command line version
bool replace_version = false;
+
+ // Whether to suppress `android:compileSdkVersion*` and `platformBuildVersion*` attributes.
+ bool no_compile_sdk_metadata = false;
};
// Verifies that the manifest is correctly formed and inserts defaults where specified with
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 098d0be7f87d..9204d2276a17 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -892,6 +892,35 @@ TEST_F(ManifestFixerTest, InsertCompileSdkVersions) {
EXPECT_THAT(attr->value, StrEq("P"));
}
+TEST_F(ManifestFixerTest, DoNotInsertCompileSdkVersions) {
+ std::string input = R"(<manifest package="com.pkg" />)";
+ ManifestFixerOptions options;
+ options.no_compile_sdk_metadata = true;
+ options.compile_sdk_version = {"28"};
+ options.compile_sdk_version_codename = {"P"};
+
+ std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options);
+ ASSERT_THAT(manifest, NotNull());
+
+ // There should be a declaration of kSchemaAndroid, even when the input
+ // didn't have one.
+ EXPECT_EQ(manifest->root->namespace_decls.size(), 1);
+ EXPECT_EQ(manifest->root->namespace_decls[0].prefix, "android");
+ EXPECT_EQ(manifest->root->namespace_decls[0].uri, xml::kSchemaAndroid);
+
+ xml::Attribute* attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersion");
+ ASSERT_THAT(attr, IsNull());
+
+ attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename");
+ ASSERT_THAT(attr, IsNull());
+
+ attr = manifest->root->FindAttribute("", "platformBuildVersionCode");
+ ASSERT_THAT(attr, IsNull());
+
+ attr = manifest->root->FindAttribute("", "platformBuildVersionName");
+ ASSERT_THAT(attr, IsNull());
+}
+
TEST_F(ManifestFixerTest, OverrideCompileSdkVersions) {
std::string input = R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"