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

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

OMS: limit shell command output via optional args

Update the list and dump commands to optionally limit their output to make it
easier to use the commands in automated tests.

Examples of the new arguments in use:

  $ adb shell cmd overlay list android
  android
  [ ] com.android.internal.display.cutout.emulation.corner
  [ ] com.android.internal.display.cutout.emulation.double
  [ ] com.android.theme.icon.square
  ...

  $ adb shell cmd overlay dump com.android.theme.icon.square
  com.android.theme.icon.square:0 {
    mPackageName...........: com.android.theme.icon.square
    mUserId................: 0
    mTargetPackageName.....: android
    ...
  }

  $ adb shell cmd overlay dump state com.android.theme.icon.square
  STATE_DISABLED

Bug: 130364252
Test: atest 'com.android.server.om.hosttest.InstallOverlayTests#testAdbShellOMSInterface'
Change-Id: Iee7d654e2600bb13b7755ce1623925df68f43463
parent 090cb136
Loading
Loading
Loading
Loading
+25 −4
Original line number Diff line number Diff line
@@ -177,6 +177,23 @@ public class InstallOverlayTests extends BaseHostJUnit4Test {
                .contains(APP_OVERLAY_PACKAGE_NAME));
    }

    @Test
    public void testAdbShellOMSInterface() throws Exception {
        installPackage("OverlayHostTests_AppOverlayV1.apk");
        assertTrue(shell("cmd overlay list " + DEVICE_TEST_PKG).contains(DEVICE_TEST_PKG));
        assertTrue(shell("cmd overlay list " + DEVICE_TEST_PKG).contains(APP_OVERLAY_PACKAGE_NAME));
        assertEquals("[ ] " + APP_OVERLAY_PACKAGE_NAME,
                shell("cmd overlay list " + APP_OVERLAY_PACKAGE_NAME).trim());
        assertEquals("STATE_DISABLED",
                shell("cmd overlay dump state " + APP_OVERLAY_PACKAGE_NAME).trim());

        setOverlayEnabled(APP_OVERLAY_PACKAGE_NAME, true);
        assertEquals("[x] " + APP_OVERLAY_PACKAGE_NAME,
                shell("cmd overlay list " + APP_OVERLAY_PACKAGE_NAME).trim());
        assertEquals("STATE_ENABLED",
                shell("cmd overlay dump state " + APP_OVERLAY_PACKAGE_NAME).trim());
    }

    private void delay() {
        try {
            Thread.sleep(1000);
@@ -195,20 +212,24 @@ public class InstallOverlayTests extends BaseHostJUnit4Test {
    }

    private void installConvertExistingInstantPackageToFull(String pkg) throws Exception {
        getDevice().executeShellCommand("cmd package install-existing --wait --full " + pkg);
        shell("cmd package install-existing --wait --full " + pkg);
    }

    private void setPackageEnabled(String pkg, boolean enabled) throws Exception {
        getDevice().executeShellCommand("cmd package " + (enabled ? "enable " : "disable ") + pkg);
        shell("cmd package " + (enabled ? "enable " : "disable ") + pkg);
        delay();
    }

    private void setOverlayEnabled(String pkg, boolean enabled) throws Exception {
        getDevice().executeShellCommand("cmd overlay " + (enabled ? "enable " : "disable ") + pkg);
        shell("cmd overlay " + (enabled ? "enable " : "disable ") + pkg);
        delay();
    }

    private boolean overlayManagerContainsPackage(String pkg) throws Exception {
        return getDevice().executeShellCommand("cmd overlay list").contains(pkg);
        return shell("cmd overlay list").contains(pkg);
    }

    private String shell(final String cmd) throws Exception {
        return getDevice().executeShellCommand(cmd);
    }
}
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 com.android.server.om;

import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.UserHandle;

/**
 * State for dumps performed by the OverlayManagerService.
 */
public final class DumpState {
    @UserIdInt private int mUserId = UserHandle.USER_ALL;
    @Nullable private String mPackageName;
    @Nullable private String mField;
    private boolean mVerbose;

    /** Sets the user to dump the state for */
    public void setUserId(@UserIdInt int userId) {
        mUserId = userId;
    }
    @UserIdInt public int getUserId() {
        return mUserId;
    }

    /** Sets the name of the package to dump the state for */
    public void setPackageName(String packageName) {
        mPackageName = packageName;
    }
    @Nullable public String getPackageName() {
        return mPackageName;
    }

    /** Sets the name of the field to dump the state of */
    public void setField(String field) {
        mField = field;
    }
    @Nullable public String getField() {
        return mField;
    }

    /** Enables verbose dump state */
    public void setVerbose(boolean verbose) {
        mVerbose = verbose;
    }
    public boolean isVerbose() {
        return mVerbose;
    }
}
+71 −9
Original line number Diff line number Diff line
@@ -749,15 +749,77 @@ public final class OverlayManagerService extends SystemService {
        }

        @Override
        protected void dump(@NonNull final FileDescriptor fd, @NonNull final PrintWriter pw,
                @NonNull final String[] argv) {
            enforceDumpPermission("dump");
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            final DumpState dumpState = new DumpState();
            dumpState.setUserId(UserHandle.getUserId(Binder.getCallingUid()));

            int opti = 0;
            while (opti < args.length) {
                final String opt = args[opti];
                if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
                    break;
                }
                opti++;

            final boolean verbose = argv.length > 0 && "--verbose".equals(argv[0]);
                if ("-h".equals(opt)) {
                    pw.println("dump [-h] [--verbose] [--user USER_ID] [[FIELD] PACKAGE]");
                    pw.println("  Print debugging information about the overlay manager.");
                    pw.println("  With optional parameter PACKAGE, limit output to the specified");
                    pw.println("  package. With optional parameter FIELD, limit output to");
                    pw.println("  the value of that SettingsItem field. Field names are");
                    pw.println("  case insensitive and out.println the m prefix can be omitted,");
                    pw.println("  so the following are equivalent: mState, mstate, State, state.");
                    return;
                } else if ("--user".equals(opt)) {
                    opti++;
                    if (opti >= args.length) {
                        pw.println("Error: user missing argument");
                        return;
                    }
                    try {
                        dumpState.setUserId(Integer.parseInt(args[opti]));
                    } catch (NumberFormatException e) {
                        pw.println("Error: user argument is not a number: " + args[opti]);
                        return;
                    }
                } else if ("--verbose".equals(opt)) {
                    dumpState.setVerbose(true);
                } else {
                    pw.println("Unknown argument: " + opt + "; use -h for help");
                }
            }
            if (opti < args.length) {
                final String arg = args[opti];
                opti++;
                switch (arg) {
                    case "packagename":
                    case "userid":
                    case "targetpackagename":
                    case "targetoverlayablename":
                    case "basecodepath":
                    case "state":
                    case "isenabled":
                    case "isstatic":
                    case "priority":
                    case "category":
                        dumpState.setField(arg);
                        break;
                    default:
                        dumpState.setPackageName(arg);
                        break;
                }
            }
            if (dumpState.getPackageName() == null && opti < args.length) {
                dumpState.setPackageName(args[opti]);
                opti++;
            }

            enforceDumpPermission("dump");
            synchronized (mLock) {
                mImpl.onDump(pw);
                mPackageManager.dump(pw, verbose);
                mImpl.dump(pw, dumpState);
                if (dumpState.getPackageName() == null) {
                    mPackageManager.dump(pw, dumpState);
                }
            }
        }

