1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.pm;
17
18import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.array;
19import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains;
20import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
21import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess;
22import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
23import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
24import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.readAll;
25import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains;
26
27import android.content.ComponentName;
28import android.os.Bundle;
29import android.os.ParcelFileDescriptor;
30import android.os.Process;
31import android.os.RemoteException;
32import android.os.ResultReceiver;
33import android.test.suitebuilder.annotation.SmallTest;
34
35import com.android.frameworks.servicestests.R;
36import com.android.server.pm.ShortcutService.ConfigConstants;
37
38import java.io.File;
39import java.io.IOException;
40import java.util.List;
41import java.util.concurrent.atomic.AtomicInteger;
42
43/**
44 * Unit test for "cmd shortcut" and "dumpsys shortcut".
45 *
46 * Launcher related commands are tested in
47 */
48@SmallTest
49public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
50    private List<String> callShellCommand(String... args) throws IOException, RemoteException {
51
52        // For reset to work, the current time needs to be incrementing.
53        mInjectedCurrentTimeMillis++;
54
55        final AtomicInteger resultCode = new AtomicInteger(Integer.MIN_VALUE);
56
57        final ResultReceiver rr = new ResultReceiver(mHandler) {
58            @Override
59            public void send(int resultCode_, Bundle resultData) {
60                resultCode.set(resultCode_);
61            }
62        };
63        final File out = File.createTempFile("shellout-", ".tmp",
64                getTestContext().getCacheDir());
65        try {
66            try (final ParcelFileDescriptor fd = ParcelFileDescriptor.open(out,
67                    ParcelFileDescriptor.MODE_READ_WRITE)) {
68                mService.onShellCommand(
69                    /* fdin*/ null,
70                    /* fdout*/ fd.getFileDescriptor(),
71                    /* fderr*/ fd.getFileDescriptor(),
72                        args, null, rr);
73            }
74            return readAll(out);
75        } finally {
76            out.delete();
77        }
78    }
79
80    public void testNonShell() throws Exception {
81        mService.mMaxUpdatesPerInterval = 99;
82
83        mInjectedCallingUid = 12345;
84        assertExpectException(SecurityException.class, "must be shell",
85                () -> callShellCommand("reset-config"));
86
87        mInjectedCallingUid = Process.SYSTEM_UID;
88        assertExpectException(SecurityException.class, "must be shell",
89                () -> callShellCommand("reset-config"));
90
91        assertEquals(99, mService.mMaxUpdatesPerInterval);
92    }
93
94    public void testRoot() throws Exception {
95        mService.mMaxUpdatesPerInterval = 99;
96
97        mInjectedCallingUid = Process.ROOT_UID;
98        assertSuccess(callShellCommand("reset-config"));
99
100        assertEquals(3, mService.mMaxUpdatesPerInterval);
101    }
102
103    public void testRestConfig() throws Exception {
104        mService.mMaxUpdatesPerInterval = 99;
105
106        mInjectedCallingUid = Process.SHELL_UID;
107        assertSuccess(callShellCommand("reset-config"));
108
109        assertEquals(3, mService.mMaxUpdatesPerInterval);
110    }
111
112    public void testOverrideConfig() throws Exception {
113        mService.mMaxUpdatesPerInterval = 99;
114
115        mInjectedCallingUid = Process.SHELL_UID;
116        assertSuccess(callShellCommand("override-config",
117                ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=1"));
118
119        assertEquals(1, mService.mMaxUpdatesPerInterval);
120    }
121
122    public void testResetThrottling() throws Exception {
123        prepareCrossProfileDataSet();
124
125        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
126            assertTrue(mManager.getRemainingCallCount() < 3);
127        });
128        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
129            assertTrue(mManager.getRemainingCallCount() < 3);
130        });
131
132        mInjectedCallingUid = Process.SHELL_UID;
133        assertSuccess(callShellCommand("reset-throttling"));
134
135        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
136            assertEquals(3, mManager.getRemainingCallCount());
137        });
138        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
139            assertTrue(mManager.getRemainingCallCount() < 3);
140        });
141    }
142
143    public void testResetThrottling_user_not_running() throws Exception {
144        prepareCrossProfileDataSet();
145
146        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
147            assertTrue(mManager.getRemainingCallCount() < 3);
148        });
149        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
150            assertTrue(mManager.getRemainingCallCount() < 3);
151        });
152
153        mInjectedCallingUid = Process.SHELL_UID;
154
155        mRunningUsers.put(USER_10, false);
156
157        assertTrue(resultContains(
158                callShellCommand("reset-throttling", "--user", "10"),
159                "User 10 is not running or locked"));
160
161        mRunningUsers.put(USER_10, true);
162
163        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
164            assertTrue(mManager.getRemainingCallCount() < 3);
165        });
166        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
167            assertTrue(mManager.getRemainingCallCount() < 3);
168        });
169    }
170
171    public void testResetThrottling_user_running() throws Exception {
172        prepareCrossProfileDataSet();
173
174        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
175            assertTrue(mManager.getRemainingCallCount() < 3);
176        });
177        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
178            assertTrue(mManager.getRemainingCallCount() < 3);
179        });
180
181        mRunningUsers.put(USER_10, true);
182        mUnlockedUsers.put(USER_10, true);
183
184        mInjectedCallingUid = Process.SHELL_UID;
185        assertSuccess(callShellCommand("reset-throttling", "--user", "10"));
186
187        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
188            assertTrue(mManager.getRemainingCallCount() < 3);
189        });
190        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
191            assertEquals(3, mManager.getRemainingCallCount());
192        });
193    }
194
195    public void testResetAllThrottling() throws Exception {
196        prepareCrossProfileDataSet();
197
198        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
199            assertTrue(mManager.getRemainingCallCount() < 3);
200        });
201        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
202            assertTrue(mManager.getRemainingCallCount() < 3);
203        });
204
205        mInjectedCallingUid = Process.SHELL_UID;
206        assertSuccess(callShellCommand("reset-all-throttling"));
207
208        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
209            assertEquals(3, mManager.getRemainingCallCount());
210        });
211        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
212            assertEquals(3, mManager.getRemainingCallCount());
213        });
214    }
215
216    public void testLauncherCommands() throws Exception {
217        prepareGetHomeActivitiesAsUser(
218                /* preferred */ null,
219                list(getSystemLauncher(), getFallbackLauncher()),
220                USER_0);
221
222        prepareGetHomeActivitiesAsUser(
223                /* preferred */ cn(CALLING_PACKAGE_2, "name"),
224                list(getSystemLauncher(), getFallbackLauncher(),
225                        ri(CALLING_PACKAGE_1, "name", false, 0),
226                        ri(CALLING_PACKAGE_2, "name", false, 0)
227                ),
228                USER_10);
229
230        assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
231
232        // First, test "get".
233
234        mRunningUsers.put(USER_10, true);
235        mUnlockedUsers.put(USER_10, true);
236        mInjectedCallingUid = Process.SHELL_UID;
237        assertContains(
238                assertSuccess(callShellCommand("get-default-launcher")),
239                "Launcher: ComponentInfo{com.android.systemlauncher/systemlauncher_name}");
240
241        assertContains(
242                assertSuccess(callShellCommand("get-default-launcher", "--user", "10")),
243                "Launcher: ComponentInfo{com.android.test.2/name}");
244
245        // Next, test "clear".
246        assertSuccess(callShellCommand("clear-default-launcher", "--user", "10"));
247
248        // User-10's launcher should be cleared.
249        assertEquals(null, mService.getUserShortcutsLocked(USER_10).getLastKnownLauncher());
250        assertEquals(null, mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
251
252        // but user'0's shouldn't.
253        assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
254                mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
255
256        // Change user-0's launcher.
257        prepareGetHomeActivitiesAsUser(
258                /* preferred */ cn(CALLING_PACKAGE_1, "name"),
259                list(
260                        ri(CALLING_PACKAGE_1, "name", false, 0)
261                ),
262                USER_0);
263        assertContains(
264                assertSuccess(callShellCommand("get-default-launcher")),
265                "Launcher: ComponentInfo{com.android.test.1/name}");
266    }
267
268    public void testUnloadUser() throws Exception {
269        prepareCrossProfileDataSet();
270
271        assertNotNull(mService.getShortcutsForTest().get(USER_10));
272
273        mRunningUsers.put(USER_10, true);
274        mUnlockedUsers.put(USER_10, true);
275
276        mInjectedCallingUid = Process.SHELL_UID;
277        assertSuccess(callShellCommand("unload-user", "--user", "10"));
278
279        assertNull(mService.getShortcutsForTest().get(USER_10));
280    }
281
282    public void testClearShortcuts() throws Exception {
283
284        mRunningUsers.put(USER_10, true);
285
286        // Add two manifests and two dynamics.
287        addManifestShortcutResource(
288                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
289                R.xml.shortcut_2);
290        updatePackageVersion(CALLING_PACKAGE_1, 1);
291        mService.mPackageMonitor.onReceive(getTestContext(),
292                genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
293
294        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
295            assertTrue(mManager.addDynamicShortcuts(list(
296                    makeShortcut("s1"), makeShortcut("s2"))));
297        });
298        runWithCaller(LAUNCHER_1, USER_10, () -> {
299            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10);
300        });
301
302        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
303            assertWith(getCallerShortcuts())
304                    .haveIds("ms1", "ms2", "s1", "s2")
305                    .areAllEnabled()
306
307                    .selectPinned()
308                    .haveIds("ms2", "s2");
309        });
310
311        // First, call for a different package.
312
313        mRunningUsers.put(USER_10, true);
314        mUnlockedUsers.put(USER_10, true);
315
316        mInjectedCallingUid = Process.SHELL_UID;
317        assertSuccess(callShellCommand("clear-shortcuts", "--user", "10", CALLING_PACKAGE_2));
318
319        // Shouldn't be cleared yet.
320        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
321            assertWith(getCallerShortcuts())
322                    .haveIds("ms1", "ms2", "s1", "s2")
323                    .areAllEnabled()
324
325                    .selectPinned()
326                    .haveIds("ms2", "s2");
327        });
328
329        mInjectedCallingUid = Process.SHELL_UID;
330        assertSuccess(callShellCommand("clear-shortcuts", "--user", "10", CALLING_PACKAGE_1));
331
332        // Only manifest shortcuts will remain, and are no longer pinned.
333        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
334            assertWith(getCallerShortcuts())
335                    .haveIds("ms1", "ms2")
336                    .areAllEnabled()
337                    .areAllNotPinned();
338        });
339    }
340
341    public void testDumpsysArgs() {
342        checkDumpsysArgs(null, true, false, false);
343        checkDumpsysArgs(array("-u"), true, true, false);
344        checkDumpsysArgs(array("--uid"), true, true, false);
345
346        checkDumpsysArgs(array("-f"), true, false, true);
347        checkDumpsysArgs(array("--files"), true, false, true);
348
349        checkDumpsysArgs(array("-a"), true, true, true);
350        checkDumpsysArgs(array("--all"), true, true, true);
351
352        checkDumpsysArgs(array("-a", "-n"), false, true, true);
353        checkDumpsysArgs(array("-a", "--no-main"), false, true, true);
354    }
355
356    private void checkDumpsysArgs(String[] args, boolean expectMain, boolean expectUid,
357            boolean expectDumpFiles) {
358        String dump = dumpsys(args);
359
360        assertEquals(expectMain, dump.contains("Icon format: PNG"));
361        assertEquals(expectUid, dump.contains("SHORTCUT MANAGER UID STATES"));
362        assertEquals(expectDumpFiles, dump.contains("SHORTCUT MANAGER FILES"));
363    }
364
365}
366