Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 01445c29 authored by Marcelo Arteiro's avatar Marcelo Arteiro
Browse files

OverlayManager updates for upcoming ThemeService

Create the `OverlayManagerInternal` interface to provide a non-Binder API for other system services to call into the `OverlayManagerService`.

This change also includes:
* A new `getEntries` method on `FabricatedOverlay` to filter overlay entries by resource type.
* Allowing `SYSTEM_UID` to register fabricated overlays.

Bug: 333694176
Test: FabricatedOverlaysTest
Flag: android.server.enable_theme_service
Change-Id: I0c4a609db025fbe59f82e816da859f825f9bb7e2
parent 4a0ebdcb
Loading
Loading
Loading
Loading
+47 −2
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.os.FabricatedOverlayInternal;
import android.os.FabricatedOverlayInternalEntry;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.TypedValue;

import com.android.internal.content.om.OverlayManagerImpl;
@@ -34,7 +35,11 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * FabricatedOverlay describes the content of Fabricated Runtime Resource Overlay (FRRO) that is
@@ -339,7 +344,12 @@ public class FabricatedOverlay {
    }

    final FabricatedOverlayInternal mOverlay;
    private FabricatedOverlay(FabricatedOverlayInternal overlay) {

    /**
     * @param overlay The internal parcelable to wrap.
     * @hide
     */
    public FabricatedOverlay(FabricatedOverlayInternal overlay) {
        mOverlay = overlay;
    }

@@ -396,6 +406,41 @@ public class FabricatedOverlay {
        return mOverlay.targetOverlayable;
    }


    /**
     * Exposes the internal FabricatedOverlayInternal so other services can use the parcelable
     *
     * @return instance of FabricatedOverlayInternal
     * @hide
     */
    public FabricatedOverlayInternal getInternal() {
        return mOverlay;
    }

    /**
     * Retrieves a list of overlay entries that match any of the specified resource data types.
     *
     * @param dataTypes A variable number of resource data types to filter by, as defined in
     *                  {@link TypedValue}.
     * @return A non-null list of {@link FabricatedOverlayInternalEntry} objects that match any of
     *         the given types. The list will be empty if no entries match or if no data types
     *         are provided.
     * @hide
     */
    @NonNull
    public List<FabricatedOverlayInternalEntry> getEntries(int... dataTypes) {
        if (mOverlay.entries == null || dataTypes == null || dataTypes.length == 0) {
            return Collections.emptyList();
        }
        final Set<Integer> typeSet = new ArraySet<>();
        for (int type : dataTypes) {
            typeSet.add(type);
        }
        return mOverlay.entries.stream()
                .filter(entry -> typeSet.contains(entry.dataType))
                .collect(Collectors.toList());
    }

    /**
     * Ensure the resource name is in the form [package]:type/entry.
     *
+39 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.overlaytest;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.testng.Assert.assertFalse;
@@ -30,6 +31,8 @@ import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
import android.content.om.OverlayManagerTransaction;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.FabricatedOverlayInternalEntry;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -45,6 +48,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
@@ -319,6 +323,41 @@ public class FabricatedOverlaysTest {
        waitForFloatResourceValue(overlaidValue);
    }

    @Test
    public void testGetEntries() {
        final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
                mContext.getPackageName(), "getEntriesTest", mContext.getPackageName())
                .setResourceValue("integer/myInt", TypedValue.TYPE_INT_DEC, 123)
                .setResourceValue("color/myColor1", TypedValue.TYPE_INT_COLOR_ARGB8, Color.RED)
                .setResourceValue("color/myColor2", TypedValue.TYPE_INT_COLOR_RGB8, Color.GREEN)
                .build();

        // Test single type
        List<FabricatedOverlayInternalEntry> colorEntries =
                overlay.getEntries(TypedValue.TYPE_INT_COLOR_ARGB8);
        assertNotNull(colorEntries);
        assertEquals(1, colorEntries.size());
        assertEquals("color/myColor1", colorEntries.get(0).resourceName);
        assertEquals(TypedValue.TYPE_INT_COLOR_ARGB8, colorEntries.get(0).dataType);

        // Test multiple types
        List<FabricatedOverlayInternalEntry> allColorEntries = overlay.getEntries(
                TypedValue.TYPE_INT_COLOR_ARGB8, TypedValue.TYPE_INT_COLOR_RGB8);
        assertNotNull(allColorEntries);
        assertEquals(2, allColorEntries.size());

        // Test non-existent type
        List<FabricatedOverlayInternalEntry> stringEntries =
                overlay.getEntries(TypedValue.TYPE_STRING);
        assertNotNull(stringEntries);
        assertTrue(stringEntries.isEmpty());

        // Test empty/null args
        List<FabricatedOverlayInternalEntry> emptyEntries = overlay.getEntries();
        assertNotNull(emptyEntries);
        assertTrue(emptyEntries.isEmpty());
    }

    private void waitForIntResourceValue(final int expectedValue) throws TimeoutException {
        waitForResourceValue(expectedValue, TEST_INT_RESOURCE, id -> mResources.getInteger(id));
    }
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.om;

import android.annotation.NonNull;
import android.content.om.IOverlayManager;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManagerTransaction;
import android.os.UserHandle;

import java.util.List;

public interface OverlayManagerInternal {

    /**
     * Provides the local service.
     *
     * @return An IOverlayManager
     */
    IOverlayManager getService();

    /**
     * Get the related information of overlays for {@code targetPackageName}.
     *
     * @param targetPackageName the target package name
     * @return a list of overlay information
     */
    List<OverlayInfo> getOverlayInfosForTarget(@NonNull String targetPackageName,
            @NonNull UserHandle user);

    /**
     * Returns information about the overlay with the given package name for
     * the specified user.
     *
     * @param packageName The name of the package.
     * @param userHandle  The user to get the OverlayInfos for.
     * @return An OverlayInfo object; if no overlays exist with the
     * requested package name, null is returned.
     * @hide
     */
    OverlayInfo getOverlayInfo(@NonNull String packageName, @NonNull UserHandle userHandle);

    /**
     * Returns information about the overlay represented by the identifier for the specified user.
     *
     * @param overlay    the identifier representing the overlay
     * @param userHandle the user of which to get overlay state info
     * @return the overlay info or null if the overlay cannot be found
     * @hide
     */
    OverlayInfo getOverlayInfo(@NonNull OverlayIdentifier overlay, @NonNull UserHandle userHandle);

    /**
     * Commit the overlay manager transaction.
     *
     * <p>Applications can register overlays and unregister the registered overlays in an atomic
     * operation via {@link OverlayManagerTransaction}.
     *
     * @param transaction the series of overlay related requests to perform
     * @throws Exception if not all the requests could be successfully completed.
     * @see OverlayManagerTransaction
     */
    void commit(@NonNull OverlayManagerTransaction transaction);
}
+49 −2
Original line number Diff line number Diff line
@@ -314,7 +314,7 @@ public final class OverlayManagerService extends SystemService {
            onStartUser(UserHandle.USER_SYSTEM);

            publishBinderService(Context.OVERLAY_SERVICE, mService);
            publishLocalService(OverlayManagerService.class, this);
            publishLocalService(OverlayManagerInternal.class, mInternal);
        } finally {
            traceEnd(TRACE_TAG_RRO);
        }