@@ -1046,10 +1108,10 @@ public final class OverlayManagerService extends SystemService {
        private static final String TAB1 = "    ";
        private static final String TAB2 = TAB1 + TAB1;

        public void dump(@NonNull final PrintWriter pw, final boolean verbose) {
        public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
            pw.println("PackageInfo cache");

            if (!verbose) {
            if (!dumpState.isVerbose()) {
                int count = 0;
                final int n = mCache.size();
                for (int i = 0; i < n; i++) {
+5 −3
Original line number Diff line number Diff line
@@ -636,10 +636,12 @@ final class OverlayManagerServiceImpl {
        return true;
    }

    void onDump(@NonNull final PrintWriter pw) {
        mSettings.dump(pw);
    void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
        mSettings.dump(pw, dumpState);
        if (dumpState.getPackageName() == null) {
            pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
        }
    }

    @NonNull String[] getDefaultOverlayPackages() {
        return mDefaultOverlays;
+69 −25
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ 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.ArrayMap;
import android.util.Slog;
import android.util.Xml;
@@ -288,19 +289,27 @@ final class OverlayManagerSettings {
        return true;
    }

    void dump(@NonNull final PrintWriter p) {
        final IndentingPrintWriter pw = new IndentingPrintWriter(p, "  ");
        pw.println("Settings");
        pw.increaseIndent();
    void dump(@NonNull final PrintWriter p, @NonNull DumpState dumpState) {
        // select items to display
        Stream<SettingsItem> items = mItems.stream();
        if (dumpState.getUserId() != UserHandle.USER_ALL) {
            items = items.filter(item -> item.mUserId == dumpState.getUserId());
        }
        if (dumpState.getPackageName() != null) {
            items = items.filter(item -> item.mPackageName.equals(dumpState.getPackageName()));
        }

        if (mItems.isEmpty()) {
            pw.println("<none>");
            return;
        // display items
        final IndentingPrintWriter pw = new IndentingPrintWriter(p, "  ");
        if (dumpState.getField() != null) {
            items.forEach(item -> dumpSettingsItemField(pw, item, dumpState.getField()));
        } else {
            items.forEach(item -> dumpSettingsItem(pw, item));
        }
    }

        final int n = mItems.size();
        for (int i = 0; i < n; i++) {
            final SettingsItem item = mItems.get(i);
    private void dumpSettingsItem(@NonNull final IndentingPrintWriter pw,
            @NonNull final SettingsItem item) {
        pw.println(item.mPackageName + ":" + item.getUserId() + " {");
        pw.increaseIndent();

@@ -318,6 +327,41 @@ final class OverlayManagerSettings {
        pw.decreaseIndent();
        pw.println("}");
    }

    private void dumpSettingsItemField(@NonNull final IndentingPrintWriter pw,
            @NonNull final SettingsItem item, @NonNull final String field) {
        switch (field) {
            case "packagename":
                pw.println(item.mPackageName);
                break;
            case "userid":
                pw.println(item.mUserId);
                break;
            case "targetpackagename":
                pw.println(item.mTargetPackageName);
                break;
            case "targetoverlayablename":
                pw.println(item.mTargetOverlayableName);
                break;
            case "basecodepath":
                pw.println(item.mBaseCodePath);
                break;
            case "state":
                pw.println(OverlayInfo.stateToString(item.mState));
                break;
            case "isenabled":
                pw.println(item.mIsEnabled);
                break;
            case "isstatic":
                pw.println(item.mIsStatic);
                break;
            case "priority":
                pw.println(item.mPriority);
                break;
            case "category":
                pw.println(item.mCategory);
                break;
        }
    }

    void restore(@NonNull final InputStream is) throws IOException, XmlPullParserException {
Loading