Unit tests for "cmd shortcut"

Also removed unnecessary commands.

Bug 29612099

Change-Id: Id554163c861fb144cb5140d7eb709178347f7868
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a91e284..68423e2 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -131,12 +131,8 @@
  * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
  *   -> But TypedValue.applyDimension() doesn't differentiate x and y..?
  *
- * - Default launcher check does take a few ms.  Worth caching.
- *
  * - Detect when already registered instances are passed to APIs again, which might break
  * internal bitmap handling.
- *
- * - Add more call stats.
  */
 public class ShortcutService extends IShortcutService.Stub {
     static final String TAG = "ShortcutService";
@@ -1343,16 +1339,21 @@
     }
 
     private void enforceSystemOrShell() {
-        Preconditions.checkState(isCallerSystem() || isCallerShell(),
-                "Caller must be system or shell");
+        if (!(isCallerSystem() || isCallerShell())) {
+            throw new SecurityException("Caller must be system or shell");
+        }
     }
 
     private void enforceShell() {
-        Preconditions.checkState(isCallerShell(), "Caller must be shell");
+        if (!isCallerShell()) {
+            throw new SecurityException("Caller must be shell");
+        }
     }
 
     private void enforceSystem() {
-        Preconditions.checkState(isCallerSystem(), "Caller must be system");
+        if (!isCallerSystem()) {
+            throw new SecurityException("Caller must be system");
+        }
     }
 
     private void enforceResetThrottlingPermission() {
@@ -3166,9 +3167,13 @@
 
         enforceShell();
 
-        final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
-
-        resultReceiver.send(status, null);
+        final long token = injectClearCallingIdentity();
+        try {
+            final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
+            resultReceiver.send(status, null);
+        } finally {
+            injectRestoreCallingIdentity(token);
+        }
     }
 
     static class CommandException extends Exception {
@@ -3213,9 +3218,6 @@
             final PrintWriter pw = getOutPrintWriter();
             try {
                 switch (cmd) {
-                    case "reset-package-throttling":
-                        handleResetPackageThrottling();
-                        break;
                     case "reset-throttling":
                         handleResetThrottling();
                         break;
@@ -3234,9 +3236,6 @@
                     case "get-default-launcher":
                         handleGetDefaultLauncher();
                         break;
-                    case "refresh-default-launcher":
-                        handleRefreshDefaultLauncher();
-                        break;
                     case "unload-user":
                         handleUnloadUser();
                         break;
@@ -3262,9 +3261,6 @@
             final PrintWriter pw = getOutPrintWriter();
             pw.println("Usage: cmd shortcut COMMAND [options ...]");
             pw.println();
-            pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
-            pw.println("    Reset throttling for a package");
-            pw.println();
             pw.println("cmd shortcut reset-throttling [--user USER_ID]");
             pw.println("    Reset throttling for all packages and users");
             pw.println();
@@ -3281,10 +3277,7 @@
             pw.println("    Clear the cached default launcher");
             pw.println();
             pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
-            pw.println("    Show the cached default launcher");
-            pw.println();
-            pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]");
-            pw.println("    Refresh the cached default launcher");
+            pw.println("    Show the default launcher");
             pw.println();
             pw.println("cmd shortcut unload-user [--user USER_ID]");
             pw.println("    Unload a user from the memory");
@@ -3298,7 +3291,7 @@
         private void handleResetThrottling() throws CommandException {
             parseOptions(/* takeUser =*/ true);
 
-            Slog.i(TAG, "cmd: handleResetThrottling");
+            Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
 
             resetThrottlingInner(mUserId);
         }
@@ -3309,16 +3302,6 @@
             resetAllThrottlingInner();
         }
 
-        private void handleResetPackageThrottling() throws CommandException {
-            parseOptions(/* takeUser =*/ true);
-
-            final String packageName = getNextArgRequired();
-
-            Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName);
-
-            resetPackageThrottling(packageName, mUserId);
-        }
-
         private void handleOverrideConfig() throws CommandException {
             final String config = getNextArgRequired();
 
@@ -3364,12 +3347,6 @@
         private void handleGetDefaultLauncher() throws CommandException {
             parseOptions(/* takeUser =*/ true);
 
-            showLauncher();
-        }
-
-        private void handleRefreshDefaultLauncher() throws CommandException {
-            parseOptions(/* takeUser =*/ true);
-
             clearLauncher();
             showLauncher();
         }
@@ -3377,7 +3354,7 @@
         private void handleUnloadUser() throws CommandException {
             parseOptions(/* takeUser =*/ true);
 
-            Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId);
+            Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
 
             ShortcutService.this.handleCleanupUser(mUserId);
         }
