summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorRyan Mitchell <rtmitchell@google.com>2021-01-19 13:51:15 -0800
committerRyan Mitchell <rtmitchell@google.com>2021-02-09 20:13:50 -0800
commit6a2ca782d497e6fdb616f6a273409786a0b81f99 (patch)
treeae9ff4df1dcaa96b153cfa311761e93b7c1fb8ed /core/java
parent2ed8bfa7fda3c42280e0816c6cf1af852981723b (diff)
OverlayManager Fabricated RROs
Adds registering and unregistering of FabricatedOverlay to the OMS. The process that creates the fabricated overlays owns it and is the only process allowed to unregister it. When a fabricated overlay is registered, overlay settings for it are initialized in all users. When a fabricated overlay is unregistered, it is disabled and removed from all users. When a new user is created, it will be able to use the fabricated overlay as well. On boot, fabricated overlays that are not referenced in overlay settings will be deleted. When the package that created the fabricated overlay is uninstalled, its fabricated overlays are also unregistered. Bug: 172471315 Test: atest OverlayDeviceTests Change-Id: I0539656f4c919246b13129579b0286c08a398dc2
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/content/om/CriticalOverlayInfo.java5
-rw-r--r--core/java/android/content/om/FabricatedOverlay.java126
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl2
-rw-r--r--core/java/android/content/om/OverlayInfo.java34
-rw-r--r--core/java/android/content/om/OverlayManagerTransaction.java62
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java24
6 files changed, 239 insertions, 14 deletions
diff --git a/core/java/android/content/om/CriticalOverlayInfo.java b/core/java/android/content/om/CriticalOverlayInfo.java
index 8d14f4360f07..8fbc698f368a 100644
--- a/core/java/android/content/om/CriticalOverlayInfo.java
+++ b/core/java/android/content/om/CriticalOverlayInfo.java
@@ -57,4 +57,9 @@ public interface CriticalOverlayInfo {
*/
@NonNull
OverlayIdentifier getOverlayIdentifier();
+
+ /**
+ * Returns whether or not the overlay is a {@link FabricatedOverlay}.
+ */
+ boolean isFabricated();
}
diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java
new file mode 100644
index 000000000000..d62b47b34dcb
--- /dev/null
+++ b/core/java/android/content/om/FabricatedOverlay.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 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 android.content.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.FabricatedOverlayInternal;
+import android.os.FabricatedOverlayInternalEntry;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+
+/**
+ * Fabricated Runtime Resource Overlays (FRROs) are overlays generated ar runtime.
+ *
+ * Fabricated overlays are enabled, disabled, and reordered just like normal overlays. The
+ * overlayable policies a fabricated overlay fulfills are the same policies the creator of the
+ * overlay fulfill. For example, a fabricated overlay created by a platform signed package on the
+ * system partition would fulfil the {@code system} and {@code signature} policies.
+ *
+ * The owner of a fabricated overlay is the UID that created it. Overlays commit to the overlay
+ * manager persist across reboots. When the UID is uninstalled, its fabricated overlays are wiped.
+ *
+ * Processes with {@link Android.Manifest.permission.CHANGE_OVERLAY_PACKAGES} can manage normal
+ * overlays and fabricated overlays.
+ * @hide
+ */
+public class FabricatedOverlay {
+
+ /** Retrieves the identifier for this fabricated overlay. */
+ public OverlayIdentifier getIdentifier() {
+ return new OverlayIdentifier(
+ mOverlay.packageName, TextUtils.nullIfEmpty(mOverlay.overlayName));
+ }
+
+ public static class Builder {
+ private final String mOwningPackage;
+ private final String mName;
+ private final String mTargetPackage;
+ private String mTargetOverlayable = "";
+ private final ArrayList<FabricatedOverlayInternalEntry> mEntries = new ArrayList<>();
+
+ /**
+ * Constructs a build for a fabricated overlay.
+ *
+ * @param owningPackage the name of the package that owns the fabricated overlay (must
+ * be a package name of this UID).
+ * @param name a name used to uniquely identify the fabricated overlay owned by
+ * {@param owningPackageName}
+ * @param targetPackage the name of the package to overlay
+ */
+ public Builder(@NonNull String owningPackage, @NonNull String name,
+ @NonNull String targetPackage) {
+ Preconditions.checkStringNotEmpty(owningPackage,
+ "'owningPackage' must not be empty nor null");
+ Preconditions.checkStringNotEmpty(name,
+ "'name'' must not be empty nor null");
+ Preconditions.checkStringNotEmpty(targetPackage,
+ "'targetPackage' must not be empty nor null");
+
+ mOwningPackage = owningPackage;
+ mName = name;
+ mTargetPackage = targetPackage;
+ }
+
+ /**
+ * Sets the name of the overlayable resources to overlay (can be null).
+ */
+ public Builder setTargetOverlayable(@Nullable String targetOverlayable) {
+ mTargetOverlayable = TextUtils.emptyIfNull(targetOverlayable);
+ return this;
+ }
+
+ /**
+ * Sets the value of
+ *
+ * @param resourceName name of the target resource to overlay (in the form
+ * [package]:type/entry)
+ * @param dataType the data type of the new value
+ * @param value the unsigned 32 bit integer representing the new value
+ *
+ * @see android.util.TypedValue#type
+ */
+ public Builder setResourceValue(@NonNull String resourceName, int dataType, int value) {
+ final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+ entry.resourceName = resourceName;
+ entry.dataType = dataType;
+ entry.data = value;
+ mEntries.add(entry);
+ return this;
+ }
+
+ /** Builds an immutable fabricated overlay. */
+ public FabricatedOverlay build() {
+ final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();
+ overlay.packageName = mOwningPackage;
+ overlay.overlayName = mName;
+ overlay.targetPackageName = mTargetPackage;
+ overlay.targetOverlayable = mTargetOverlayable;
+ overlay.entries = new ArrayList<>();
+ overlay.entries.addAll(mEntries);
+ return new FabricatedOverlay(overlay);
+ }
+ }
+
+ final FabricatedOverlayInternal mOverlay;
+ private FabricatedOverlay(FabricatedOverlayInternal overlay) {
+ mOverlay = overlay;
+ }
+}
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index a99e79294f76..e319d2c4655c 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -175,7 +175,7 @@ interface IOverlayManager {
* Invalidates and removes the idmap for an overlay,
* @param packageName The name of the overlay package whose idmap should be deleted.
*/
- void invalidateCachesForOverlay(in String packageName, in int userIs);
+ void invalidateCachesForOverlay(in String packageName, in int userId);
/**
* Perform a series of requests related to overlay packages. This is an
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 8c316d7998ac..c66f49cab088 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -214,6 +214,12 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
private OverlayIdentifier mIdentifierCached;
/**
+ *
+ * @hide
+ */
+ public final boolean isFabricated;
+
+ /**
* Create a new OverlayInfo based on source with an updated state.
*
* @param source the source OverlayInfo to base the new instance on
@@ -224,7 +230,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
this(source.packageName, source.overlayName, source.targetPackageName,
source.targetOverlayableName, source.category, source.baseCodePath, state,
- source.userId, source.priority, source.isMutable);
+ source.userId, source.priority, source.isMutable, source.isFabricated);
}
/** @hide */
@@ -233,14 +239,15 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
@Nullable String targetOverlayableName, @Nullable String category,
@NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable) {
this(packageName, null /* overlayName */, targetPackageName, targetOverlayableName,
- category, baseCodePath, state, userId, priority, isMutable);
+ category, baseCodePath, state, userId, priority, isMutable,
+ false /* isFabricated */);
}
/** @hide */
public OverlayInfo(@NonNull String packageName, @Nullable String overlayName,
@NonNull String targetPackageName, @Nullable String targetOverlayableName,
@Nullable String category, @NonNull String baseCodePath, int state, int userId,
- int priority, boolean isMutable) {
+ int priority, boolean isMutable, boolean isFabricated) {
this.packageName = packageName;
this.overlayName = overlayName;
this.targetPackageName = targetPackageName;
@@ -251,6 +258,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
this.userId = userId;
this.priority = priority;
this.isMutable = isMutable;
+ this.isFabricated = isFabricated;
ensureValidState();
}
@@ -266,6 +274,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
userId = source.readInt();
priority = source.readInt();
isMutable = source.readBoolean();
+ isFabricated = source.readBoolean();
ensureValidState();
}
@@ -339,6 +348,24 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
* @hide
*/
@Override
+ public boolean isFabricated() {
+ return isFabricated;
+ }
+
+ /**
+ * Full path to the base APK or fabricated overlay for this overlay package.
+ *
+ * @hide
+ */
+ public String getBaseCodePath() {
+ return baseCodePath;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
@NonNull
public OverlayIdentifier getOverlayIdentifier() {
if (mIdentifierCached == null) {
@@ -390,6 +417,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
dest.writeInt(userId);
dest.writeInt(priority);
dest.writeBoolean(isMutable);
+ dest.writeBoolean(isFabricated);
}
public static final @android.annotation.NonNull Parcelable.Creator<OverlayInfo> CREATOR =
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
index d03ad9d28a4f..73be0ffbf467 100644
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -20,6 +20,9 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -29,6 +32,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
/**
* Container for a batch of requests to the OverlayManagerService.
@@ -65,7 +69,8 @@ public class OverlayManagerTransaction
final int request = source.readInt();
final OverlayIdentifier overlay = source.readParcelable(null);
final int userId = source.readInt();
- mRequests.add(new Request(request, overlay, userId));
+ final Bundle extras = source.readBundle(null);
+ mRequests.add(new Request(request, overlay, userId, extras));
}
}
@@ -95,21 +100,35 @@ public class OverlayManagerTransaction
public static final int TYPE_SET_ENABLED = 0;
public static final int TYPE_SET_DISABLED = 1;
+ public static final int TYPE_REGISTER_FABRICATED = 2;
+ public static final int TYPE_UNREGISTER_FABRICATED = 3;
- @RequestType public final int type;
+ public static final String BUNDLE_FABRICATED_OVERLAY = "fabricated_overlay";
+
+ @RequestType
+ public final int type;
+ @NonNull
public final OverlayIdentifier overlay;
public final int userId;
+ @Nullable
+ public final Bundle extras;
public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
final int userId) {
+ this(type, overlay, userId, null /* extras */);
+ }
+
+ public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
+ final int userId, @Nullable Bundle extras) {
this.type = type;
this.overlay = overlay;
this.userId = userId;
+ this.extras = extras;
}
@Override
public String toString() {
- return String.format("Request{type=0x%02x (%s), overlay=%s, userId=%d}",
+ return String.format(Locale.US, "Request{type=0x%02x (%s), overlay=%s, userId=%d}",
type, typeToString(), overlay, userId);
}
@@ -123,6 +142,8 @@ public class OverlayManagerTransaction
switch (type) {
case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
+ case TYPE_REGISTER_FABRICATED: return "TYPE_REGISTER_FABRICATED";
+ case TYPE_UNREGISTER_FABRICATED: return "TYPE_UNREGISTER_FABRICATED";
default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
}
}
@@ -172,6 +193,40 @@ public class OverlayManagerTransaction
}
/**
+ * Registers the fabricated overlay with the overlay manager so it can be enabled and
+ * disabled for any user.
+ *
+ * The fabricated overlay is initialized in a disabled state. If an overlay is re-registered
+ * the existing overlay will be replaced by the newly registered overlay and the enabled
+ * state of the overlay will be left unchanged if the target package and target overlayable
+ * have not changed.
+ *
+ * @param overlay the overlay to register with the overlay manager
+ *
+ * @hide
+ */
+ public Builder registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) {
+ final Bundle extras = new Bundle();
+ extras.putParcelable(Request.BUNDLE_FABRICATED_OVERLAY, overlay.mOverlay);
+ mRequests.add(new Request(Request.TYPE_REGISTER_FABRICATED, overlay.getIdentifier(),
+ UserHandle.USER_ALL, extras));
+ return this;
+ }
+
+ /**
+ * Disables and removes the overlay from the overlay manager for all users.
+ *
+ * @param overlay the overlay to disable and remove
+ *
+ * @hide
+ */
+ public Builder unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) {
+ mRequests.add(new Request(Request.TYPE_UNREGISTER_FABRICATED, overlay,
+ UserHandle.USER_ALL));
+ return this;
+ }
+
+ /**
* Create a new transaction out of the requests added so far. Execute
* the transaction by calling OverlayManager#commit.
*
@@ -197,6 +252,7 @@ public class OverlayManagerTransaction
dest.writeInt(req.type);
dest.writeParcelable(req.overlay, flags);
dest.writeInt(req.userId);
+ dest.writeBundle(req.extras);
}
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 66bdb9bb864a..b7aa30f00691 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2807,7 +2807,7 @@ public class ParsingPackageUtils {
* limits length of the name to the {@link #MAX_FILE_NAME_SIZE}.
* @return Success if it's valid.
*/
- public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
+ public static String validateName(String name, boolean requireSeparator,
boolean requireFilename) {
final int N = name.length();
boolean hasSep = false;
@@ -2828,18 +2828,28 @@ public class ParsingPackageUtils {
front = true;
continue;
}
- return input.error("bad character '" + c + "'");
+ return "bad character '" + c + "'";
}
if (requireFilename) {
if (!FileUtils.isValidExtFilename(name)) {
- return input.error("Invalid filename");
+ return "Invalid filename";
} else if (N > MAX_FILE_NAME_SIZE) {
- return input.error("the length of the name is greater than " + MAX_FILE_NAME_SIZE);
+ return "the length of the name is greater than " + MAX_FILE_NAME_SIZE;
}
}
- return hasSep || !requireSeparator
- ? input.success(null)
- : input.error("must have at least one '.' separator");
+ return hasSep || !requireSeparator ? null : "must have at least one '.' separator";
+ }
+
+ /**
+ * @see #validateName(String, boolean, boolean)
+ */
+ public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
+ boolean requireFilename) {
+ final String errorMessage = validateName(name, requireSeparator, requireFilename);
+ if (errorMessage != null) {
+ return input.error(errorMessage);
+ }
+ return input.success(null);
}
/**