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

Commit 4ea02d10 authored by Mårten Kongstad's avatar Mårten Kongstad Committed by Todd Kennedy
Browse files

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
parent 9bb86ea9
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -744,7 +744,7 @@ public final class OverlayManagerService extends SystemService {
                @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);
        }

+105 −1
Original line number Diff line number Diff line
@@ -18,15 +18,23 @@ package com.android.server.om;

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 @@ import java.util.Map;
 * 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 @@ final class OverlayManagerShellCommand extends ShellCommand {
                    return runEnableExclusive();
                case "set-priority":
                    return runSetPriority();
                case "lookup":
                    return runLookup();
                default:
                    return handleDefaultCommands(cmd);
            }
@@ -102,6 +114,10 @@ final class OverlayManagerShellCommand extends ShellCommand {
        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 @@ final class OverlayManagerShellCommand extends ShellCommand {
            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);
        }
    }
}