/* * 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.assertBundlesEqual; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException; 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.makeBundle; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.parceled; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.Manifest.permission; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.graphics.BitmapFactory; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.PersistableBundle; import android.os.UserHandle; import android.test.MoreAsserts; 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.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.util.Locale; /** * Tests for ShortcutService and ShortcutManager. * m FrameworksServicesTests && adb install \ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest2 \ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner */ @SmallTest public class ShortcutManagerTest2 extends BaseShortcutManagerTest { // ShortcutInfo tests public void testShortcutInfoMissingMandatoryFields() { // Disable throttling. mService.updateConfigurationLocked( ShortcutService.ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999," + ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=99999999" ); assertExpectException( IllegalArgumentException.class, "ID must be provided", () -> new ShortcutInfo.Builder(getTestContext()).build()); assertExpectException( RuntimeException.class, "id cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), null)); assertExpectException( RuntimeException.class, "id cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "")); assertExpectException( RuntimeException.class, "intents cannot contain null", () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(null)); assertExpectException( RuntimeException.class, "action must be set", () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent())); assertExpectException( RuntimeException.class, "action must be set", () -> new ShortcutInfo.Builder(getTestContext(), "id") .setIntents(new Intent[]{new Intent("action"), new Intent()})); assertExpectException( RuntimeException.class, "activity cannot be null", () -> new ShortcutInfo.Builder(getTestContext(), "id").setActivity(null)); assertExpectException( RuntimeException.class, "shortLabel cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "id").setShortLabel(null)); assertExpectException( RuntimeException.class, "shortLabel cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "id").setShortLabel("")); assertExpectException( RuntimeException.class, "longLabel cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "id").setLongLabel(null)); assertExpectException( RuntimeException.class, "longLabel cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "id").setLongLabel("")); assertExpectException( RuntimeException.class, "disabledMessage cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "id").setDisabledMessage(null)); assertExpectException( RuntimeException.class, "disabledMessage cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "id").setDisabledMessage("")); assertExpectException(NullPointerException.class, "action must be set", () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent())); assertExpectException( IllegalArgumentException.class, "Short label must be provided", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName(getTestContext().getPackageName(), "s")) .build(); assertTrue(getManager().setDynamicShortcuts(list(si))); }); // same for add. assertExpectException( IllegalArgumentException.class, "Short label must be provided", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName(getTestContext().getPackageName(), "s")) .build(); assertTrue(getManager().addDynamicShortcuts(list(si))); }); assertExpectException(NullPointerException.class, "Intent must be provided", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName(getTestContext().getPackageName(), "s")) .setShortLabel("x") .build(); assertTrue(getManager().setDynamicShortcuts(list(si))); }); // same for add. assertExpectException(NullPointerException.class, "Intent must be provided", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName(getTestContext().getPackageName(), "s")) .setShortLabel("x") .build(); assertTrue(getManager().addDynamicShortcuts(list(si))); }); assertExpectException( IllegalStateException.class, "does not belong to package", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName("xxx", "s")) .build(); assertTrue(getManager().setDynamicShortcuts(list(si))); }); // same for add. assertExpectException( IllegalStateException.class, "does not belong to package", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName("xxx", "s")) .build(); assertTrue(getManager().addDynamicShortcuts(list(si))); }); // Now all activities are not main. mMainActivityChecker = (component, userId) -> false; assertExpectException( IllegalStateException.class, "is not main", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName(getTestContext(), "s")) .build(); assertTrue(getManager().setDynamicShortcuts(list(si))); }); // For add assertExpectException( IllegalStateException.class, "is not main", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName(getTestContext(), "s")) .build(); assertTrue(getManager().addDynamicShortcuts(list(si))); }); // For update assertExpectException( IllegalStateException.class, "is not main", () -> { ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id") .setActivity(new ComponentName(getTestContext(), "s")) .build(); assertTrue(getManager().updateShortcuts(list(si))); }); } public void testShortcutInfoParcel() { setCaller(CALLING_PACKAGE_1, USER_10); ShortcutInfo si = parceled(new ShortcutInfo.Builder(mClientContext) .setId("id") .setTitle("title") .setIntent(makeIntent("action", ShortcutActivity.class)) .build()); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals(USER_10, si.getUserId()); assertEquals(HANDLE_USER_10, si.getUserHandle()); assertEquals("id", si.getId()); assertEquals("title", si.getTitle()); assertEquals("action", si.getIntent().getAction()); PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); si = new ShortcutInfo.Builder(getTestContext()) .setId("id") .setActivity(new ComponentName("a", "b")) .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitle("title") .setText("text") .setDisabledMessage("dismes") .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) .setExtras(pb) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); si.setIconResourceId(456); si = parceled(si); assertEquals(getTestContext().getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(123, si.getIcon().getResId()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals(0, si.getTitleResId()); assertEquals(null, si.getTitleResName()); assertEquals(0, si.getTextResId()); assertEquals(null, si.getTextResName()); assertEquals(0, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); } public void testShortcutInfoParcel_resId() { setCaller(CALLING_PACKAGE_1, USER_10); ShortcutInfo si; PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); si = new ShortcutInfo.Builder(getTestContext()) .setId("id") .setActivity(new ComponentName("a", "b")) .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitleResId(10) .setTextResId(11) .setDisabledMessageResId(12) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) .setExtras(pb) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); si.setIconResourceId(456); lookupAndFillInResourceNames(si); si = parceled(si); assertEquals(getTestContext().getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(123, si.getIcon().getResId()); assertEquals(10, si.getTitleResId()); assertEquals("r10", si.getTitleResName()); assertEquals(11, si.getTextResId()); assertEquals("r11", si.getTextResName()); assertEquals(12, si.getDisabledMessageResourceId()); assertEquals("r12", si.getDisabledMessageResName()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); } public void testShortcutInfoClone() { setCaller(CALLING_PACKAGE_1, USER_11); PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName("a", "b")) .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitle("title") .setText("text") .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); sorig.setIconResourceId(456); lookupAndFillInResourceNames(sorig); ShortcutInfo si = sorig.clone(/* clone flags*/ 0); assertEquals(USER_11, si.getUserId()); assertEquals(HANDLE_USER_11, si.getUserHandle()); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(123, si.getIcon().getResId()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(null, si.getIcon()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(null, si.getIcon()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals(null, si.getIntent()); assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(null, si.getIcon()); assertEquals(null, si.getTitle()); assertEquals(null, si.getText()); assertEquals(null, si.getDisabledMessage()); assertEquals(null, si.getCategories()); assertEquals(null, si.getIntent()); assertEquals(0, si.getRank()); assertEquals(null, si.getExtras()); assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); } public void testShortcutInfoClone_resId() { setCaller(CALLING_PACKAGE_1, USER_11); PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName("a", "b")) .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitleResId(10) .setTextResId(11) .setDisabledMessageResId(12) .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); sorig.setIconResourceId(456); lookupAndFillInResourceNames(sorig); ShortcutInfo si = sorig.clone(/* clone flags*/ 0); assertEquals(USER_11, si.getUserId()); assertEquals(HANDLE_USER_11, si.getUserHandle()); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(123, si.getIcon().getResId()); assertEquals(10, si.getTitleResId()); assertEquals("r10", si.getTitleResName()); assertEquals(11, si.getTextResId()); assertEquals("r11", si.getTextResName()); assertEquals(12, si.getDisabledMessageResourceId()); assertEquals("r12", si.getDisabledMessageResName()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(null, si.getIcon()); assertEquals(10, si.getTitleResId()); assertEquals(null, si.getTitleResName()); assertEquals(11, si.getTextResId()); assertEquals(null, si.getTextResName()); assertEquals(12, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(null, si.getIcon()); assertEquals(10, si.getTitleResId()); assertEquals(null, si.getTitleResName()); assertEquals(11, si.getTextResId()); assertEquals(null, si.getTextResName()); assertEquals(12, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals(null, si.getIntent()); assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); assertEquals(mClientContext.getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(new ComponentName("a", "b"), si.getActivity()); assertEquals(null, si.getIcon()); assertEquals(0, si.getTitleResId()); assertEquals(null, si.getTitleResName()); assertEquals(0, si.getTextResId()); assertEquals(null, si.getTextResName()); assertEquals(0, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); assertEquals(null, si.getCategories()); assertEquals(null, si.getIntent()); assertEquals(0, si.getRank()); assertEquals(null, si.getExtras()); assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); assertEquals(null, si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); } public void testShortcutInfoClone_minimum() { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext()) .setId("id") .setTitle("title") .setIntent(makeIntent("action", ShortcutActivity.class)) .build(); ShortcutInfo si = sorig.clone(/* clone flags*/ 0); assertEquals(getTestContext().getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals("title", si.getTitle()); assertEquals("action", si.getIntent().getAction()); assertEquals(null, si.getCategories()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); assertEquals(getTestContext().getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals("title", si.getTitle()); assertEquals("action", si.getIntent().getAction()); assertEquals(null, si.getCategories()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); assertEquals(getTestContext().getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals("title", si.getTitle()); assertEquals(null, si.getIntent()); assertEquals(null, si.getCategories()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); assertEquals(getTestContext().getPackageName(), si.getPackage()); assertEquals("id", si.getId()); assertEquals(null, si.getTitle()); assertEquals(null, si.getIntent()); assertEquals(null, si.getCategories()); } public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext()) .setId("id") .setActivity(new ComponentName("a", "b")) .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitle("title") .setText("text") .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); sorig.setIconResourceId(456); lookupAndFillInResourceNames(sorig); ShortcutInfo si; si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setActivity(new ComponentName("x", "y")).build()); assertEquals("text", si.getText()); assertEquals(123, si.getRank()); assertEquals(new ComponentName("x", "y"), si.getActivity()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setIcon(Icon.createWithResource(mClientContext, 456)).build()); assertEquals("text", si.getText()); assertEquals(456, si.getIcon().getResId()); assertEquals(0, si.getIconResourceId()); assertEquals(null, si.getIconResName()); assertEquals(null, si.getBitmapPath()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setTitle("xyz").build()); assertEquals("text", si.getText()); assertEquals("xyz", si.getTitle()); assertEquals(0, si.getTitleResId()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setTitleResId(123).build()); assertEquals("text", si.getText()); assertEquals(null, si.getTitle()); assertEquals(123, si.getTitleResId()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setText("xxx").build()); assertEquals(123, si.getRank()); assertEquals("xxx", si.getText()); assertEquals(0, si.getTextResId()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setTextResId(1111).build()); assertEquals(123, si.getRank()); assertEquals(null, si.getText()); assertEquals(1111, si.getTextResId()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setDisabledMessage("xxx").build()); assertEquals(123, si.getRank()); assertEquals("xxx", si.getDisabledMessage()); assertEquals(0, si.getDisabledMessageResourceId()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setDisabledMessageResId(11111).build()); assertEquals(123, si.getRank()); assertEquals(null, si.getDisabledMessage()); assertEquals(11111, si.getDisabledMessageResourceId()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setCategories(set()).build()); assertEquals("text", si.getText()); assertEquals(set(), si.getCategories()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setCategories(set("x")).build()); assertEquals("text", si.getText()); assertEquals(set("x"), si.getCategories()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setIntent(makeIntent("action2", ShortcutActivity.class)).build()); assertEquals("text", si.getText()); assertEquals("action2", si.getIntent().getAction()); assertEquals(null, si.getIntent().getStringExtra("key")); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build()); assertEquals("text", si.getText()); assertEquals("action3", si.getIntent().getAction()); assertEquals("x", si.getIntent().getStringExtra("key")); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setRank(999).build()); assertEquals("text", si.getText()); assertEquals(999, si.getRank()); PersistableBundle pb2 = new PersistableBundle(); pb2.putInt("x", 99); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setExtras(pb2).build()); assertEquals("text", si.getText()); assertEquals(99, si.getExtras().getInt("x")); } public void testShortcutInfoCopyNonNullFieldsFrom_resId() throws InterruptedException { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext()) .setId("id") .setActivity(new ComponentName("a", "b")) .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitleResId(10) .setTextResId(11) .setDisabledMessageResId(12) .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); sorig.setIconResourceId(456); ShortcutInfo si; si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setActivity(new ComponentName("x", "y")).build()); assertEquals(11, si.getTextResId()); assertEquals(new ComponentName("x", "y"), si.getActivity()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setIcon(Icon.createWithResource(mClientContext, 456)).build()); assertEquals(11, si.getTextResId()); assertEquals(456, si.getIcon().getResId()); assertEquals(0, si.getIconResourceId()); assertEquals(null, si.getIconResName()); assertEquals(null, si.getBitmapPath()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setTitle("xyz").build()); assertEquals(11, si.getTextResId()); assertEquals("xyz", si.getTitle()); assertEquals(0, si.getTitleResId()); assertEquals(null, si.getTitleResName()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setTitleResId(123).build()); assertEquals(11, si.getTextResId()); assertEquals(null, si.getTitle()); assertEquals(123, si.getTitleResId()); assertEquals(null, si.getTitleResName()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setText("xxx").build()); assertEquals(123, si.getRank()); assertEquals("xxx", si.getText()); assertEquals(0, si.getTextResId()); assertEquals(null, si.getTextResName()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setTextResId(1111).build()); assertEquals(123, si.getRank()); assertEquals(null, si.getText()); assertEquals(1111, si.getTextResId()); assertEquals(null, si.getTextResName()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setDisabledMessage("xxx").build()); assertEquals(123, si.getRank()); assertEquals("xxx", si.getDisabledMessage()); assertEquals(0, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setDisabledMessageResId(11111).build()); assertEquals(123, si.getRank()); assertEquals(null, si.getDisabledMessage()); assertEquals(11111, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setCategories(set()).build()); assertEquals(11, si.getTextResId()); assertEquals(set(), si.getCategories()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setCategories(set("x")).build()); assertEquals(11, si.getTextResId()); assertEquals(set("x"), si.getCategories()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setIntent(makeIntent("action2", ShortcutActivity.class)).build()); assertEquals(11, si.getTextResId()); assertEquals("action2", si.getIntent().getAction()); assertEquals(null, si.getIntent().getStringExtra("key")); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build()); assertEquals(11, si.getTextResId()); assertEquals("action3", si.getIntent().getAction()); assertEquals("x", si.getIntent().getStringExtra("key")); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setRank(999).build()); assertEquals(11, si.getTextResId()); assertEquals(999, si.getRank()); PersistableBundle pb2 = new PersistableBundle(); pb2.putInt("x", 99); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setExtras(pb2).build()); assertEquals(11, si.getTextResId()); assertEquals(99, si.getExtras().getInt("x")); } public void testShortcutInfoSaveAndLoad() throws InterruptedException { mRunningUsers.put(USER_10, true); setCaller(CALLING_PACKAGE_1, USER_10); final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource( getTestContext().getResources(), R.drawable.black_32x32)); PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIcon(bmp32x32) .setTitle("title") .setText("text") .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) .build(); sorig.setTimestamp(mInjectedCurrentTimeMillis); ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext) .setId("id2") .setTitle("x") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(456) .build(); sorig2.setTimestamp(mInjectedCurrentTimeMillis); mManager.addDynamicShortcuts(list(sorig, sorig2)); mInjectedCurrentTimeMillis += 1; final long now = mInjectedCurrentTimeMillis; mInjectedCurrentTimeMillis += 1; dumpsysOnLogcat("before save"); // Save and load. mService.saveDirtyInfo(); initService(); mService.handleUnlockUser(USER_10); dumpUserFile(USER_10); dumpsysOnLogcat("after load"); ShortcutInfo si; si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10); assertEquals(USER_10, si.getUserId()); assertEquals(HANDLE_USER_10, si.getUserHandle()); assertEquals(CALLING_PACKAGE_1, si.getPackage()); assertEquals("id", si.getId()); assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName()); assertEquals(null, si.getIcon()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(0, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags()); assertNotNull(si.getBitmapPath()); // Something should be set. assertEquals(0, si.getIconResourceId()); assertTrue(si.getLastChangedTimestamp() < now); // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts // to test it. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10); assertEquals(1, si.getRank()); dumpUserFile(USER_10); } public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException { mRunningUsers.put(USER_10, true); setCaller(CALLING_PACKAGE_1, USER_10); final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32); PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIcon(res32x32) .setTitleResId(10) .setTextResId(11) .setDisabledMessageResId(12) .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) .build(); sorig.setTimestamp(mInjectedCurrentTimeMillis); ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext) .setId("id2") .setTitle("x") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(456) .build(); sorig2.setTimestamp(mInjectedCurrentTimeMillis); mManager.addDynamicShortcuts(list(sorig, sorig2)); mInjectedCurrentTimeMillis += 1; final long now = mInjectedCurrentTimeMillis; mInjectedCurrentTimeMillis += 1; // Save and load. mService.saveDirtyInfo(); initService(); mService.handleUnlockUser(USER_10); ShortcutInfo si; si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10); assertEquals(USER_10, si.getUserId()); assertEquals(HANDLE_USER_10, si.getUserHandle()); assertEquals(CALLING_PACKAGE_1, si.getPackage()); assertEquals("id", si.getId()); assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName()); assertEquals(null, si.getIcon()); assertEquals(10, si.getTitleResId()); assertEquals("r10", si.getTitleResName()); assertEquals(11, si.getTextResId()); assertEquals("r11", si.getTextResName()); assertEquals(12, si.getDisabledMessageResourceId()); assertEquals("r12", si.getDisabledMessageResName()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(0, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_RES | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags()); assertNull(si.getBitmapPath()); assertEquals(R.drawable.black_32x32, si.getIconResourceId()); assertTrue(si.getLastChangedTimestamp() < now); // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts // to test it. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10); assertEquals(1, si.getRank()); } public void testShortcutInfoSaveAndLoad_forBackup() { setCaller(CALLING_PACKAGE_1, USER_0); final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource( getTestContext().getResources(), R.drawable.black_32x32)); PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIcon(bmp32x32) .setTitle("title") .setText("text") .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) .build(); ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext) .setId("id2") .setTitle("x") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(456) .build(); mManager.addDynamicShortcuts(list(sorig, sorig2)); // Dynamic shortcuts won't be backed up, so we need to pin it. setCaller(LAUNCHER_1, USER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id", "id2"), HANDLE_USER_0); // Do backup & restore. backupAndRestore(); mService.handleUnlockUser(USER_0); // Load user-0. ShortcutInfo si; si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0); assertEquals(CALLING_PACKAGE_1, si.getPackage()); assertEquals("id", si.getId()); assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName()); assertEquals(null, si.getIcon()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(0, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags()); assertNull(si.getBitmapPath()); // No icon. assertEquals(0, si.getIconResourceId()); // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0); assertEquals(0, si.getRank()); } public void testShortcutInfoSaveAndLoad_forBackup_resId() { setCaller(CALLING_PACKAGE_1, USER_0); final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32); PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIcon(res32x32) .setTitleResId(10) .setTextResId(11) .setDisabledMessageResId(12) .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) .build(); ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext) .setId("id2") .setTitle("x") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(456) .build(); mManager.addDynamicShortcuts(list(sorig, sorig2)); // Dynamic shortcuts won't be backed up, so we need to pin it. setCaller(LAUNCHER_1, USER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id", "id2"), HANDLE_USER_0); // Do backup & restore. backupAndRestore(); mService.handleUnlockUser(USER_0); // Load user-0. ShortcutInfo si; si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0); assertEquals(CALLING_PACKAGE_1, si.getPackage()); assertEquals("id", si.getId()); assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName()); assertEquals(null, si.getIcon()); assertEquals(10, si.getTitleResId()); assertEquals("r10", si.getTitleResName()); assertEquals(11, si.getTextResId()); assertEquals("r11", si.getTextResName()); assertEquals(12, si.getDisabledMessageResourceId()); assertEquals("r12", si.getDisabledMessageResName()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); assertEquals(0, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags()); assertNull(si.getBitmapPath()); // No icon. assertEquals(0, si.getIconResourceId()); assertEquals(null, si.getIconResName()); // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0); assertEquals(0, si.getRank()); } private void checkShortcutInfoSaveAndLoad_intents(Intent intent) { assertTrue(mManager.setDynamicShortcuts(list( makeShortcutWithIntent("s1", intent)))); initService(); mService.handleUnlockUser(USER_0); assertWith(getCallerShortcuts()) .haveIds("s1") .forShortcutWithId("s1", si -> { assertEquals(intent.getAction(), si.getIntent().getAction()); assertEquals(intent.getData(), si.getIntent().getData()); assertEquals(intent.getComponent(), si.getIntent().getComponent()); assertBundlesEqual(intent.getExtras(), si.getIntent().getExtras()); }); } private void checkShortcutInfoSaveAndLoad_intents(Intent... intents) { assertTrue(mManager.setDynamicShortcuts(list( makeShortcutWithIntents("s1", intents)))); initService(); mService.handleUnlockUser(USER_0); assertWith(getCallerShortcuts()) .haveIds("s1") .forShortcutWithId("s1", si -> { final Intent[] actual = si.getIntents(); assertEquals(intents.length, actual.length); for (int i = 0; i < intents.length; i++) { assertEquals(intents[i].getAction(), actual[i].getAction()); assertEquals(intents[i].getData(), actual[i].getData()); assertEquals(intents[i].getComponent(), actual[i].getComponent()); assertEquals(intents[i].getFlags(), actual[i].getFlags()); assertBundlesEqual(intents[i].getExtras(), actual[i].getExtras()); } }); } public void testShortcutInfoSaveAndLoad_intents() { checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW)); mInjectedCurrentTimeMillis += INTERVAL; // reset throttling. checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_MAIN)); mInjectedCurrentTimeMillis += INTERVAL; // reset throttling. checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com/"))); mInjectedCurrentTimeMillis += INTERVAL; // reset throttling. checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_MAIN, Uri.parse("http://www.example.com/"))); mInjectedCurrentTimeMillis += INTERVAL; // reset throttling. checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW) .setComponent(new ComponentName("a", "b"))); mInjectedCurrentTimeMillis += INTERVAL; // reset throttling. checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_MAIN) .setComponent(new ComponentName("a", "b"))); mInjectedCurrentTimeMillis += INTERVAL; // reset throttling. checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW) .putExtras(makeBundle("a", "b"))); mInjectedCurrentTimeMillis += INTERVAL; // reset throttling. checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_MAIN) .putExtras(makeBundle("a", "b"))); mInjectedCurrentTimeMillis += INTERVAL; // reset throttling. // Multi-intents checkShortcutInfoSaveAndLoad_intents( new Intent(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK), new Intent(Intent.ACTION_VIEW).setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) ); checkShortcutInfoSaveAndLoad_intents( new Intent(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) .setComponent(new ComponentName("a", "b")), new Intent(Intent.ACTION_VIEW) .setComponent(new ComponentName("a", "b")) ); checkShortcutInfoSaveAndLoad_intents( new Intent(Intent.ACTION_MAIN) .setComponent(new ComponentName("a", "b")), new Intent(Intent.ACTION_VIEW).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) .setComponent(new ComponentName("a", "b")), new Intent("xyz").setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FILL_IN_COMPONENT) .setComponent(new ComponentName("a", "b")).putExtras( makeBundle("xx", "yy")) ); } public void testThrottling() { final ShortcutInfo si1 = makeShortcut("shortcut1"); assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(2, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(1, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(0, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); // Reached the max mInjectedCurrentTimeMillis++; assertFalse(mManager.setDynamicShortcuts(list(si1))); assertEquals(0, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); // Still throttled mInjectedCurrentTimeMillis = START_TIME + INTERVAL - 1; assertFalse(mManager.setDynamicShortcuts(list(si1))); assertEquals(0, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); // Now it should work. mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); // fail assertEquals(2, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(1, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(0, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis++; assertFalse(mManager.setDynamicShortcuts(list(si1))); assertEquals(0, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime()); // 4 hours later... mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(2, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(1, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime()); // Make sure getRemainingCallCount() itself gets reset without calling setDynamicShortcuts(). mInjectedCurrentTimeMillis = START_TIME + 8 * INTERVAL; assertEquals(3, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(2, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime()); } public void testThrottling_rewind() { final ShortcutInfo si1 = makeShortcut("shortcut1"); assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(2, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis = 12345; // Clock reset! // Since the clock looks invalid, the counter shouldn't have reset. assertEquals(2, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); // Forward again. Still haven't reset yet. mInjectedCurrentTimeMillis = START_TIME + INTERVAL - 1; assertEquals(2, mManager.getRemainingCallCount()); assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); // Now rewind -- this will reset the counters. mInjectedCurrentTimeMillis = START_TIME - 100000; assertEquals(3, mManager.getRemainingCallCount()); assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(2, mManager.getRemainingCallCount()); // Forward again, should be reset now. mInjectedCurrentTimeMillis += INTERVAL; assertEquals(3, mManager.getRemainingCallCount()); } public void testThrottling_perPackage() { final ShortcutInfo si1 = makeShortcut("shortcut1"); assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(2, mManager.getRemainingCallCount()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(1, mManager.getRemainingCallCount()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(0, mManager.getRemainingCallCount()); // Reached the max mInjectedCurrentTimeMillis++; assertFalse(mManager.setDynamicShortcuts(list(si1))); // Try from a different caller. mInjectedClientPackage = CALLING_PACKAGE_2; mInjectedCallingUid = CALLING_UID_2; // Need to create a new one wit the updated package name. final ShortcutInfo si2 = makeShortcut("shortcut1"); assertEquals(3, mManager.getRemainingCallCount()); assertTrue(mManager.setDynamicShortcuts(list(si2))); assertEquals(2, mManager.getRemainingCallCount()); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si2))); assertEquals(1, mManager.getRemainingCallCount()); // Back to the original caller, still throttled. mInjectedClientPackage = CALLING_PACKAGE_1; mInjectedCallingUid = CALLING_UID_1; mInjectedCurrentTimeMillis = START_TIME + INTERVAL - 1; assertEquals(0, mManager.getRemainingCallCount()); assertFalse(mManager.setDynamicShortcuts(list(si1))); assertEquals(0, mManager.getRemainingCallCount()); // Now it should work. mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); mInjectedCurrentTimeMillis++; assertTrue(mManager.setDynamicShortcuts(list(si1))); mInjectedCurrentTimeMillis++; assertFalse(mManager.setDynamicShortcuts(list(si1))); mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL; assertTrue(mManager.setDynamicShortcuts(list(si1))); assertTrue(mManager.setDynamicShortcuts(list(si1))); assertTrue(mManager.setDynamicShortcuts(list(si1))); assertFalse(mManager.setDynamicShortcuts(list(si1))); mInjectedClientPackage = CALLING_PACKAGE_2; mInjectedCallingUid = CALLING_UID_2; assertEquals(3, mManager.getRemainingCallCount()); assertTrue(mManager.setDynamicShortcuts(list(si2))); assertTrue(mManager.setDynamicShortcuts(list(si2))); assertTrue(mManager.setDynamicShortcuts(list(si2))); assertFalse(mManager.setDynamicShortcuts(list(si2))); } public void testThrottling_localeChanges() { prepareCrossProfileDataSet(); dumpsysOnLogcat("Before save & load"); mService.saveDirtyInfo(); initService(); mInjectedLocale = Locale.CHINA; mService.mReceiver.onReceive(mServiceContext, new Intent(Intent.ACTION_LOCALE_CHANGED)); // Note at this point only user-0 is loaded, and the counters are reset for this user, // but it will work for other users too because we check the locale change at any // API entry point. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); // Make sure even if we receive ACTION_LOCALE_CHANGED, if the locale hasn't actually // changed, we don't reset throttling. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { mManager.updateShortcuts(list()); assertEquals(2, mManager.getRemainingCallCount()); }); mService.mReceiver.onReceive(mServiceContext, new Intent(Intent.ACTION_LOCALE_CHANGED)); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(2, mManager.getRemainingCallCount()); // Still 2. }); mService.saveDirtyInfo(); initService(); // The locale should be persisted, so it still shouldn't reset throttling. mService.mReceiver.onReceive(mServiceContext, new Intent(Intent.ACTION_LOCALE_CHANGED)); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(2, mManager.getRemainingCallCount()); // Still 2. }); } public void testThrottling_foreground() throws Exception { prepareCrossProfileDataSet(); dumpsysOnLogcat("Before save & load"); mService.saveDirtyInfo(); initService(); // We need to update the current time from time to time, since some of the internal checks // rely on the time being correctly incremented. mInjectedCurrentTimeMillis++; // First, all packages have less than 3 (== initial value) remaining calls. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); mInjectedCurrentTimeMillis++; // State changed, but not foreground, so no resetting. mService.mUidObserver.onUidStateChanged( CALLING_UID_1, ActivityManager.PROCESS_STATE_TOP_SLEEPING); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); mInjectedCurrentTimeMillis++; // State changed, package1 foreground, reset. mService.mUidObserver.onUidStateChanged( CALLING_UID_1, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); mService.mUidObserver.onUidStateChanged( CALLING_UID_1, ActivityManager.PROCESS_STATE_TOP_SLEEPING); mInjectedCurrentTimeMillis++; // Different app comes to foreground briefly, and goes back to background. // Now, make sure package 2's counter is reset, even in this case. mService.mUidObserver.onUidStateChanged( CALLING_UID_2, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); mService.mUidObserver.onUidStateChanged( CALLING_UID_2, ActivityManager.PROCESS_STATE_TOP_SLEEPING); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); mInjectedCurrentTimeMillis++; // Do the same thing one more time. This would catch the bug with mixuing up // the current time and the elapsed time. runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { mManager.updateShortcuts(list(makeShortcut("s"))); MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); mService.mUidObserver.onUidStateChanged( CALLING_UID_2, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); mService.mUidObserver.onUidStateChanged( CALLING_UID_2, ActivityManager.PROCESS_STATE_TOP_SLEEPING); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); mInjectedCurrentTimeMillis++; // Package 1 on user-10 comes to foreground. // Now, also try calling some APIs and make sure foreground apps don't get throttled. mService.mUidObserver.onUidStateChanged( UserHandle.getUid(USER_10, CALLING_UID_1), ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); assertFalse(mManager.isRateLimitingActive()); mManager.setDynamicShortcuts(list(makeShortcut("s"))); assertEquals(2, mManager.getRemainingCallCount()); assertFalse(mManager.isRateLimitingActive()); mManager.setDynamicShortcuts(list(makeShortcut("s"))); assertEquals(1, mManager.getRemainingCallCount()); assertFalse(mManager.isRateLimitingActive()); mManager.setDynamicShortcuts(list(makeShortcut("s"))); assertEquals(0, mManager.getRemainingCallCount()); assertTrue(mManager.isRateLimitingActive()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); assertEquals(0, mManager.getRemainingCallCount()); assertTrue(mManager.isRateLimitingActive()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); assertEquals(0, mManager.getRemainingCallCount()); assertTrue(mManager.isRateLimitingActive()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); assertEquals(0, mManager.getRemainingCallCount()); assertTrue(mManager.isRateLimitingActive()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); assertEquals(0, mManager.getRemainingCallCount()); assertTrue(mManager.isRateLimitingActive()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertEquals(3, mManager.getRemainingCallCount()); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); mManager.setDynamicShortcuts(list(makeShortcut("s"))); assertEquals(3, mManager.getRemainingCallCount()); // Still 3! assertFalse(mManager.isRateLimitingActive()); }); } public void testThrottling_resetByInternalCall() throws Exception { prepareCrossProfileDataSet(); dumpsysOnLogcat("Before save & load"); mService.saveDirtyInfo(); initService(); // First, all packages have less than 3 (== initial value) remaining calls. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); // Simulate a call from sys UI. mCallerPermissions.add(permission.RESET_SHORTCUT_MANAGER_THROTTLING); mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); mManager.onApplicationActive(CALLING_PACKAGE_3, USER_0); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); mManager.onApplicationActive(CALLING_PACKAGE_1, USER_10); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount()); }); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertEquals(3, mManager.getRemainingCallCount()); }); } public void testReportShortcutUsed() { mRunningUsers.put(USER_10, true); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { reset(mMockUsageStatsManagerInternal); // Report with an nonexistent shortcut. mManager.reportShortcutUsed("s1"); verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( anyString(), anyString(), anyInt()); // Publish s2, but s1 still doesn't exist. mManager.setDynamicShortcuts(list(makeShortcut("s2"))); mManager.reportShortcutUsed("s1"); verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( anyString(), anyString(), anyInt()); mManager.reportShortcutUsed("s2"); verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( eq(CALLING_PACKAGE_1), eq("s2"), eq(USER_10)); }); runWithCaller(CALLING_PACKAGE_2, USER_10, () -> { // Try with a different package. reset(mMockUsageStatsManagerInternal); // Report with an nonexistent shortcut. mManager.reportShortcutUsed("s2"); verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( anyString(), anyString(), anyInt()); // Publish s2, but s1 still doesn't exist. mManager.setDynamicShortcuts(list(makeShortcut("s3"))); mManager.reportShortcutUsed("s2"); verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( anyString(), anyString(), anyInt()); mManager.reportShortcutUsed("s3"); verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( eq(CALLING_PACKAGE_2), eq("s3"), eq(USER_10)); }); } // Test for a ShortcutInfo method. public void testGetResourcePackageName() { assertEquals(null, ShortcutInfo.getResourcePackageName("")); assertEquals(null, ShortcutInfo.getResourcePackageName("abc")); assertEquals("p", ShortcutInfo.getResourcePackageName("p:")); assertEquals("p", ShortcutInfo.getResourcePackageName("p:xx")); assertEquals("pac", ShortcutInfo.getResourcePackageName("pac:")); } // Test for a ShortcutInfo method. public void testGetResourceTypeName() { assertEquals(null, ShortcutInfo.getResourceTypeName("")); assertEquals(null, ShortcutInfo.getResourceTypeName(":")); assertEquals(null, ShortcutInfo.getResourceTypeName("/")); assertEquals(null, ShortcutInfo.getResourceTypeName("/:")); assertEquals("a", ShortcutInfo.getResourceTypeName(":a/")); assertEquals("type", ShortcutInfo.getResourceTypeName("xxx:type/yyy")); } // Test for a ShortcutInfo method. public void testGetResourceTypeAndEntryName() { assertEquals(null, ShortcutInfo.getResourceTypeAndEntryName("")); assertEquals(null, ShortcutInfo.getResourceTypeAndEntryName("abc")); assertEquals("", ShortcutInfo.getResourceTypeAndEntryName("p:")); assertEquals("x", ShortcutInfo.getResourceTypeAndEntryName(":x")); assertEquals("x", ShortcutInfo.getResourceTypeAndEntryName("p:x")); assertEquals("xyz", ShortcutInfo.getResourceTypeAndEntryName("pac:xyz")); } // Test for a ShortcutInfo method. public void testGetResourceEntryName() { assertEquals(null, ShortcutInfo.getResourceEntryName("")); assertEquals(null, ShortcutInfo.getResourceEntryName("ab:")); assertEquals("", ShortcutInfo.getResourceEntryName("/")); assertEquals("abc", ShortcutInfo.getResourceEntryName("/abc")); assertEquals("abc", ShortcutInfo.getResourceEntryName("xyz/abc")); } // Test for a ShortcutInfo method. public void testLookUpResourceName_systemResources() { // For android system resources, lookUpResourceName will simply return the value as a // string, regardless of "withType". final Resources res = getTestContext().getResources(); assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res, android.R.string.cancel, true, getTestContext().getPackageName())); assertEquals("" + android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceName(res, android.R.drawable.alert_dark_frame, true, getTestContext().getPackageName())); assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res, android.R.string.cancel, false, getTestContext().getPackageName())); } public void testLookUpResourceName_appResources() { final Resources res = getTestContext().getResources(); assertEquals("shortcut_text1", ShortcutInfo.lookUpResourceName(res, R.string.shortcut_text1, false, getTestContext().getPackageName())); assertEquals("string/shortcut_text1", ShortcutInfo.lookUpResourceName(res, R.string.shortcut_text1, true, getTestContext().getPackageName())); assertEquals("black_16x64", ShortcutInfo.lookUpResourceName(res, R.drawable.black_16x64, false, getTestContext().getPackageName())); assertEquals("drawable/black_16x64", ShortcutInfo.lookUpResourceName(res, R.drawable.black_16x64, true, getTestContext().getPackageName())); } // Test for a ShortcutInfo method. public void testLookUpResourceId_systemResources() { final Resources res = getTestContext().getResources(); assertEquals(android.R.string.cancel, ShortcutInfo.lookUpResourceId(res, "" + android.R.string.cancel, null, getTestContext().getPackageName())); assertEquals(android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceId(res, "" + android.R.drawable.alert_dark_frame, null, getTestContext().getPackageName())); } // Test for a ShortcutInfo method. public void testLookUpResourceId_appResources() { final Resources res = getTestContext().getResources(); assertEquals(R.string.shortcut_text1, ShortcutInfo.lookUpResourceId(res, "shortcut_text1", "string", getTestContext().getPackageName())); assertEquals(R.string.shortcut_text1, ShortcutInfo.lookUpResourceId(res, "string/shortcut_text1", null, getTestContext().getPackageName())); assertEquals(R.drawable.black_16x64, ShortcutInfo.lookUpResourceId(res, "black_16x64", "drawable", getTestContext().getPackageName())); assertEquals(R.drawable.black_16x64, ShortcutInfo.lookUpResourceId(res, "drawable/black_16x64", null, getTestContext().getPackageName())); } public void testDumpCheckin() throws IOException { prepareCrossProfileDataSet(); // prepareCrossProfileDataSet() doesn't set any icons, so do set here. final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32); final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64); final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource( getTestContext().getResources(), R.drawable.black_32x32)); final Icon bmp64x64 = Icon.createWithBitmap(BitmapFactory.decodeResource( getTestContext().getResources(), R.drawable.black_64x64)); runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { assertTrue(mManager.setDynamicShortcuts(list( makeShortcutWithIcon("res32x32", res32x32), makeShortcutWithIcon("res64x64", res64x64), makeShortcutWithIcon("bmp32x32", bmp32x32), makeShortcutWithIcon("bmp64x64", bmp64x64)))); }); // We can't predict the compressed bitmap sizes, so get the real sizes here. final long bitmapTotal = new File(getPackageShortcut(CALLING_PACKAGE_2, "bmp32x32", USER_0) .getBitmapPath()).length() + new File(getPackageShortcut(CALLING_PACKAGE_2, "bmp64x64", USER_0) .getBitmapPath()).length(); // Read the expected output and inject the bitmap size. final String expected = readTestAsset("shortcut/dumpsys_expected.txt") .replace("***BITMAP_SIZE***", String.valueOf(bitmapTotal)); assertEquals(expected, dumpCheckin()); } public void testDumpsysNoPermission() { assertExpectException(SecurityException.class, "android.permission.DUMP", () -> mService.dump(null, new PrintWriter(new StringWriter()), null)); // System can call it without the permission. runWithSystemUid(() -> { mService.dump(null, new PrintWriter(new StringWriter()), null); }); } /** * Make sure the legacy file format that only supported a single intent per shortcut * can still be read. */ public void testLoadLegacySavedFile() throws Exception { final File path = mService.getUserFile(USER_0); path.getParentFile().mkdirs(); try (Writer w = new FileWriter(path)) { w.write(readTestAsset("shortcut/shortcut_legacy_file.xml")); }; initService(); mService.handleUnlockUser(USER_0); runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertWith(getCallerShortcuts()) .haveIds("manifest-shortcut-storage") .forShortcutWithId("manifest-shortcut-storage", si -> { assertEquals("android.settings.INTERNAL_STORAGE_SETTINGS", si.getIntent().getAction()); assertEquals(12345, si.getIntent().getIntExtra("key", 0)); }); }); } public void testIsUserUnlocked() { mRunningUsers.clear(); mUnlockedUsers.clear(); assertFalse(mService.isUserUnlockedL(USER_0)); assertFalse(mService.isUserUnlockedL(USER_10)); // Start user 0, still locked. mRunningUsers.put(USER_0, true); assertFalse(mService.isUserUnlockedL(USER_0)); assertFalse(mService.isUserUnlockedL(USER_10)); // Unlock user. mUnlockedUsers.put(USER_0, true); assertTrue(mService.isUserUnlockedL(USER_0)); assertFalse(mService.isUserUnlockedL(USER_10)); // Clear again. mRunningUsers.clear(); mUnlockedUsers.clear(); // Directly call the lifecycle event. Now also locked. mService.handleUnlockUser(USER_0); assertTrue(mService.isUserUnlockedL(USER_0)); assertFalse(mService.isUserUnlockedL(USER_10)); // Directly call the stop lifecycle event. Goes back to the initial state. mService.handleCleanupUser(USER_0); assertFalse(mService.isUserUnlockedL(USER_0)); assertFalse(mService.isUserUnlockedL(USER_10)); } }