diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl index 86c1aa8228f1e..5b3c9dd933707 100644 --- a/core/java/android/content/om/IOverlayManager.aidl +++ b/core/java/android/content/om/IOverlayManager.aidl @@ -83,16 +83,35 @@ interface IOverlayManager { * @param packageName The name of the overlay package. * @param enable true to enable the overlay, false to disable it. * @param userId The user for which to change the overlay. - * @return true if the system successfully registered the request, false - * otherwise. + * @return true if the system successfully registered the request, false otherwise. */ boolean setEnabled(in String packageName, in boolean enable, in int userId); /** - * Version of setEnabled that will also disable any other overlays for the target package. + * Request that an overlay package is enabled and any other overlay packages with the same + * target package are disabled. + * + * See {@link #setEnabled} for the details on overlay packages. + * + * @param packageName the name of the overlay package to enable. + * @param enabled must be true, otherwise the operation fails. + * @param userId The user for which to change the overlay. + * @return true if the system successfully registered the request, false otherwise. */ boolean setEnabledExclusive(in String packageName, in boolean enable, in int userId); + /** + * Request that an overlay package is enabled and any other overlay packages with the same + * target package and category are disabled. + * + * See {@link #setEnabled} for the details on overlay packages. + * + * @param packageName the name of the overlay package to enable. + * @param userId The user for which to change the overlay. + * @return true if the system successfully registered the request, false otherwise. + */ + boolean setEnabledExclusiveInCategory(in String packageName, in int userId); + /** * Change the priority of the given overlay to be just higher than the * overlay with package name newParentPackageName. Both overlay packages diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java index 8464e26ec6cd2..6e63342698b34 100644 --- a/core/java/android/content/om/OverlayInfo.java +++ b/core/java/android/content/om/OverlayInfo.java @@ -18,6 +18,7 @@ package android.content.om; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -66,14 +67,14 @@ public final class OverlayInfo implements Parcelable { /** * The overlay is currently disabled. It can be enabled. * - * @see IOverlayManager.setEnabled + * @see IOverlayManager#setEnabled */ public static final int STATE_DISABLED = 2; /** * The overlay is currently enabled. It can be disabled. * - * @see IOverlayManager.setEnabled + * @see IOverlayManager#setEnabled */ public static final int STATE_ENABLED = 3; @@ -89,6 +90,11 @@ public final class OverlayInfo implements Parcelable { */ public static final int STATE_OVERLAY_UPGRADING = 5; + /** + * Category for theme overlays. + */ + public static final String CATEGORY_THEME = "android.theme"; + /** * Package name of the overlay package */ @@ -99,6 +105,11 @@ public final class OverlayInfo implements Parcelable { */ public final String targetPackageName; + /** + * Category of the overlay package + */ + public final String category; + /** * Full path to the base APK for this overlay package */ @@ -121,14 +132,15 @@ public final class OverlayInfo implements Parcelable { * @param state the new state for the source OverlayInfo */ public OverlayInfo(@NonNull OverlayInfo source, @State int state) { - this(source.packageName, source.targetPackageName, source.baseCodePath, state, - source.userId); + this(source.packageName, source.targetPackageName, source.category, source.baseCodePath, + state, source.userId); } public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName, - @NonNull String baseCodePath, @State int state, int userId) { + @Nullable String category, @NonNull String baseCodePath, int state, int userId) { this.packageName = packageName; this.targetPackageName = targetPackageName; + this.category = category; this.baseCodePath = baseCodePath; this.state = state; this.userId = userId; @@ -138,6 +150,7 @@ public final class OverlayInfo implements Parcelable { public OverlayInfo(Parcel source) { packageName = source.readString(); targetPackageName = source.readString(); + category = source.readString(); baseCodePath = source.readString(); state = source.readInt(); userId = source.readInt(); @@ -177,6 +190,7 @@ public final class OverlayInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(packageName); dest.writeString(targetPackageName); + dest.writeString(category); dest.writeString(baseCodePath); dest.writeInt(state); dest.writeInt(userId); @@ -275,6 +289,9 @@ public final class OverlayInfo implements Parcelable { if (!targetPackageName.equals(other.targetPackageName)) { return false; } + if (!category.equals(other.category)) { + return false; + } if (!baseCodePath.equals(other.baseCodePath)) { return false; } diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 0342c93bb34ff..627ceb7871f2b 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -362,6 +362,13 @@ public class PackageInfo implements Parcelable { */ public String overlayTarget; + /** + * The overlay category, if any, of this package + * + * @hide + */ + public String overlayCategory; + /** @hide */ public int overlayPriority; @@ -464,6 +471,7 @@ public class PackageInfo implements Parcelable { dest.writeString(restrictedAccountType); dest.writeString(requiredAccountType); dest.writeString(overlayTarget); + dest.writeString(overlayCategory); dest.writeInt(overlayPriority); dest.writeBoolean(mOverlayIsStatic); dest.writeInt(compileSdkVersion); @@ -531,6 +539,7 @@ public class PackageInfo implements Parcelable { restrictedAccountType = source.readString(); requiredAccountType = source.readString(); overlayTarget = source.readString(); + overlayCategory = source.readString(); overlayPriority = source.readInt(); mOverlayIsStatic = source.readBoolean(); compileSdkVersion = source.readInt(); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index dda4167d3c3b2..9e4166eaec1b1 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -676,6 +676,7 @@ public class PackageParser { pi.restrictedAccountType = p.mRestrictedAccountType; pi.requiredAccountType = p.mRequiredAccountType; pi.overlayTarget = p.mOverlayTarget; + pi.overlayCategory = p.mOverlayCategory; pi.overlayPriority = p.mOverlayPriority; pi.mOverlayIsStatic = p.mOverlayIsStatic; pi.compileSdkVersion = p.mCompileSdkVersion; @@ -2073,6 +2074,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestResourceOverlay); pkg.mOverlayTarget = sa.getString( com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage); + pkg.mOverlayCategory = sa.getString( + com.android.internal.R.styleable.AndroidManifestResourceOverlay_category); pkg.mOverlayPriority = sa.getInt( com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority, 0); @@ -6324,6 +6327,7 @@ public class PackageParser { public String mRequiredAccountType; public String mOverlayTarget; + public String mOverlayCategory; public int mOverlayPriority; public boolean mOverlayIsStatic; @@ -6834,6 +6838,7 @@ public class PackageParser { mRestrictedAccountType = dest.readString(); mRequiredAccountType = dest.readString(); mOverlayTarget = dest.readString(); + mOverlayCategory = dest.readString(); mOverlayPriority = dest.readInt(); mOverlayIsStatic = (dest.readInt() == 1); mCompileSdkVersion = dest.readInt(); @@ -6957,6 +6962,7 @@ public class PackageParser { dest.writeString(mRestrictedAccountType); dest.writeString(mRequiredAccountType); dest.writeString(mOverlayTarget); + dest.writeString(mOverlayCategory); dest.writeInt(mOverlayPriority); dest.writeInt(mOverlayIsStatic ? 1 : 0); dest.writeInt(mCompileSdkVersion); diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index a61c8c1dba684..0b72c22d4e272 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -52,6 +52,16 @@ public final class DisplayCutout { private static final String TAG = "DisplayCutout"; private static final String DP_MARKER = "@dp"; + /** + * Category for overlays that allow emulating a display cutout on devices that don't have + * one. + * + * @see android.content.om.IOverlayManager + * @hide + */ + public static final String EMULATION_OVERLAY_CATEGORY = + "com.android.internal.display_cutout_emulation"; + private static final Rect ZERO_RECT = new Rect(); private static final Region EMPTY_REGION = new Region(); diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 287f29615e2be..32fe2b2a72d81 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2558,6 +2558,9 @@ + + + diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml index 71ce6b4332154..426aae969fd41 100644 --- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml +++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml @@ -2,7 +2,9 @@ package="com.android.internal.display.cutout.emulation.narrow" android:versionCode="1" android:versionName="1.0"> - + diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml index 5a93cfb06c6db..368a2d5c17eb9 100644 --- a/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml +++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml @@ -18,7 +18,9 @@ package="com.android.internal.display.cutout.emulation.tall" android:versionCode="1" android:versionName="1.0"> - + diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml index 96bd060286110..b721efe8a7958 100644 --- a/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml +++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml @@ -18,7 +18,9 @@ package="com.android.internal.display.cutout.emulation.wide" android:versionCode="1" android:versionName="1.0"> - + diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 4bc4a7ecc055c..7467954918d86 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -544,7 +544,28 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setEnabledExclusive(packageName, userId); + return mImpl.setEnabledExclusive(packageName, false /* withinCategory */, + userId); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public boolean setEnabledExclusiveInCategory(@Nullable String packageName, int userId) + throws RemoteException { + enforceChangeOverlayPackagesPermission("setEnabled"); + userId = handleIncomingUser(userId, "setEnabled"); + if (packageName == null) { + return false; + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mImpl.setEnabledExclusive(packageName, true /* withinCategory */, + userId); } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 6e02db7b25ea6..a027fc6f2580a 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -35,6 +35,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import libcore.util.Objects; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Iterator; @@ -103,12 +105,14 @@ final class OverlayManagerServiceImpl { for (int i = 0; i < overlayPackagesSize; i++) { final PackageInfo overlayPackage = overlayPackages.get(i); final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName); - if (oi == null || !oi.targetPackageName.equals(overlayPackage.overlayTarget)) { + if (oi == null || !oi.targetPackageName.equals(overlayPackage.overlayTarget) + || !Objects.equal(oi.category, overlayPackage.overlayCategory)) { // Update the overlay if it didn't exist or had the wrong target package. mSettings.init(overlayPackage.packageName, newUserId, overlayPackage.overlayTarget, overlayPackage.applicationInfo.getBaseCodePath(), - overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority); + overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority, + overlayPackage.overlayCategory); if (oi == null) { // This overlay does not exist in our settings. @@ -259,7 +263,8 @@ final class OverlayManagerServiceImpl { mSettings.init(packageName, userId, overlayPackage.overlayTarget, overlayPackage.applicationInfo.getBaseCodePath(), - overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority); + overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority, + overlayPackage.overlayCategory); try { if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) { mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); @@ -320,7 +325,7 @@ final class OverlayManagerServiceImpl { if (!oldOi.targetPackageName.equals(pkg.overlayTarget)) { mSettings.init(packageName, userId, pkg.overlayTarget, pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(), - pkg.overlayPriority); + pkg.overlayPriority, pkg.overlayCategory); } if (updateState(pkg.overlayTarget, packageName, userId, 0)) { @@ -394,10 +399,11 @@ final class OverlayManagerServiceImpl { } } - boolean setEnabledExclusive(@NonNull final String packageName, final int userId) { + boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory, + final int userId) { if (DEBUG) { - Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName, - userId)); + Slog.d(TAG, String.format("setEnabledExclusive packageName=%s" + + " withinCategory=%s userId=%d", packageName, withinCategory, userId)); } final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); @@ -428,6 +434,11 @@ final class OverlayManagerServiceImpl { // Don't touch static overlays. continue; } + if (withinCategory && !Objects.equal(disabledOverlayPackageInfo.overlayCategory, + oi.category)) { + // Don't touch overlays from other categories. + continue; + } // Disable the overlay. modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false); diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index a80cae4dcb4b9..e57fa0b51d399 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -20,10 +20,7 @@ import static com.android.server.om.OverlayManagerService.DEBUG; import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.om.OverlayInfo; -import android.os.UserHandle; -import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.Slog; import android.util.Xml; @@ -67,11 +64,11 @@ final class OverlayManagerSettings { void init(@NonNull final String packageName, final int userId, @NonNull final String targetPackageName, @NonNull final String baseCodePath, - boolean isStatic, int priority) { + boolean isStatic, int priority, String overlayCategory) { remove(packageName, userId); final SettingsItem item = new SettingsItem(packageName, userId, targetPackageName, baseCodePath, - isStatic, priority); + isStatic, priority, overlayCategory); if (isStatic) { int i; for (i = mItems.size() - 1; i >= 0; i--) { @@ -292,6 +289,7 @@ final class OverlayManagerSettings { pw.print("mState.............: "); pw.println(OverlayInfo.stateToString(item.getState())); pw.print("mIsEnabled.........: "); pw.println(item.isEnabled()); pw.print("mIsStatic..........: "); pw.println(item.isStatic()); + pw.print("mCategory..........: "); pw.println(item.mCategory); pw.decreaseIndent(); pw.println("}"); @@ -317,6 +315,7 @@ final class OverlayManagerSettings { private static final String ATTR_TARGET_PACKAGE_NAME = "targetPackageName"; private static final String ATTR_IS_STATIC = "isStatic"; private static final String ATTR_PRIORITY = "priority"; + private static final String ATTR_CATEGORY = "category"; private static final String ATTR_USER_ID = "userId"; private static final String ATTR_VERSION = "version"; @@ -371,9 +370,10 @@ final class OverlayManagerSettings { final boolean isEnabled = XmlUtils.readBooleanAttribute(parser, ATTR_IS_ENABLED); final boolean isStatic = XmlUtils.readBooleanAttribute(parser, ATTR_IS_STATIC); final int priority = XmlUtils.readIntAttribute(parser, ATTR_PRIORITY); + final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY); - return new SettingsItem(packageName, userId, targetPackageName, baseCodePath, state, - isEnabled, isStatic, priority); + return new SettingsItem(packageName, userId, targetPackageName, baseCodePath, + state, isEnabled, isStatic, priority, category); } public static void persist(@NonNull final ArrayList table, @@ -405,6 +405,7 @@ final class OverlayManagerSettings { XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled); XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, item.mIsStatic); XmlUtils.writeIntAttribute(xml, ATTR_PRIORITY, item.mPriority); + XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory); xml.endTag(null, TAG_ITEM); } } @@ -419,17 +420,19 @@ final class OverlayManagerSettings { private OverlayInfo mCache; private boolean mIsStatic; private int mPriority; + private final String mCategory; SettingsItem(@NonNull final String packageName, final int userId, @NonNull final String targetPackageName, @NonNull final String baseCodePath, final @OverlayInfo.State int state, final boolean isEnabled, final boolean isStatic, - final int priority) { + final int priority, String category) { mPackageName = packageName; mUserId = userId; mTargetPackageName = targetPackageName; mBaseCodePath = baseCodePath; mState = state; mIsEnabled = isEnabled; + mCategory = category; mCache = null; mIsStatic = isStatic; mPriority = priority; @@ -437,9 +440,9 @@ final class OverlayManagerSettings { SettingsItem(@NonNull final String packageName, final int userId, @NonNull final String targetPackageName, @NonNull final String baseCodePath, - final boolean isStatic, final int priority) { + final boolean isStatic, final int priority, String category) { this(packageName, userId, targetPackageName, baseCodePath, OverlayInfo.STATE_UNKNOWN, - false, isStatic, priority); + false, isStatic, priority, category); } private String getTargetPackageName() { @@ -491,8 +494,8 @@ final class OverlayManagerSettings { private OverlayInfo getOverlayInfo() { if (mCache == null) { - mCache = new OverlayInfo(mPackageName, mTargetPackageName, mBaseCodePath, mState, - mUserId); + mCache = new OverlayInfo(mPackageName, mTargetPackageName, mCategory, mBaseCodePath, + mState, mUserId); } return mCache; } diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index 29ddaf49e200a..54bb115c54052 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -56,6 +56,8 @@ final class OverlayManagerShellCommand extends ShellCommand { return runEnableDisable(true); case "disable": return runEnableDisable(false); + case "enable-exclusive": + return runEnableExclusive(); case "set-priority": return runSetPriority(); default: @@ -86,6 +88,10 @@ final class OverlayManagerShellCommand extends ShellCommand { out.println(" Enable overlay package PACKAGE."); out.println(" disable [--user USER_ID] PACKAGE"); out.println(" Disable overlay package PACKAGE."); + out.println(" enable-exclusive [--user USER_ID] [--category] PACKAGE"); + out.println(" Enable overlay package PACKAGE and disable all other overlays for"); + out.println(" its target package. If the --category option is given, only disables"); + out.println(" other overlays in the same category."); out.println(" set-priority [--user USER_ID] PACKAGE PARENT|lowest|highest"); out.println(" Change the priority of the overlay PACKAGE to be just higher than"); out.println(" the priority of PACKAGE_PARENT If PARENT is the special keyword"); @@ -157,6 +163,33 @@ final class OverlayManagerShellCommand extends ShellCommand { return mInterface.setEnabled(packageName, enable, userId) ? 0 : 1; } + private int runEnableExclusive() throws RemoteException { + final PrintWriter err = getErrPrintWriter(); + + int userId = UserHandle.USER_SYSTEM; + boolean inCategory = false; + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--category": + inCategory = true; + break; + default: + err.println("Error: Unknown option: " + opt); + return 1; + } + } + final String overlay = getNextArgRequired(); + if (inCategory) { + return mInterface.setEnabledExclusiveInCategory(overlay, userId) ? 0 : 1; + } else { + return mInterface.setEnabledExclusive(overlay, true, userId) ? 0 : 1; + } + } + private int runSetPriority() throws RemoteException { final PrintWriter err = getErrPrintWriter(); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b5fe9ea49b86c..a38cbda245ca1 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4637,6 +4637,11 @@ public final class Settings { pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC); pw.println(); + if (ps.pkg.mOverlayTarget != null) { + pw.print(prefix); pw.print(" overlayTarget="); pw.println(ps.pkg.mOverlayTarget); + pw.print(prefix); pw.print(" overlayCategory="); pw.println(ps.pkg.mOverlayCategory); + } + if (ps.pkg != null && ps.pkg.permissions != null && ps.pkg.permissions.size() > 0) { final ArrayList perms = ps.pkg.permissions; pw.print(prefix); pw.println(" declared permissions:");