@@ -3386,7 +3363,7 @@
             parseOptions(/* takeUser =*/ true);
             final String packageName = getNextArgRequired();
 
-            Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName);
+            Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
 
             ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
                     /* appStillExists = */ true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 5864255..f79dd3b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -621,6 +621,14 @@
     protected final Map<Integer, Boolean> mRunningUsers = new HashMap<>();
     protected final Map<Integer, Boolean> mUnlockedUsers = new HashMap<>();
 
+    protected static final String PACKAGE_SYSTEM_LAUNCHER = "com.android.systemlauncher";
+    protected static final String PACKAGE_SYSTEM_LAUNCHER_NAME = "systemlauncher_name";
+    protected static final int PACKAGE_SYSTEM_LAUNCHER_PRIORITY = 0;
+
+    protected static final String PACKAGE_FALLBACK_LAUNCHER = "com.android.settings";
+    protected static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback";
+    protected static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999;
+
     static {
         QUERY_ALL.setQueryFlags(
                 ShortcutQuery.FLAG_GET_ALL_KINDS);
@@ -1824,4 +1832,40 @@
         }
         return sb.toString();
     }
+
+    protected void prepareGetHomeActivitiesAsUser(ComponentName preferred,
+            List<ResolveInfo> candidates, int userId) {
+        doAnswer(inv -> {
+            ((List) inv.getArguments()[0]).addAll(candidates);
+            return preferred;
+        }).when(mMockPackageManagerInternal).getHomeActivitiesAsUser(any(List.class), eq(userId));
+    }
+
+    protected static ComponentName cn(String packageName, String name) {
+        return new ComponentName(packageName, name);
+    }
+
+    protected static ResolveInfo ri(String packageName, String name, boolean isSystem, int priority) {
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.applicationInfo = new ApplicationInfo();
+
+        ri.activityInfo.packageName = packageName;
+        ri.activityInfo.name = name;
+        if (isSystem) {
+            ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        }
+        ri.priority = priority;
+        return ri;
+    }
+
+    protected static ResolveInfo getSystemLauncher() {
+        return ri(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME, true,
+                PACKAGE_SYSTEM_LAUNCHER_PRIORITY);
+    }
+
+    protected static ResolveInfo getFallbackLauncher() {
+        return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true,
+                PACKAGE_FALLBACK_LAUNCHER_PRIORITY);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 55fa625f..5252c34 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -105,15 +105,6 @@
  -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
  adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest1 \
  -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
-
-
- * TODO More tests for pinning + manifest shortcuts
- * TODO Manifest shortcuts + app upgrade -> launcher callback.
- *      Also locale change should trigger launcehr callbacks too, when they use strign resoucres.
- *      (not implemented yet.)
- * TODO: Add checks with assertAllNotHaveIcon()
- * TODO: Detailed test for hasShortcutPermissionInner().
- * TODO: Add tests for the command line functions too.
  */
 @SmallTest
 public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