@@ -993,7 +993,8 @@ public final class OverlayManagerService extends SystemService {
                // Enforce that the calling process can only register and unregister fabricated
                // overlays using its package name.
                final String pkgName = request.overlay.getPackageName();
                if (callingUid != Process.ROOT_UID && !ArrayUtils.contains(
                if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
                        && !ArrayUtils.contains(
                        mPackageManager.getPackagesForUid(callingUid), pkgName)) {
                    throw new IllegalArgumentException("UID " + callingUid + " does not own "
                            + "packageName " + pkgName);
@@ -1199,6 +1200,52 @@ public final class OverlayManagerService extends SystemService {

    };

    private final OverlayManagerInternal mInternal = new OverlayManagerInternal() {
        public IOverlayManager getService() {
            return IOverlayManager.Stub.asInterface(mService);
        }

        @Override
        public List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName,
                @NonNull UserHandle user) {
            try {
                return getService().getOverlayInfosForTarget(targetPackageName,
                        user.getIdentifier());
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public OverlayInfo getOverlayInfo(@NonNull final String packageName,
                @NonNull final UserHandle userHandle) {
            try {
                return getService().getOverlayInfo(packageName, userHandle.getIdentifier());
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public OverlayInfo getOverlayInfo(@NonNull OverlayIdentifier overlay,
                @NonNull UserHandle userHandle) {
            try {
                return getService().getOverlayInfoByIdentifier(overlay, userHandle.getIdentifier());
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void commit(@NonNull OverlayManagerTransaction transaction) {
            try {
                getService().commit(transaction);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }
    };

    private static final class PackageManagerHelperImpl implements PackageManagerHelper {
        private static class PackageStateUsers {
            private PackageState mPackageState;