Enable color resources loader to be created using FRRO
Adjust OverlayManagerImpl to enable creating self-targeting FRRO with
"android" as a target package and without a target overlayable. Then
adjust ColorResources#create to overlay android colors using a
self-targeting FRRO, instead of using ARSC file manipulation.
Flag: EXEMPT bug fix
Bug: 365751758
Test: Manually verified colors are overlaid in Customization Picker
Launcher preview
Change-Id: If3dd47323cd1b2db51e46725399ecd2227405140
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index ed965b3..6db7dfe 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -78,7 +78,8 @@
/**
* Applications can use OverlayManager to create overlays to overlay on itself resources. The
- * overlay target is itself and the work range is only in caller application.
+ * overlay target is itself, or the Android package, and the work range is only in caller
+ * application.
*
* <p>In {@link android.content.Context#getSystemService(String)}, it crashes because of {@link
* java.lang.NullPointerException} if the parameter is OverlayManager. if the self-targeting is
@@ -401,7 +402,7 @@
}
/**
- * Get the related information of overlays for {@code targetPackageName}.
+ * Get the related information of self-targeting overlays for {@code targetPackageName}.
*
* @param targetPackageName the target package name
* @return a list of overlay information
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
index becd0ea..87b2e93 100644
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -209,6 +209,7 @@
*/
public static final class Builder {
private final List<Request> mRequests = new ArrayList<>();
+ private boolean mSelfTargeting = false;
/**
* Request that an overlay package be enabled and change its loading
@@ -246,6 +247,18 @@
}
/**
+ * Request that an overlay package be self-targeting. Self-targeting overlays enable
+ * applications to overlay on itself resources. The overlay target is itself, or the Android
+ * package, and the work range is only in caller application.
+ * @param selfTargeting whether the overlay is self-targeting, the default is false.
+ * @hide
+ */
+ public Builder setSelfTargeting(boolean selfTargeting) {
+ mSelfTargeting = selfTargeting;
+ return this;
+ }
+
+ /**
* Registers the fabricated overlay with the overlay manager so it can be enabled and
* disabled for any user.
*
@@ -286,7 +299,7 @@
*/
@NonNull
public OverlayManagerTransaction build() {
- return new OverlayManagerTransaction(mRequests, false /* selfTargeting */);
+ return new OverlayManagerTransaction(mRequests, mSelfTargeting);
}
}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 6fd4d01..347bebd 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -75,7 +75,10 @@
private static final String TAG = "AssetManager";
private static final boolean DEBUG_REFS = false;
- private static final String FRAMEWORK_APK_PATH = getFrameworkApkPath();
+ /**
+ * @hide
+ */
+ public static final String FRAMEWORK_APK_PATH = getFrameworkApkPath();
private static final String FRAMEWORK_APK_PATH_DEVICE = "/system/framework/framework-res.apk";
private static final String FRAMEWORK_APK_PATH_RAVENWOOD = "ravenwood-data/framework-res.apk";
diff --git a/core/java/android/content/res/loader/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java
index b097bc0..830b7e0 100644
--- a/core/java/android/content/res/loader/ResourcesProvider.java
+++ b/core/java/android/content/res/loader/ResourcesProvider.java
@@ -90,8 +90,6 @@
throws IOException {
Objects.requireNonNull(overlayInfo);
Preconditions.checkArgument(overlayInfo.isFabricated(), "Not accepted overlay");
- Preconditions.checkStringNotEmpty(
- overlayInfo.getTargetOverlayableName(), "Without overlayable name");
final String overlayName =
OverlayManagerImpl.checkOverlayNameValid(overlayInfo.getOverlayName());
final String path =
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index d7b5211..d65bf7c 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -20,6 +20,7 @@
import static android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO;
import static android.appwidget.flags.Flags.drawDataParcel;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
+import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
import static android.util.proto.ProtoInputStream.NO_MORE_FIELDS;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
@@ -54,6 +55,10 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.ServiceConnection;
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.ColorStateList;
@@ -79,14 +84,12 @@
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.Trace;
import android.os.UserHandle;
-import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -128,11 +131,8 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -8546,12 +8546,8 @@
* @hide
*/
public static final class ColorResources {
- // Set of valid colors resources.
- private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0;
- private static final int LAST_RESOURCE_COLOR_ID =
- android.R.color.system_error_1000;
- // Size, in bytes, of an entry in the array of colors in an ARSC file.
- private static final int ARSC_ENTRY_SIZE = 16;
+ private static final String OVERLAY_NAME = "remote_views_color_resources";
+ private static final String OVERLAY_TARGET_PACKAGE_NAME = "android";
private final ResourcesLoader mLoader;
private final SparseIntArray mColorMapping;
@@ -8585,44 +8581,6 @@
}
/**
- * Creates the compiled resources content from the asset stored in the APK.
- *
- * The asset is a compiled resource with the correct resources name and correct ids, only
- * the values are incorrect. The last value is at the very end of the file. The resources
- * are in an array, the array's entries are 16 bytes each. We use this to work out the
- * location of all the positions of the various resources.
- */
- @Nullable
- private static byte[] createCompiledResourcesContent(Context context,
- SparseIntArray colorResources) throws IOException {
- byte[] content;
- try (InputStream input = context.getResources().openRawResource(
- com.android.internal.R.raw.remote_views_color_resources)) {
- ByteArrayOutputStream rawContent = readFileContent(input);
- content = rawContent.toByteArray();
- }
- int valuesOffset =
- content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4;
- if (valuesOffset < 0) {
- Log.e(LOG_TAG, "ARSC file for theme colors is invalid.");
- return null;
- }
- for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID;
- colorRes++) {
- // The last 2 bytes are the index in the color array.
- int index = colorRes & 0xffff;
- int offset = valuesOffset + index * ARSC_ENTRY_SIZE;
- int value = colorResources.get(colorRes, context.getColor(colorRes));
- // Write the 32 bit integer in little endian
- for (int b = 0; b < 4; b++) {
- content[offset + b] = (byte) (value & 0xff);
- value >>= 8;
- }
- }
- return content;
- }
-
- /**
* Adds a resource loader for theme colors to the given context.
*
* @param context Context of the view hosting the widget.
@@ -8633,31 +8591,38 @@
@Nullable
public static ColorResources create(Context context, SparseIntArray colorMapping) {
try {
- byte[] contentBytes = createCompiledResourcesContent(context, colorMapping);
- if (contentBytes == null) {
+ String owningPackage = context.getPackageName();
+ FabricatedOverlay overlay = new FabricatedOverlay.Builder(owningPackage,
+ OVERLAY_NAME, OVERLAY_TARGET_PACKAGE_NAME).build();
+
+ for (int i = 0; i < colorMapping.size(); i++) {
+ overlay.setResourceValue(
+ context.getResources().getResourceName(colorMapping.keyAt(i)),
+ TYPE_INT_COLOR_ARGB8, colorMapping.valueAt(i), null);
+ }
+ OverlayManager overlayManager = context.getSystemService(OverlayManager.class);
+ OverlayManagerTransaction.Builder transaction =
+ new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .setSelfTargeting(true);
+ overlayManager.commit(transaction.build());
+
+ OverlayInfo overlayInfo =
+ overlayManager.getOverlayInfosForTarget(OVERLAY_TARGET_PACKAGE_NAME)
+ .stream()
+ .filter(info -> TextUtils.equals(info.overlayName, OVERLAY_NAME)
+ && TextUtils.equals(info.packageName, owningPackage))
+ .findFirst()
+ .orElse(null);
+ if (overlayInfo == null) {
+ Log.e(LOG_TAG, "Failed to get overlay info ", new Throwable());
return null;
}
- FileDescriptor arscFile = null;
- try {
- arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */);
- // Note: This must not be closed through the OutputStream.
- try (OutputStream pipeWriter = new FileOutputStream(arscFile)) {
- pipeWriter.write(contentBytes);
-
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) {
- ResourcesLoader colorsLoader = new ResourcesLoader();
- colorsLoader.addProvider(ResourcesProvider
- .loadFromTable(pfd, null /* assetsProvider */));
- return new ColorResources(colorsLoader, colorMapping.clone());
- }
- }
- } finally {
- if (arscFile != null) {
- Os.close(arscFile);
- }
- }
- } catch (Exception ex) {
- Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex);
+ ResourcesLoader colorsLoader = new ResourcesLoader();
+ colorsLoader.addProvider(ResourcesProvider.loadOverlay(overlayInfo));
+ return new ColorResources(colorsLoader, colorMapping.clone());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to add theme color overlay into loader", e);
}
return null;
}
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 07e178c..38593b4 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackagePartitions;
+import android.content.res.AssetManager;
import android.os.Build;
import android.os.Trace;
import android.util.ArrayMap;
@@ -533,7 +534,7 @@
*/
@NonNull
public String[] createImmutableFrameworkIdmapsInZygote() {
- final String targetPath = "/system/framework/framework-res.apk";
+ final String targetPath = AssetManager.FRAMEWORK_APK_PATH;
final ArrayList<String> idmapPaths = new ArrayList<>();
final ArrayList<IdmapInvocation> idmapInvocations =
getImmutableFrameworkOverlayIdmapInvocations();
diff --git a/core/java/com/android/internal/content/om/OverlayManagerImpl.java b/core/java/com/android/internal/content/om/OverlayManagerImpl.java
index c462449..fa5cf2a 100644
--- a/core/java/com/android/internal/content/om/OverlayManagerImpl.java
+++ b/core/java/com/android/internal/content/om/OverlayManagerImpl.java
@@ -35,6 +35,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.parsing.FrameworkParsingPackageUtils;
+import android.content.res.AssetManager;
import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
import android.os.FabricatedOverlayInternalEntry;
@@ -60,8 +61,8 @@
import java.util.Objects;
/**
- * This class provides the functionalities of registering an overlay, unregistering an overlay, and
- * getting the list of overlays information.
+ * This class provides the functionalities for managing self-targeting overlays, including
+ * registering an overlay, unregistering an overlay, and getting the list of overlays information.
*/
public class OverlayManagerImpl {
private static final String TAG = "OverlayManagerImpl";
@@ -234,14 +235,17 @@
Preconditions.checkArgument(!entryList.isEmpty(), "overlay entries shouldn't be empty");
final String overlayName = checkOverlayNameValid(overlayInternal.overlayName);
checkPackageName(overlayInternal.packageName);
- checkPackageName(overlayInternal.targetPackageName);
- Preconditions.checkStringNotEmpty(
- overlayInternal.targetOverlayable,
- "Target overlayable should be neither null nor empty string.");
+ Preconditions.checkStringNotEmpty(overlayInternal.targetPackageName);
final ApplicationInfo applicationInfo = mContext.getApplicationInfo();
- final String targetPackage = Preconditions.checkStringNotEmpty(
- applicationInfo.getBaseCodePath());
+ String targetPackage = null;
+ if (TextUtils.equals(overlayInternal.targetPackageName, "android")) {
+ targetPackage = AssetManager.FRAMEWORK_APK_PATH;
+ } else {
+ targetPackage = Preconditions.checkStringNotEmpty(
+ applicationInfo.getBaseCodePath());
+ }
+
final Path frroPath = mBasePath.resolve(overlayName + FRRO_EXTENSION);
final Path idmapPath = mBasePath.resolve(overlayName + IDMAP_EXTENSION);
diff --git a/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java b/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java
index 40d0bef..28d6545 100644
--- a/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java
+++ b/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java
@@ -210,21 +210,6 @@
}
@Test
- public void registerOverlay_forAndroidPackage_shouldFail() {
- FabricatedOverlayInternal overlayInternal =
- createOverlayWithName(
- mOverlayName,
- SYSTEM_APP_OVERLAYABLE,
- "android",
- List.of(Pair.create("color/white", Pair.create(null, Color.BLACK))));
-
- assertThrows(
- "Wrong target package name",
- IllegalArgumentException.class,
- () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal));
- }
-
- @Test
public void getOverlayInfosForTarget_defaultShouldBeZero() {
List<OverlayInfo> overlayInfos =
mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName());