OverlayManager: Add categories

Adds the possibility to declare different categories
for resource overlays (e.g. themes, display cutout emulation, ...)

Bug: 72436677
Test: adb shell cmd overlay enable-exclusive --category com.android.internal.display_cutout_emulation android com.android.internal.display.cutout.emulation.narrow
Test: adb shell cmd overlay enable-exclusive --category com.android.internal.display_cutout_emulation android
Change-Id: I23f22113351b3948beb9e3a1fb969700852539cc
This commit is contained in:
Adrian Roos
2018-01-19 21:20:22 +01:00
parent 85a6db68f1
commit c84df7755c
14 changed files with 174 additions and 31 deletions

View File

@@ -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

View File

@@ -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;
}

View File

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

View File

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

View File

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

View File

@@ -2558,6 +2558,9 @@
<!-- Package name of base package whose resources will be overlaid. -->
<attr name="targetPackage" />
<!-- Category of the resource overlay. -->
<attr name="category" format="string"/>
<!-- Load order of overlay package. -->
<attr name="priority" />

View File

@@ -2,7 +2,9 @@
package="com.android.internal.display.cutout.emulation.narrow"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android" android:priority="1"/>
<overlay android:targetPackage="android"
android:category="com.android.internal.display_cutout_emulation"
android:priority="1"/>
<application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
</manifest>

View File

@@ -18,7 +18,9 @@
package="com.android.internal.display.cutout.emulation.tall"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android" android:priority="1"/>
<overlay android:targetPackage="android"
android:category="com.android.internal.display_cutout_emulation"
android:priority="1"/>
<application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
</manifest>

View File

@@ -18,7 +18,9 @@
package="com.android.internal.display.cutout.emulation.wide"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android" android:priority="1"/>
<overlay android:targetPackage="android"
android:category="com.android.internal.display_cutout_emulation"
android:priority="1"/>
<application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
</manifest>

View File

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

View File

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

View File

@@ -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<SettingsItem> 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;
}

View File

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

View File

@@ -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<PackageParser.Permission> perms = ps.pkg.permissions;
pw.print(prefix); pw.println(" declared permissions:");