• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package android.content.pm.cts.shortcutmanager;
17 
18 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.*;
19 
20 import android.app.Activity;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.ContextWrapper;
24 import android.content.Intent;
25 import android.content.pm.LauncherApps;
26 import android.content.pm.LauncherApps.ShortcutQuery;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ShortcutInfo;
29 import android.content.pm.ShortcutManager;
30 import android.content.res.Resources;
31 import android.graphics.drawable.AdaptiveIconDrawable;
32 import android.graphics.drawable.Drawable;
33 import android.graphics.drawable.Icon;
34 import android.os.Bundle;
35 import android.os.PersistableBundle;
36 import android.os.StrictMode;
37 import android.os.StrictMode.ThreadPolicy;
38 import android.os.UserHandle;
39 import androidx.annotation.NonNull;
40 import android.test.InstrumentationTestCase;
41 import android.text.TextUtils;
42 
43 import java.util.ArrayList;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.concurrent.atomic.AtomicReference;
48 
49 public abstract class ShortcutManagerCtsTestsBase extends InstrumentationTestCase {
50     protected static final String TAG = "ShortcutCTS";
51 
52     private static final boolean DUMPSYS_IN_TEARDOWN = false; // DO NOT SUBMIT WITH true
53 
54     /**
55      * Whether to enable strict mode or not.
56      *
57      * TODO Enable it after fixing b/68051728. Somehow violations would happen on the dashboard
58      * only and can't reproduce it locally.
59      */
60     private static final boolean ENABLE_STRICT_MODE = false;
61 
62     private static class SpoofingContext extends ContextWrapper {
63         private final String mPackageName;
64 
SpoofingContext(Context base, String packageName)65         public SpoofingContext(Context base, String packageName) {
66             super(base);
67             mPackageName = packageName;
68         }
69 
70         @Override
getPackageName()71         public String getPackageName() {
72             return mPackageName;
73         }
74     }
75 
76     private Context mCurrentCallerPackage;
77     private int mUserId;
78     private UserHandle mUserHandle;
79 
80     private String mOriginalLauncher;
81 
82     protected Context mPackageContext1;
83     protected Context mPackageContext2;
84     protected Context mPackageContext3;
85     protected Context mPackageContext4;
86 
87     protected Context mLauncherContext1;
88     protected Context mLauncherContext2;
89     protected Context mLauncherContext3;
90     protected Context mLauncherContext4;
91 
92     private LauncherApps mLauncherApps1;
93     private LauncherApps mLauncherApps2;
94     private LauncherApps mLauncherApps3;
95     private LauncherApps mLauncherApps4;
96 
97     private Map<Context, ShortcutManager> mManagers = new HashMap<>();
98     private Map<Context, LauncherApps> mLauncherAppses = new HashMap<>();
99 
100     private ShortcutManager mCurrentManager;
101     private LauncherApps mCurrentLauncherApps;
102 
103     private static final String[] ACTIVITIES_WITH_MANIFEST_SHORTCUTS = {
104             "Launcher_manifest_1",
105             "Launcher_manifest_2",
106             "Launcher_manifest_3",
107             "Launcher_manifest_4a",
108             "Launcher_manifest_4b",
109             "Launcher_manifest_error_1",
110             "Launcher_manifest_error_2",
111             "Launcher_manifest_error_3"
112     };
113 
114     private ComponentName mTargetActivityOverride;
115 
116     private static class ShortcutActivity extends Activity {
117     }
118 
119     @Override
setUp()120     protected void setUp() throws Exception {
121         super.setUp();
122 
123         mUserId = getTestContext().getUserId();
124         mUserHandle = android.os.Process.myUserHandle();
125 
126         resetConfig(getInstrumentation());
127         final String config = getOverrideConfig();
128         if (config != null) {
129             overrideConfig(getInstrumentation(), config);
130         }
131         mOriginalLauncher = getDefaultLauncher(getInstrumentation());
132 
133         mPackageContext1 = new SpoofingContext(getTestContext(),
134                 "android.content.pm.cts.shortcutmanager.packages.package1");
135         mPackageContext2 = new SpoofingContext(getTestContext(),
136                 "android.content.pm.cts.shortcutmanager.packages.package2");
137         mPackageContext3 = new SpoofingContext(getTestContext(),
138                 "android.content.pm.cts.shortcutmanager.packages.package3");
139         mPackageContext4 = new SpoofingContext(getTestContext(),
140                 "android.content.pm.cts.shortcutmanager.packages.package4");
141         mLauncherContext1 = new SpoofingContext(getTestContext(),
142                 "android.content.pm.cts.shortcutmanager.packages.launcher1");
143         mLauncherContext2 = new SpoofingContext(getTestContext(),
144                 "android.content.pm.cts.shortcutmanager.packages.launcher2");
145         mLauncherContext3 = new SpoofingContext(getTestContext(),
146                 "android.content.pm.cts.shortcutmanager.packages.launcher3");
147         mLauncherContext4 = new SpoofingContext(getTestContext(),
148                 "android.content.pm.cts.shortcutmanager.packages.launcher4");
149 
150         mLauncherApps1 = new LauncherApps(mLauncherContext1);
151         mLauncherApps2 = new LauncherApps(mLauncherContext2);
152         mLauncherApps3 = new LauncherApps(mLauncherContext3);
153         mLauncherApps4 = new LauncherApps(mLauncherContext4);
154 
155         clearShortcuts(getInstrumentation(), mUserId, mPackageContext1.getPackageName());
156         clearShortcuts(getInstrumentation(), mUserId, mPackageContext2.getPackageName());
157         clearShortcuts(getInstrumentation(), mUserId, mPackageContext3.getPackageName());
158         clearShortcuts(getInstrumentation(), mUserId, mPackageContext4.getPackageName());
159 
160         setCurrentCaller(mPackageContext1);
161 
162         // Make sure shortcuts are removed.
163         withCallers(getAllPublishers(), () -> {
164             // Clear all shortcuts.
165             clearShortcuts(getInstrumentation(), mUserId, getCurrentCallingPackage());
166 
167             disableActivitiesWithManifestShortucts();
168 
169             assertEquals("for " + getCurrentCallingPackage(),
170                     0, getManager().getDynamicShortcuts().size());
171             assertEquals("for " + getCurrentCallingPackage(),
172                     0, getManager().getPinnedShortcuts().size());
173             assertEquals("for " + getCurrentCallingPackage(),
174                     0, getManager().getManifestShortcuts().size());
175         });
176     }
177 
178     @Override
tearDown()179     protected void tearDown() throws Exception {
180         if (DUMPSYS_IN_TEARDOWN) {
181             dumpsysShortcut(getInstrumentation());
182         }
183 
184         withCallers(getAllPublishers(), () -> disableActivitiesWithManifestShortucts());
185 
186         resetConfig(getInstrumentation());
187 
188         if (!TextUtils.isEmpty(mOriginalLauncher)) {
189             setDefaultLauncher(getInstrumentation(), mOriginalLauncher);
190         }
191 
192         super.tearDown();
193     }
194 
getTestContext()195     protected Context getTestContext() {
196         return getInstrumentation().getContext();
197     }
198 
getUserHandle()199     protected UserHandle getUserHandle() {
200         return mUserHandle;
201     }
202 
getAllPublishers()203     protected List<Context> getAllPublishers() {
204         // 4 has a different signature, so we can't call for it.
205         return list(mPackageContext1, mPackageContext2, mPackageContext3);
206     }
207 
getAllLaunchers()208     protected List<Context> getAllLaunchers() {
209         // 4 has a different signature, so we can't call for it.
210         return list(mLauncherContext1, mLauncherContext2, mLauncherContext3);
211     }
212 
getAllCallers()213     protected List<Context> getAllCallers() {
214         return list(
215                 mPackageContext1, mPackageContext2, mPackageContext3, mPackageContext4,
216                 mLauncherContext1, mLauncherContext2, mLauncherContext3, mLauncherContext4);
217     }
218 
getActivity(String className)219     protected ComponentName getActivity(String className) {
220         return new ComponentName(getCurrentCallingPackage(),
221                 "android.content.pm.cts.shortcutmanager.packages." + className);
222 
223     }
224 
disableActivitiesWithManifestShortucts()225     protected void disableActivitiesWithManifestShortucts() {
226         if (getManager().getManifestShortcuts().size() > 0) {
227             // Disable DISABLED_ACTIVITIES
228             for (String className : ACTIVITIES_WITH_MANIFEST_SHORTCUTS) {
229                 enableManifestActivity(className, false);
230             }
231         }
232     }
233 
enableManifestActivity(String className, boolean enabled)234     protected void enableManifestActivity(String className, boolean enabled) {
235         getTestContext().getPackageManager().setComponentEnabledSetting(getActivity(className),
236                 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
237                         : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
238                 PackageManager.DONT_KILL_APP);
239     }
240 
setTargetActivityOverride(String className)241     protected void setTargetActivityOverride(String className) {
242         mTargetActivityOverride = getActivity(className);
243     }
244 
245 
withCallers(List<Context> callers, Runnable r)246     protected void withCallers(List<Context> callers, Runnable r) {
247         for (Context c : callers) {
248             runWithCaller(c, r);
249         }
250     }
251 
getOverrideConfig()252     protected String getOverrideConfig() {
253         return null;
254     }
255 
setCurrentCaller(Context callerContext)256     protected void setCurrentCaller(Context callerContext) {
257         mCurrentCallerPackage = callerContext;
258 
259         if (!mManagers.containsKey(mCurrentCallerPackage)) {
260             mManagers.put(mCurrentCallerPackage, new ShortcutManager(mCurrentCallerPackage));
261         }
262         mCurrentManager = mManagers.get(mCurrentCallerPackage);
263 
264         if (!mLauncherAppses.containsKey(mCurrentCallerPackage)) {
265             mLauncherAppses.put(mCurrentCallerPackage, new LauncherApps(mCurrentCallerPackage));
266         }
267         mCurrentLauncherApps = mLauncherAppses.get(mCurrentCallerPackage);
268 
269         mTargetActivityOverride = null;
270     }
271 
getCurrentCallerContext()272     protected Context getCurrentCallerContext() {
273         return mCurrentCallerPackage;
274     }
275 
getCurrentCallingPackage()276     protected String getCurrentCallingPackage() {
277         return getCurrentCallerContext().getPackageName();
278     }
279 
getManager()280     protected ShortcutManager getManager() {
281         return mCurrentManager;
282     }
283 
getLauncherApps()284     protected LauncherApps getLauncherApps() {
285         return mCurrentLauncherApps;
286     }
287 
runWithStrictMode(Runnable r)288     protected void runWithStrictMode(Runnable r) {
289         final ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
290         try {
291             if (ENABLE_STRICT_MODE) {
292                 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
293                         .detectAll()
294                         .penaltyDeath()
295                         .build());
296             }
297             r.run();
298         } finally {
299             StrictMode.setThreadPolicy(oldPolicy);
300         }
301     }
302 
runWithNoStrictMode(Runnable r)303     protected void runWithNoStrictMode(Runnable r) {
304         final ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
305         try {
306             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
307                     .permitAll()
308                     .build());
309             r.run();
310         } finally {
311             StrictMode.setThreadPolicy(oldPolicy);
312         }
313     }
314 
runWithCaller(Context callerContext, Runnable r)315     protected void runWithCaller(Context callerContext, Runnable r) {
316         final Context prev = mCurrentCallerPackage;
317 
318         setCurrentCaller(callerContext);
319 
320         r.run();
321 
322         setCurrentCaller(prev);
323     }
324 
runWithCallerWithStrictMode(Context callerContext, Runnable r)325     protected void runWithCallerWithStrictMode(Context callerContext, Runnable r) {
326         runWithCaller(callerContext, () -> runWithStrictMode(r));
327     }
328 
runWithCallerWithNoStrictMode(Context callerContext, Runnable r)329     protected void runWithCallerWithNoStrictMode(Context callerContext, Runnable r) {
330         runWithCaller(callerContext, () -> runWithNoStrictMode(r));
331     }
332 
makeBundle(Object... keysAndValues)333     public static Bundle makeBundle(Object... keysAndValues) {
334         assertTrue((keysAndValues.length % 2) == 0);
335 
336         if (keysAndValues.length == 0) {
337             return null;
338         }
339         final Bundle ret = new Bundle();
340 
341         for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
342             final String key = keysAndValues[i].toString();
343             final Object value = keysAndValues[i + 1];
344 
345             if (value == null) {
346                 ret.putString(key, null);
347             } else if (value instanceof Integer) {
348                 ret.putInt(key, (Integer) value);
349             } else if (value instanceof String) {
350                 ret.putString(key, (String) value);
351             } else if (value instanceof Bundle) {
352                 ret.putBundle(key, (Bundle) value);
353             } else {
354                 fail("Type not supported yet: " + value.getClass().getName());
355             }
356         }
357         return ret;
358     }
359 
makePersistableBundle(Object... keysAndValues)360     public static PersistableBundle makePersistableBundle(Object... keysAndValues) {
361         assertTrue((keysAndValues.length % 2) == 0);
362 
363         if (keysAndValues.length == 0) {
364             return null;
365         }
366         final PersistableBundle ret = new PersistableBundle();
367 
368         for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
369             final String key = keysAndValues[i].toString();
370             final Object value = keysAndValues[i + 1];
371 
372             if (value == null) {
373                 ret.putString(key, null);
374             } else if (value instanceof Integer) {
375                 ret.putInt(key, (Integer) value);
376             } else if (value instanceof String) {
377                 ret.putString(key, (String) value);
378             } else if (value instanceof PersistableBundle) {
379                 ret.putPersistableBundle(key, (PersistableBundle) value);
380             } else {
381                 fail("Type not supported yet: " + value.getClass().getName());
382             }
383         }
384         return ret;
385     }
386 
387     /**
388      * Make a shortcut with an ID.
389      */
makeShortcut(String id)390     protected ShortcutInfo makeShortcut(String id) {
391         return makeShortcut(id, "Title-" + id);
392     }
393 
makeShortcutWithRank(String id, int rank)394     protected ShortcutInfo makeShortcutWithRank(String id, int rank) {
395         return makeShortcut(
396                 id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
397                 makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank);
398     }
399 
400     /**
401      * Make a shortcut with an ID and a title.
402      */
makeShortcut(String id, String shortLabel)403     protected ShortcutInfo makeShortcut(String id, String shortLabel) {
404         return makeShortcut(
405                 id, shortLabel, /* activity =*/ null, /* icon =*/ null,
406                 makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
407     }
408 
makeShortcut(String id, ComponentName activity)409     protected ShortcutInfo makeShortcut(String id, ComponentName activity) {
410         return makeShortcut(
411                 id, "Title-" + id, activity, /* icon =*/ null,
412                 makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
413     }
414 
415     /**
416      * Make a shortcut with an ID and icon.
417      */
makeShortcutWithIcon(String id, Icon icon)418     protected ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
419         return makeShortcut(
420                 id, "Title-" + id, /* activity =*/ null, icon,
421                 makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
422     }
423 
424     /**
425      * Make multiple shortcuts with IDs.
426      */
makeShortcuts(String... ids)427     protected List<ShortcutInfo> makeShortcuts(String... ids) {
428         final ArrayList<ShortcutInfo> ret = new ArrayList();
429         for (String id : ids) {
430             ret.add(makeShortcut(id));
431         }
432         return ret;
433     }
434 
makeShortcutBuilder(String id)435     protected ShortcutInfo.Builder makeShortcutBuilder(String id) {
436         return new ShortcutInfo.Builder(getCurrentCallerContext(), id);
437     }
438 
439     /**
440      * Make a shortcut with details.
441      */
makeShortcut(String id, String shortLabel, ComponentName activity, Icon icon, Intent intent, int rank)442     protected ShortcutInfo makeShortcut(String id, String shortLabel, ComponentName activity,
443             Icon icon, Intent intent, int rank) {
444         final ShortcutInfo.Builder b = makeShortcutBuilder(id)
445                 .setShortLabel(shortLabel)
446                 .setRank(rank)
447                 .setIntent(intent);
448         if (activity != null) {
449             b.setActivity(activity);
450         } else if (mTargetActivityOverride != null) {
451             b.setActivity(mTargetActivityOverride);
452         }
453         if (icon != null) {
454             b.setIcon(icon);
455         }
456         return b.build();
457     }
458 
459     /**
460      * Make an intent.
461      */
makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues)462     protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) {
463         final Intent intent = new Intent(action);
464         intent.setComponent(makeComponent(clazz));
465         intent.replaceExtras(makeBundle(bundleKeysAndValues));
466         return intent;
467     }
468 
469     /**
470      * Make an component name, with the client context.
471      */
472     @NonNull
makeComponent(Class<?> clazz)473     protected ComponentName makeComponent(Class<?> clazz) {
474         return new ComponentName(getCurrentCallerContext(), clazz);
475     }
476 
getIconAsLauncher(Context launcherContext, String packageName, String shortcutId)477     protected Drawable getIconAsLauncher(Context launcherContext, String packageName,
478             String shortcutId) {
479         return getIconAsLauncher(launcherContext, packageName, shortcutId, /* withBadge=*/ true);
480     }
481 
getIconAsLauncher(Context launcherContext, String packageName, String shortcutId, boolean withBadge)482     protected Drawable getIconAsLauncher(Context launcherContext, String packageName,
483             String shortcutId, boolean withBadge) {
484         runWithNoStrictMode(() -> setDefaultLauncher(getInstrumentation(), launcherContext));
485 
486         final AtomicReference<Drawable> ret = new AtomicReference<>();
487 
488         runWithCallerWithNoStrictMode(launcherContext, () -> {
489             final ShortcutQuery q = new ShortcutQuery()
490                     .setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC
491                                     | ShortcutQuery.FLAG_MATCH_MANIFEST
492                                     | ShortcutQuery.FLAG_MATCH_PINNED
493                                     | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY)
494                     .setPackage(packageName)
495                     .setShortcutIds(list(shortcutId));
496             final List<ShortcutInfo> found = getLauncherApps().getShortcuts(q, getUserHandle());
497 
498             assertEquals("Shortcut not found", 1, found.size());
499 
500             if (withBadge) {
501                 ret.set(getLauncherApps().getShortcutBadgedIconDrawable(found.get(0), 0));
502             } else {
503                 ret.set(getLauncherApps().getShortcutIconDrawable(found.get(0), 0));
504             }
505         });
506         return ret.get();
507     }
508 
assertIconDimensions(Context launcherContext, String packageName, String shortcutId, Icon expectedIcon)509     protected void assertIconDimensions(Context launcherContext, String packageName,
510             String shortcutId, Icon expectedIcon) {
511         final Drawable actual = getIconAsLauncher(launcherContext, packageName, shortcutId);
512         if (actual == null && expectedIcon == null) {
513             return; // okay
514         }
515         final Drawable expected = expectedIcon.loadDrawable(getTestContext());
516         assertEquals(expected.getIntrinsicWidth(), actual.getIntrinsicWidth());
517         assertEquals(expected.getIntrinsicHeight(), actual.getIntrinsicHeight());
518     }
519 
assertIconDimensions(Icon expectedIcon, Drawable actual)520     protected void assertIconDimensions(Icon expectedIcon, Drawable actual) {
521         if (actual == null && expectedIcon == null) {
522             return; // okay
523         }
524         final Drawable expected = expectedIcon.loadDrawable(getTestContext());
525 
526         if (expected instanceof AdaptiveIconDrawable) {
527             assertTrue(actual instanceof AdaptiveIconDrawable);
528         }
529         assertEquals(expected.getIntrinsicWidth(), actual.getIntrinsicWidth());
530         assertEquals(expected.getIntrinsicHeight(), actual.getIntrinsicHeight());
531     }
532 
loadPackageDrawableIcon(Context packageContext, String resName)533     protected Icon loadPackageDrawableIcon(Context packageContext, String resName)
534             throws Exception {
535         final Resources res = getTestContext().getPackageManager().getResourcesForApplication(
536                 packageContext.getPackageName());
537 
538         // Note the resource package names don't have the numbers.
539         final int id = res.getIdentifier(resName, "drawable",
540                 "android.content.pm.cts.shortcutmanager.packages");
541         if (id == 0) {
542             fail("Drawable " + resName + " is not found in package "
543                     + packageContext.getPackageName());
544         }
545         return Icon.createWithResource(packageContext, id);
546     }
547 
loadCallerDrawableIcon(String resName)548     protected Icon loadCallerDrawableIcon(String resName) throws Exception {
549         return loadPackageDrawableIcon(getCurrentCallerContext(), resName);
550     }
551 
getShortcutsAsLauncher(int flags, String packageName)552     protected List<ShortcutInfo> getShortcutsAsLauncher(int flags, String packageName) {
553         return getShortcutsAsLauncher(flags, packageName, null, 0, null);
554     }
555 
getShortcutsAsLauncher( int flags, String packageName, String activityName, long changedSince, List<String> ids)556     protected List<ShortcutInfo> getShortcutsAsLauncher(
557             int flags, String packageName, String activityName,
558             long changedSince, List<String> ids) {
559         final ShortcutQuery q = new ShortcutQuery();
560         q.setQueryFlags(flags);
561         if (packageName != null) {
562             q.setPackage(packageName);
563             if (activityName != null) {
564                 q.setActivity(new ComponentName(packageName,
565                         "android.content.pm.cts.shortcutmanager.packages." + activityName));
566             }
567         }
568         q.setChangedSince(changedSince);
569         if (ids != null && ids.size() > 0) {
570             q.setShortcutIds(ids);
571         }
572         return getLauncherApps().getShortcuts(q, getUserHandle());
573     }
574 }
575