OMS: add 'cmd overlay lookup' command
Add a shell command to look up the value of any resource. This allows a
developer to quickly see what the value of a resource is, using the
current device configuration and the currently enabled overlays. For
more fine-grained control, use 'idmap2 lookup' instead.
Example:
$ adb shell cmd overlay lookup --verbose android android:bool/config_annoy_dianne
Resolution for 0x01110020 android:bool/config_annoy_dianne
For config -en-rUS-ldltr-sw320dp-w320dp-h461dp-normal-notlong-notround-nowidecg-lowdr-port-uiModeType=2-notnight-hdpi-finger-keysexposed-qwerty-trackball-728x480-v29
Found initial: android
true
$ adb shell cmd overlay enable test.overlay
$ adb shell cmd overlay lookup --verbose android android:bool/config_annoy_dianne
Resolution for 0x01110020 test.overlay:bool/config_annoy_dianne
For config -en-rUS-ldltr-sw320dp-w320dp-h461dp-normal-notlong-notround-nowidecg-lowdr-port-uiModeType=2-notnight-hdpi-finger-keysexposed-qwerty-trackball-728x480-v29
Found initial: android
Overlaid: test.overlay
false
Test: manual: adb shell cmd overlay lookup --verbose android android:array/preloaded_drawables
Change-Id: I00efe23b1a0f7342a5cf7f4ba86f3eae6714c9aa
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 965ddc9..f8b3fb2 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -744,7 +744,7 @@
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
@NonNull final ResultReceiver resultReceiver) {
- (new OverlayManagerShellCommand(this)).exec(
+ (new OverlayManagerShellCommand(getContext(), this)).exec(
this, in, out, err, args, callback, resultReceiver);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index 99f5839..eb43275 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -18,15 +18,23 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.util.TypedValue;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Implementation of 'cmd overlay' commands.
@@ -36,9 +44,11 @@
* for a list of available commands.
*/
final class OverlayManagerShellCommand extends ShellCommand {
+ private final Context mContext;
private final IOverlayManager mInterface;
- OverlayManagerShellCommand(@NonNull final IOverlayManager iom) {
+ OverlayManagerShellCommand(@NonNull final Context ctx, @NonNull final IOverlayManager iom) {
+ mContext = ctx;
mInterface = iom;
}
@@ -60,6 +70,8 @@
return runEnableExclusive();
case "set-priority":
return runSetPriority();
+ case "lookup":
+ return runLookup();
default:
return handleDefaultCommands(cmd);
}
@@ -102,6 +114,10 @@
out.println(" 'lowest', change priority of PACKAGE to the lowest priority.");
out.println(" If PARENT is the special keyword 'highest', change priority of");
out.println(" PACKAGE to the highest priority.");
+ out.println(" lookup [--verbose] PACKAGE-TO-LOAD PACKAGE:TYPE/NAME");
+ out.println(" Load a package and print the value of a given resource");
+ out.println(" applying the current configuration and enabled overlays.");
+ out.println(" For a more fine-grained alernative, use 'idmap2 lookup'.");
}
private int runList() throws RemoteException {
@@ -253,4 +269,92 @@
return mInterface.setPriority(packageName, newParentPackageName, userId) ? 0 : 1;
}
}
+
+ private int runLookup() throws RemoteException {
+ final PrintWriter out = getOutPrintWriter();
+ final PrintWriter err = getErrPrintWriter();
+
+ final boolean verbose = "--verbose".equals(getNextOption());
+
+ final String packageToLoad = getNextArgRequired();
+
+ final String fullyQualifiedResourceName = getNextArgRequired(); // package:type/name
+ final Pattern regex = Pattern.compile("(.*?):(.*?)/(.*?)");
+ final Matcher matcher = regex.matcher(fullyQualifiedResourceName);
+ if (!matcher.matches()) {
+ err.println("Error: bad resource name, doesn't match package:type/name");
+ return 1;
+ }
+
+ final PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ err.println("Error: failed to get package manager");
+ return 1;
+ }
+
+ final Resources res;
+ try {
+ res = pm.getResourcesForApplication(packageToLoad);
+ } catch (PackageManager.NameNotFoundException e) {
+ err.println("Error: failed to get resources for package " + packageToLoad);
+ return 1;
+ }
+ final AssetManager assets = res.getAssets();
+ try {
+ assets.setResourceResolutionLoggingEnabled(true);
+
+ // first try as non-complex type ...
+ try {
+ final TypedValue value = new TypedValue();
+ res.getValue(fullyQualifiedResourceName, value, false /* resolveRefs */);
+ final CharSequence valueString = value.coerceToString();
+ final String resolution = assets.getLastResourceResolution();
+
+ res.getValue(fullyQualifiedResourceName, value, true /* resolveRefs */);
+ final CharSequence resolvedString = value.coerceToString();
+
+ if (verbose) {
+ out.println(resolution);
+ }
+
+ if (valueString.equals(resolvedString)) {
+ out.println(valueString);
+ } else {
+ out.println(valueString + " -> " + resolvedString);
+ }
+ return 0;
+ } catch (Resources.NotFoundException e) {
+ // this is ok, resource could still be a complex type
+ }
+
+ // ... then try as complex type
+ try {
+
+ final String pkg = matcher.group(1);
+ final String type = matcher.group(2);
+ final String name = matcher.group(3);
+ final int resid = res.getIdentifier(name, type, pkg);
+ if (resid == 0) {
+ throw new Resources.NotFoundException();
+ }
+ final TypedArray array = res.obtainTypedArray(resid);
+ if (verbose) {
+ out.println(assets.getLastResourceResolution());
+ }
+ TypedValue tv = new TypedValue();
+ for (int i = 0; i < array.length(); i++) {
+ array.getValue(i, tv);
+ out.println(tv.coerceToString());
+ }
+ array.recycle();
+ return 0;
+ } catch (Resources.NotFoundException e) {
+ // give up
+ err.println("Error: failed to get the resource " + fullyQualifiedResourceName);
+ return 1;
+ }
+ } finally {
+ assets.setResourceResolutionLoggingEnabled(false);
+ }
+ }
}