@@ -4069,7 +4060,7 @@
         });
 
         // Clear data
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDataClear(CALLING_PACKAGE_1, USER_10));
 
         // Only manifest shortcuts will remain, and are no longer pinned.
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
index ffb2953..5f24637 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
@@ -18,7 +18,6 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
 
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 
@@ -36,51 +35,6 @@
  */
 @SmallTest
 public class ShortcutManagerTest6 extends BaseShortcutManagerTest {
-
-    private static final String PACKAGE_SYSTEM_LAUNCHER = "com.android.systemlauncher";
-    private static final String PACKAGE_SYSTEM_LAUNCHER_NAME = "systemlauncher_name";
-    private static final int PACKAGE_SYSTEM_LAUNCHER_PRIORITY = 0;
-
-    private static final String PACKAGE_FALLBACK_LAUNCHER = "com.android.settings";
-    private static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback";
-    private static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999;
-
-    private void prepareGetHomeActivitiesAsUser(ComponentName preferred,
-            List<ResolveInfo> candidates, int userId) {
-        doAnswer(inv -> {
-            ((List) inv.getArguments()[0]).addAll(candidates);
-            return preferred;
-        }).when(mMockPackageManagerInternal).getHomeActivitiesAsUser(any(List.class), eq(userId));
-    }
-
-    private static ComponentName cn(String packageName, String name) {
-        return new ComponentName(packageName, name);
-    }
-
-    private static ResolveInfo ri(String packageName, String name, boolean isSystem, int priority) {
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.applicationInfo = new ApplicationInfo();
-
-        ri.activityInfo.packageName = packageName;
-        ri.activityInfo.name = name;
-        if (isSystem) {
-            ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
-        }
-        ri.priority = priority;
-        return ri;
-    }
-
-    private static ResolveInfo getSystemLauncher() {
-        return ri(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME, true,
-                PACKAGE_SYSTEM_LAUNCHER_PRIORITY);
-    }
-
-    private static ResolveInfo getFallbackLauncher() {
-        return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true,
-                PACKAGE_FALLBACK_LAUNCHER_PRIORITY);
-    }
-
     public void testHasShortcutHostPermissionInner_systemLauncherOnly() {
         // Preferred isn't set, use the system launcher.
         prepareGetHomeActivitiesAsUser(
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
new file mode 100644
index 0000000..f9ff514
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2016 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.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.readAll;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.frameworks.servicestests.R;
+import com.android.server.pm.ShortcutService.ConfigConstants;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unit test for "cmd shortcut"
+ *
+ * Launcher related commands are tested in
+ */
+@SmallTest
+public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
+    private List<String> callShellCommand(String... args) throws IOException, RemoteException {
+
+        // For reset to work, the current time needs to be incrementing.
+        mInjectedCurrentTimeMillis++;
+
+        final AtomicInteger resultCode = new AtomicInteger(Integer.MIN_VALUE);
+
+        final ResultReceiver rr = new ResultReceiver(mHandler) {
+            @Override
+            public void send(int resultCode_, Bundle resultData) {
+                resultCode.set(resultCode_);
+            }
+        };
+        final File out = File.createTempFile("shellout-", ".tmp",
+                getTestContext().getCacheDir());
+        try {
+            try (final ParcelFileDescriptor fd = ParcelFileDescriptor.open(out,
+                    ParcelFileDescriptor.MODE_READ_WRITE)) {
+                mService.onShellCommand(
+                    /* fdin*/ null,
+                    /* fdout*/ fd.getFileDescriptor(),
+                    /* fderr*/ fd.getFileDescriptor(),
+                        args, rr);
+            }
+            return readAll(out);
+        } finally {
+            out.delete();
+        }
+    }
+
+    public void testNonShell() throws Exception {
+        mService.mMaxUpdatesPerInterval = 99;
+
+        mInjectedCallingUid = 12345;
+        assertExpectException(SecurityException.class, "must be shell",
+                () -> callShellCommand("reset-config"));
+
+        mInjectedCallingUid = Process.SYSTEM_UID;
+        assertExpectException(SecurityException.class, "must be shell",
+                () -> callShellCommand("reset-config"));
+
+        assertEquals(99, mService.mMaxUpdatesPerInterval);
+    }
+
+    public void testRoot() throws Exception {
+        mService.mMaxUpdatesPerInterval = 99;
+
+        mInjectedCallingUid = Process.ROOT_UID;
+        assertSuccess(callShellCommand("reset-config"));
+
+        assertEquals(3, mService.mMaxUpdatesPerInterval);
+    }
+
+    public void testRestConfig() throws Exception {
+        mService.mMaxUpdatesPerInterval = 99;
+
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertSuccess(callShellCommand("reset-config"));
+
+        assertEquals(3, mService.mMaxUpdatesPerInterval);
+    }
+
+    public void testOverrideConfig() throws Exception {
+        mService.mMaxUpdatesPerInterval = 99;
+
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertSuccess(callShellCommand("override-config",
+                ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=1"));
+
+        assertEquals(1, mService.mMaxUpdatesPerInterval);
+    }
+
+    public void testResetThrottling() throws Exception {
+        prepareCrossProfileDataSet();
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertSuccess(callShellCommand("reset-throttling"));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertEquals(3, mManager.getRemainingCallCount());
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+    }
+
+    public void testResetThrottling_user_not_running() throws Exception {
+        prepareCrossProfileDataSet();
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+
+        mInjectedCallingUid = Process.SHELL_UID;
+
+        assertTrue(resultContains(
+                callShellCommand("reset-throttling", "--user", "10"),
+                "User 10 is not running or locked"));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+    }
+
+    public void testResetThrottling_user_running() throws Exception {
+        prepareCrossProfileDataSet();
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+
+        mRunningUsers.put(USER_10, true);
+        mUnlockedUsers.put(USER_10, true);
+
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertSuccess(callShellCommand("reset-throttling", "--user", "10"));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertEquals(3, mManager.getRemainingCallCount());
+        });
+    }
+
+    public void testResetAllThrottling() throws Exception {
+        prepareCrossProfileDataSet();
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.getRemainingCallCount() < 3);
+        });
+
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertSuccess(callShellCommand("reset-all-throttling"));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertEquals(3, mManager.getRemainingCallCount());
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertEquals(3, mManager.getRemainingCallCount());
+        });
+    }
+
+    public void testLauncherCommands() throws Exception {
+        prepareGetHomeActivitiesAsUser(
+                /* preferred */ null,
+                list(getSystemLauncher(), getFallbackLauncher()),
+                USER_0);
+
+        prepareGetHomeActivitiesAsUser(
+                /* preferred */ cn(CALLING_PACKAGE_2, "name"),
+                list(getSystemLauncher(), getFallbackLauncher(),
+                        ri(CALLING_PACKAGE_1, "name", false, 0),
+                        ri(CALLING_PACKAGE_2, "name", false, 0)
+                ),
+                USER_10);
+
+        assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+
+        // First, test "get".
+
+        mRunningUsers.put(USER_10, true);
+        mUnlockedUsers.put(USER_10, true);
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertContains(
+                assertSuccess(callShellCommand("get-default-launcher")),
+                "Launcher: ComponentInfo{com.android.systemlauncher/systemlauncher_name}");
+
+        assertContains(
+                assertSuccess(callShellCommand("get-default-launcher", "--user", "10")),
+                "Launcher: ComponentInfo{com.android.test.2/name}");
+
+        // Next, test "clear".
+        assertSuccess(callShellCommand("clear-default-launcher", "--user", "10"));
+
+        // User-10's launcher should be cleared.
+        assertEquals(null, mService.getUserShortcutsLocked(USER_10).getLastKnownLauncher());
+        assertEquals(null, mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+
+        // but user'0's shouldn't.
+        assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+                mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+        // Change user-0's launcher.
+        prepareGetHomeActivitiesAsUser(
+                /* preferred */ cn(CALLING_PACKAGE_1, "name"),
+                list(
+                        ri(CALLING_PACKAGE_1, "name", false, 0)
+                ),
+                USER_0);
+        assertContains(
+                assertSuccess(callShellCommand("get-default-launcher")),
+                "Launcher: ComponentInfo{com.android.test.1/name}");
+    }
+
+    public void testUnloadUser() throws Exception {
+        prepareCrossProfileDataSet();
+
+        assertNotNull(mService.getShortcutsForTest().get(USER_10));
+
+        mRunningUsers.put(USER_10, true);
+        mUnlockedUsers.put(USER_10, true);
+
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertSuccess(callShellCommand("unload-user", "--user", "10"));
+
+        assertNull(mService.getShortcutsForTest().get(USER_10));
+    }
+
+    public void testClearShortcuts() throws Exception {
+
+        mRunningUsers.put(USER_10, true);
+
+        // Add two manifests and two dynamics.
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_2);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.addDynamicShortcuts(list(
+                    makeShortcut("s1"), makeShortcut("s2"))));
+        });
+        runWithCaller(LAUNCHER_1, USER_10, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2", "s1", "s2")
+                    .areAllEnabled()
+
+                    .selectPinned()
+                    .haveIds("ms2", "s2");
+        });
+
+        // First, call for a different package.
+
+        mRunningUsers.put(USER_10, true);
+        mUnlockedUsers.put(USER_10, true);
+
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertSuccess(callShellCommand("clear-shortcuts", "--user", "10", CALLING_PACKAGE_2));
+
+        // Shouldn't be cleared yet.
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2", "s1", "s2")
+                    .areAllEnabled()
+
+                    .selectPinned()
+                    .haveIds("ms2", "s2");
+        });
+
+        mInjectedCallingUid = Process.SHELL_UID;
+        assertSuccess(callShellCommand("clear-shortcuts", "--user", "10", CALLING_PACKAGE_1));
+
+        // Only manifest shortcuts will remain, and are no longer pinned.
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2")
+                    .areAllEnabled()
+                    .areAllNotPinned();
+        });
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index e2dce853..11f9ebb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -16,15 +16,14 @@
 
 package com.android.server.pm;
 
