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());