-import android.os.UserHandle;
-import com.android.server.devicepolicy.DpmTestUtils;
-
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.AndroidTestCase;
-import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.server.devicepolicy.DpmTestUtils;
+
 /**
  * Tests for {@link com.android.server.pm.UserRestrictionsUtils}.
  *
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 2f64ad7..04d7b04 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -59,6 +59,8 @@
 import org.mockito.Mockito;
 
 import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -93,7 +95,12 @@
     private ShortcutManagerTestUtils() {
     }
 
-    private static List<String> readAll(ParcelFileDescriptor pfd) {
+    public static List<String> readAll(File file) throws FileNotFoundException {
+        return readAll(ParcelFileDescriptor.open(
+                file.getAbsoluteFile(), ParcelFileDescriptor.MODE_READ_ONLY));
+    }
+
+    public static List<String> readAll(ParcelFileDescriptor pfd) {
         try {
             try {
                 final ArrayList<String> ret = new ArrayList<>();
@@ -114,7 +121,7 @@
         }
     }
 
-    private static String concatResult(List<String> result) {
+    public static String concatResult(List<String> result) {
         final StringBuilder sb = new StringBuilder();
         for (String s : result) {
             sb.append(s);
@@ -123,6 +130,30 @@
         return sb.toString();
     }
 
+    public static boolean resultContains(List<String> result, String expected) {
+        for (String line : result) {
+            if (line.contains(expected)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static List<String> assertSuccess(List<String> result) {
+        if (!resultContains(result, "Success")) {
+            fail("Command failed.  Result was:\n" + concatResult(result));
+        }
+        return result;
+    }
+
+    public static List<String> assertContains(List<String> result, String expected) {
+        if (!resultContains(result, expected)) {
+            fail("Didn't contain expected string=" + expected
+                    + "\nActual:\n" + concatResult(result));
+        }
+        return result;
+    }
+
     private static List<String> runCommand(Instrumentation instrumentation, String command) {
         return runCommand(instrumentation, command, null);
     }
@@ -321,24 +352,29 @@
         return filter(list, si -> si.getLastChangedTimestamp() >= time);
     }
 
+    @FunctionalInterface
+    public interface ExceptionRunnable {
+        void run() throws Exception;
+    }
+
     public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
-            String expectedExceptionMessageRegex, Runnable r) {
+            String expectedExceptionMessageRegex, ExceptionRunnable r) {
         assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
     }
 
     public static void assertCannotUpdateImmutable(Runnable r) {
         assertExpectException(
-                IllegalArgumentException.class, "may not be manipulated via APIs", r);
+                IllegalArgumentException.class, "may not be manipulated via APIs", r::run);
     }
 
     public static void assertDynamicShortcutCountExceeded(Runnable r) {
         assertExpectException(IllegalArgumentException.class,
-                "Max number of dynamic shortcuts exceeded", r);
+                "Max number of dynamic shortcuts exceeded", r::run);
     }
 
     public static void assertExpectException(String message,
             Class<? extends Throwable> expectedExceptionType,
-            String expectedExceptionMessageRegex, Runnable r) {
+            String expectedExceptionMessageRegex, ExceptionRunnable r) {
         try {
             r.run();
         } catch (Throwable e) {