blob: ad86fd08cea97d8c0e1652f935b4d69b88837a10 [file] [log] [blame]
/*
* 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 org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ILauncherApps;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.os.BaseBundle;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.test.InstrumentationTestCase;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.SparseArray;
import com.android.frameworks.servicestests.R;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutService.ConfigConstants;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
import libcore.io.IoUtils;
import org.junit.Assert;
import org.mockito.ArgumentCaptor;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 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.ShortcutManagerTest \
-w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
* TODO: Add checks with assertAllNotHaveIcon()
*
* TODO: separate, detailed tests for ShortcutInfo (CTS?) *
*
* TODO: Cross-user test (do in CTS?)
*
*/
@SmallTest
public class ShortcutManagerTest extends InstrumentationTestCase {
private static final String TAG = "ShortcutManagerTest";
/**
* Whether to enable dump or not. Should be only true when debugging to avoid bugs where
* dump affecting the behavior.
*/
private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true
private class BaseContext extends MockContext {
@Override
public Object getSystemService(String name) {
switch (name) {
case Context.USER_SERVICE:
return mMockUserManager;
}
throw new UnsupportedOperationException();
}
@Override
public PackageManager getPackageManager() {
return mMockPackageManager;
}
@Override
public Resources getResources() {
return getTestContext().getResources();
}
@Override
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
IntentFilter filter, String broadcastPermission, Handler scheduler) {
// ignore.
return null;
}
}
/** Context used in the client side */
private class ClientContext extends BaseContext {
@Override
public String getPackageName() {
return mInjectedClientPackage;
}
}
/** Context used in the service side */
private final class ServiceContext extends BaseContext {
long injectClearCallingIdentity() {
final int prevCallingUid = mInjectedCallingUid;
mInjectedCallingUid = Process.SYSTEM_UID;
return prevCallingUid;
}
void injectRestoreCallingIdentity(long token) {
mInjectedCallingUid = (int) token;
}
}
/** ShortcutService with injection override methods. */
private final class ShortcutServiceTestable extends ShortcutService {
final ServiceContext mContext;
public ShortcutServiceTestable(ServiceContext context, Looper looper) {
super(context, looper);
mContext = context;
}
@Override
String injectShortcutManagerConstants() {
return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
+ ConfigConstants.KEY_MAX_SHORTCUTS + "=" + MAX_SHORTCUTS + ","
+ ConfigConstants.KEY_MAX_DAILY_UPDATES + "=" + MAX_DAILY_UPDATES + ","
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=" + MAX_ICON_DIMENSION + ","
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "="
+ MAX_ICON_DIMENSION_LOWRAM + ","
+ ConfigConstants.KEY_ICON_FORMAT + "=PNG,"
+ ConfigConstants.KEY_ICON_QUALITY + "=100";
}
@Override
long injectClearCallingIdentity() {
return mContext.injectClearCallingIdentity();
}
@Override
void injectRestoreCallingIdentity(long token) {
mContext.injectRestoreCallingIdentity(token);
}
@Override
int injectDipToPixel(int dip) {
return dip;
}
@Override
long injectCurrentTimeMillis() {
return mInjectedCurrentTimeLillis;
}
@Override
int injectBinderCallingUid() {
return mInjectedCallingUid;
}
@Override
int injectGetPackageUid(String packageName, int userId) {
Integer uid = mInjectedPackageUidMap.get(packageName);
return UserHandle.getUid(getCallingUserId(), (uid != null ? uid : 0));
}
@Override
File injectSystemDataPath() {
return new File(mInjectedFilePathRoot, "system");
}
@Override
File injectUserDataPath(@UserIdInt int userId) {
return new File(mInjectedFilePathRoot, "user-" + userId);
}
@Override
void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
// Can't check
}
@Override
boolean injectIsLowRamDevice() {
return mInjectdIsLowRamDevice;
}
@Override
PackageManagerInternal injectPackageManagerInternal() {
return mMockPackageManagerInternal;
}
@Override
boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
// Sort of hack; do a simpler check.
return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage);
}
@Override
void postToHandler(Runnable r) {
final long token = mContext.injectClearCallingIdentity();
super.postToHandler(r);
try {
runTestOnUiThread(() -> {});
} catch (Throwable e) {
fail("runTestOnUiThread failed: " + e);
}
mContext.injectRestoreCallingIdentity(token);
}
@Override
void wtf(String message, Exception e) {
// During tests, WTF is fatal.
fail(message + " exception: " + e);
}
}
/** ShortcutManager with injection override methods. */
private class ShortcutManagerTestable extends ShortcutManager {
public ShortcutManagerTestable(Context context, ShortcutServiceTestable service) {
super(context, service);
}
@Override
protected int injectMyUserId() {
return UserHandle.getUserId(mInjectedCallingUid);
}
}
private class LauncherAppImplTestable extends LauncherAppsImpl {
final ServiceContext mContext;
public LauncherAppImplTestable(ServiceContext context) {
super(context);
mContext = context;
}
@Override
public void ensureInUserProfiles(UserHandle userToCheck, String message) {
if (getCallingUserId() == userToCheck.getIdentifier()) {
return; // okay
}
assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
// SKIP
}
@Override
public void verifyCallingPackage(String callingPackage) {
// SKIP
}
@Override
boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser, String debugMsg) {
// This requires CROSS_USER
assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
return user.getIdentifier() == listeningUser.getIdentifier();
}
@Override
void postToPackageMonitorHandler(Runnable r) {
final long token = mContext.injectClearCallingIdentity();
r.run();
mContext.injectRestoreCallingIdentity(token);
}
}
private class LauncherAppsTestable extends LauncherApps {
public LauncherAppsTestable(Context context, ILauncherApps service) {
super(context, service);
}
}
public static class ShortcutActivity extends Activity {
}
public static class ShortcutActivity2 extends Activity {
}
public static class ShortcutActivity3 extends Activity {
}
private ServiceContext mServiceContext;
private ClientContext mClientContext;
private ShortcutServiceTestable mService;
private ShortcutManagerTestable mManager;
private ShortcutServiceInternal mInternal;
private LauncherAppImplTestable mLauncherAppImpl;
private LauncherAppsTestable mLauncherApps;
private File mInjectedFilePathRoot;
private long mInjectedCurrentTimeLillis;
private boolean mInjectdIsLowRamDevice;
private int mInjectedCallingUid;
private String mInjectedClientPackage;
private Map<String, Integer> mInjectedPackageUidMap;
private PackageManager mMockPackageManager;
private PackageManagerInternal mMockPackageManagerInternal;
private UserManager mMockUserManager;
private static final String CALLING_PACKAGE_1 = "com.android.test.1";
private static final int CALLING_UID_1 = 10001;
private static final String CALLING_PACKAGE_2 = "com.android.test.2";
private static final int CALLING_UID_2 = 10002;
private static final String CALLING_PACKAGE_3 = "com.android.test.3";
private static final int CALLING_UID_3 = 10003;
private static final String LAUNCHER_1 = "com.android.launcher.1";
private static final int LAUNCHER_UID_1 = 10011;
private static final String LAUNCHER_2 = "com.android.launcher.2";
private static final int LAUNCHER_UID_2 = 10012;
private static final int USER_0 = UserHandle.USER_SYSTEM;
private static final int USER_10 = 10;
private static final int USER_11 = 11;
private static final long START_TIME = 1440000000101L;
private static final long INTERVAL = 10000;
private static final int MAX_SHORTCUTS = 10;
private static final int MAX_DAILY_UPDATES = 3;
private static final int MAX_ICON_DIMENSION = 128;
private static final int MAX_ICON_DIMENSION_LOWRAM = 32;
@Override
protected void setUp() throws Exception {
super.setUp();
mServiceContext = new ServiceContext();
mClientContext = new ClientContext();
mMockPackageManager = mock(PackageManager.class);
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
mMockUserManager = mock(UserManager.class);
// Prepare injection values.
mInjectedCurrentTimeLillis = START_TIME;
mInjectedPackageUidMap = new HashMap<>();
mInjectedPackageUidMap.put(CALLING_PACKAGE_1, CALLING_UID_1);
mInjectedPackageUidMap.put(CALLING_PACKAGE_2, CALLING_UID_2);
mInjectedPackageUidMap.put(CALLING_PACKAGE_3, CALLING_UID_3);
mInjectedPackageUidMap.put(LAUNCHER_1, LAUNCHER_UID_1);
mInjectedPackageUidMap.put(LAUNCHER_2, LAUNCHER_UID_2);
mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
// Empty the data directory.
if (mInjectedFilePathRoot.exists()) {
Assert.assertTrue("failed to delete dir",
FileUtils.deleteContents(mInjectedFilePathRoot));
}
mInjectedFilePathRoot.mkdirs();
initService();
setCaller(CALLING_PACKAGE_1);
}
private Context getTestContext() {
return getInstrumentation().getContext();
}
/** (Re-) init the manager and the service. */
private void initService() {
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
// Instantiate targets.
mService = new ShortcutServiceTestable(mServiceContext, Looper.getMainLooper());
mManager = new ShortcutManagerTestable(mClientContext, mService);
mInternal = LocalServices.getService(ShortcutServiceInternal.class);
mLauncherAppImpl = new LauncherAppImplTestable(mServiceContext);
mLauncherApps = new LauncherAppsTestable(mClientContext, mLauncherAppImpl);
// Load the setting file.
mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
}
/** Replace the current calling package */
private void setCaller(String packageName, int userId) {
mInjectedClientPackage = packageName;
mInjectedCallingUid = UserHandle.getUid(userId,
Preconditions.checkNotNull(mInjectedPackageUidMap.get(packageName),
"Unknown package"));
}
private void setCaller(String packageName) {
setCaller(packageName, UserHandle.USER_SYSTEM);
}
private String getCallingPackage() {
return mInjectedClientPackage;
}
private void runWithCaller(String packageName, int userId, Runnable r) {
final String previousPackage = mInjectedClientPackage;
final int previousUid = mInjectedCallingUid;
setCaller(packageName, userId);
r.run();
setCaller(previousPackage, previousUid);
}
private int getCallingUserId() {
return UserHandle.getUserId(mInjectedCallingUid);
}
private UserHandle getCallingUser() {
return UserHandle.of(getCallingUserId());
}
/** For debugging */
private void dumpsysOnLogcat() {
if (!ENABLE_DUMP) return;
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintWriter pw = new PrintWriter(out);
mService.dumpInner(pw);
pw.close();
Log.e(TAG, "Dumping ShortcutService:");
for (String line : out.toString().split("\n")) {
Log.e(TAG, line);
}
}
/**
* For debugging, dump arbitrary file on logcat.
*/
private void dumpFileOnLogcat(String path) {
if (!ENABLE_DUMP) return;
Log.i(TAG, "Dumping file: " + path);
final StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
String line;
while ((line = br.readLine()) != null) {
Log.i(TAG, line);
}
} catch (Exception e) {
Log.e(TAG, "Couldn't read file", e);
fail("Exception " + e);
}
}
/**
* For debugging, dump the main state file on logcat.
*/
private void dumpBaseStateFile() {
mService.saveDirtyInfo();
dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+ "/system/" + ShortcutService.FILENAME_BASE_STATE);
}
/**
* For debugging, dump per-user state file on logcat.
*/
private void dumpUserFile(int userId) {
mService.saveDirtyInfo();
dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+ "/user-" + userId
+ "/" + ShortcutService.FILENAME_USER_PACKAGES);
}
private void waitOnMainThread() throws Throwable {
runTestOnUiThread(() -> {});
}
private static Bundle makeBundle(Object... keysAndValues) {
Preconditions.checkState((keysAndValues.length % 2) == 0);
if (keysAndValues.length == 0) {
return null;
}
final Bundle ret = new Bundle();
for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
final String key = keysAndValues[i].toString();
final Object value = keysAndValues[i + 1];
if (value == null) {
ret.putString(key, null);
} else if (value instanceof Integer) {
ret.putInt(key, (Integer) value);
} else if (value instanceof String) {
ret.putString(key, (String) value);
} else if (value instanceof Bundle) {
ret.putBundle(key, (Bundle) value);
} else {
fail("Type not supported yet: " + value.getClass().getName());
}
}
return ret;
}
/**
* Make a shortcut with an ID.
*/
private ShortcutInfo makeShortcut(String id) {
return makeShortcut(
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
}
/**
* Make a shortcut with an ID and timestamp.
*/
private ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) {
final ShortcutInfo s = makeShortcut(
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
s.setTimestamp(timestamp);
return s;
}
/**
* Make a shortcut with an ID and icon.
*/
private ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
return makeShortcut(
id, "Title-" + id, /* activity =*/ null, icon,
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
}
private ShortcutInfo makePackageShortcut(String packageName, String id) {
String origCaller = getCallingPackage();
setCaller(packageName);
ShortcutInfo s = makeShortcut(
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
setCaller(origCaller); // restore the caller
return s;
}
/**
* Make multiple shortcuts with IDs.
*/
private List<ShortcutInfo> makeShortcuts(String... ids) {
final ArrayList<ShortcutInfo> ret = new ArrayList();
for (String id : ids) {
ret.add(makeShortcut(id));
}
return ret;
}
private ShortcutInfo.Builder makeShortcutBuilder() {
return new ShortcutInfo.Builder(mClientContext);
}
/**
* Make a shortcut with details.
*/
private ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
Icon icon, Intent intent, int weight) {
final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext)
.setId(id)
.setTitle(title)
.setWeight(weight)
.setIntent(intent);
if (icon != null) {
b.setIcon(icon);
}
if (activity != null) {
b.setActivityComponent(activity);
}
final ShortcutInfo s = b.build();
s.setTimestamp(mInjectedCurrentTimeLillis); // HACK
return s;
}
/**
* Make an intent.
*/
private Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) {
final Intent intent = new Intent(action);
intent.setComponent(makeComponent(clazz));
intent.replaceExtras(makeBundle(bundleKeysAndValues));
return intent;
}
/**
* Make an component name, with the client context.
*/
@NonNull
private ComponentName makeComponent(Class<?> clazz) {
return new ComponentName(mClientContext, clazz);
}
@NonNull
private ShortcutInfo findById(List<ShortcutInfo> list, String id) {
for (ShortcutInfo s : list) {
if (s.getId().equals(id)) {
return s;
}
}
fail("Shortcut with id " + id + " not found");
return null;
}
private void assertResetTimes(long expectedLastResetTime, long expectedNextResetTime) {
assertEquals(expectedLastResetTime, mService.getLastResetTimeLocked());
assertEquals(expectedNextResetTime, mService.getNextResetTimeLocked());
}
@NonNull
private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
String... expectedIds) {
assertEquals(expectedIds.length, actualShortcuts.size());
final HashSet<String> expected = new HashSet<>(Arrays.asList(expectedIds));
final HashSet<String> actual = new HashSet<>();
for (ShortcutInfo s : actualShortcuts) {
actual.add(s.getId());
}
// Compare the sets.
assertEquals(expected, actual);
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllHaveIntents(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertNotNull("ID " + s.getId(), s.getIntent());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllNotHaveIntents(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertNull("ID " + s.getId(), s.getIntent());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllHaveTitle(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertNotNull("ID " + s.getId(), s.getTitle());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllNotHaveTitle(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertNull("ID " + s.getId(), s.getTitle());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllNotHaveIcon(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertNull("ID " + s.getId(), s.getIcon());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllHaveIconResId(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllHaveIconFile(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllHaveIcon(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertTrue("ID " + s.getId(), s.hasIconFile() || s.hasIconResource());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllHaveFlags(@NonNull List<ShortcutInfo> actualShortcuts,
int shortcutFlags) {
for (ShortcutInfo s : actualShortcuts) {
assertTrue("ID " + s.getId(), s.hasFlags(shortcutFlags));
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllKeyFieldsOnly(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllNotKeyFieldsOnly(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
}
return actualShortcuts;
}
@NonNull
private List<ShortcutInfo> assertAllDynamic(@NonNull List<ShortcutInfo> actualShortcuts) {
return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_DYNAMIC);
}
@NonNull
private List<ShortcutInfo> assertAllPinned(@NonNull List<ShortcutInfo> actualShortcuts) {
return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_PINNED);
}
@NonNull
private List<ShortcutInfo> assertAllDynamicOrPinned(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
}
return actualShortcuts;
}
private void assertBitmapSize(int expectedWidth, int expectedHeight, @NonNull Bitmap bitmap) {
assertEquals("width", expectedWidth, bitmap.getWidth());
assertEquals("height", expectedHeight, bitmap.getHeight());
}
private <T> void assertAllUnique(Collection<T> list) {
final Set<Object> set = new HashSet<>();
for (T item : list) {
if (set.contains(item)) {
fail("Duplicate item found: " + item + " (in the list: " + list + ")");
}
set.add(item);
}
}
@NonNull
private Bitmap pfdToBitmap(@NonNull ParcelFileDescriptor pfd) {
Preconditions.checkNotNull(pfd);
try {
return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
} finally {
IoUtils.closeQuietly(pfd);
}
}
private void assertBundleEmpty(BaseBundle b) {
assertTrue(b == null || b.size() == 0);
}
private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) {
return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
}
private ShortcutInfo getPackageShortcut(String packageName, String shortcutId) {
return getPackageShortcut(packageName, shortcutId, getCallingUserId());
}
private ShortcutInfo getCallerShortcut(String shortcutId) {
return getPackageShortcut(getCallingPackage(), shortcutId, getCallingUserId());
}
/**
* Test for the first launch path, no settings file available.
*/
public void testFirstInitialize() {
assertResetTimes(START_TIME, START_TIME + INTERVAL);
}
/**
* Test for {@link ShortcutService#updateTimes()}
*/
public void testUpdateAndGetNextResetTimeLocked() {
assertResetTimes(START_TIME, START_TIME + INTERVAL);
// Advance clock.
mInjectedCurrentTimeLillis += 100;
// Shouldn't have changed.
assertResetTimes(START_TIME, START_TIME + INTERVAL);
// Advance clock, almost the reset time.
mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
// Shouldn't have changed.
assertResetTimes(START_TIME, START_TIME + INTERVAL);
// Advance clock.
mInjectedCurrentTimeLillis += 1;
assertResetTimes(START_TIME + INTERVAL, START_TIME + 2 * INTERVAL);
// Advance further; 4 days since start.
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
}
/**
* Test for the restoration from saved file.
*/
public void testInitializeFromSavedFile() {
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
mService.saveBaseStateLocked();
dumpBaseStateFile();
mService.saveDirtyInfo();
// Restore.
initService();
assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
}
/**
* Test for the restoration from restored file.
*/
public void testLoadFromBrokenFile() {
// TODO Add various broken cases.
}
public void testLoadConfig() {
mService.updateConfigurationLocked(
ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
+ ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
+ ConfigConstants.KEY_MAX_DAILY_UPDATES + "=5,"
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ ConfigConstants.KEY_ICON_FORMAT + "=WEBP,"
+ ConfigConstants.KEY_ICON_QUALITY + "=75");
assertEquals(123000, mService.getResetIntervalForTest());
assertEquals(4, mService.getMaxDynamicShortcutsForTest());
assertEquals(5, mService.getMaxDailyUpdatesForTest());
assertEquals(100, mService.getMaxIconDimensionForTest());
assertEquals(CompressFormat.WEBP, mService.getIconPersistFormatForTest());
assertEquals(75, mService.getIconPersistQualityForTest());
mInjectdIsLowRamDevice = true;
mService.updateConfigurationLocked(
ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ ConfigConstants.KEY_ICON_FORMAT + "=JPEG");
assertEquals(ShortcutService.DEFAULT_RESET_INTERVAL_SEC * 1000,
mService.getResetIntervalForTest());
assertEquals(ShortcutService.DEFAULT_MAX_SHORTCUTS_PER_APP,
mService.getMaxDynamicShortcutsForTest());
assertEquals(ShortcutService.DEFAULT_MAX_DAILY_UPDATES,
mService.getMaxDailyUpdatesForTest());
assertEquals(50, mService.getMaxIconDimensionForTest());
assertEquals(CompressFormat.JPEG, mService.getIconPersistFormatForTest());
assertEquals(ShortcutService.DEFAULT_ICON_PERSIST_QUALITY,
mService.getIconPersistQualityForTest());
}
// === Test for app side APIs ===
/** Test for {@link android.content.pm.ShortcutManager#getMaxDynamicShortcutCount()} */
public void testGetMaxDynamicShortcutCount() {
assertEquals(MAX_SHORTCUTS, mManager.getMaxDynamicShortcutCount());
}
/** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
public void testGetRemainingCallCount() {
assertEquals(MAX_DAILY_UPDATES, mManager.getRemainingCallCount());
}
/** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
public void testGetRateLimitResetTime() {
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
}
public void testSetDynamicShortcuts() {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.icon2));
final ShortcutInfo si1 = makeShortcut(
"shortcut1",
"Title 1",
makeComponent(ShortcutActivity.class),
icon1,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
final ShortcutInfo si2 = makeShortcut(
"shortcut2",
"Title 2",
/* activity */ null,
icon2,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
final ShortcutInfo si3 = makeShortcut("shortcut3");
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2");
assertEquals(2, mManager.getRemainingCallCount());
// TODO: Check fields
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1");
assertEquals(1, mManager.getRemainingCallCount());
assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
assertEquals(0, mManager.getDynamicShortcuts().size());
assertEquals(0, mManager.getRemainingCallCount());
dumpsysOnLogcat();
mInjectedCurrentTimeLillis++; // Need to advance the clock for reset to work.
mService.resetThrottlingInner(UserHandle.USER_SYSTEM);
dumpsysOnLogcat();
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2, si3)));
assertEquals(2, mManager.getDynamicShortcuts().size());
// TODO Check max number
}
public void testAddDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
assertEquals(3, mManager.getRemainingCallCount());
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1");
assertTrue(mManager.addDynamicShortcut(si2));
assertEquals(1, mManager.getRemainingCallCount());
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2");
// Add with the same ID
assertTrue(mManager.addDynamicShortcut(makeShortcut("shortcut1")));
assertEquals(0, mManager.getRemainingCallCount());
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2");
// TODO Check max number
// TODO Check fields.
}
public void testDeleteDynamicShortcut() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2", "shortcut3");
assertEquals(2, mManager.getRemainingCallCount());
mManager.deleteDynamicShortcut("shortcut1");
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut2", "shortcut3");
mManager.deleteDynamicShortcut("shortcut1");
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut2", "shortcut3");
mManager.deleteDynamicShortcut("shortcutXXX");
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut2", "shortcut3");
mManager.deleteDynamicShortcut("shortcut2");
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut3");
mManager.deleteDynamicShortcut("shortcut3");
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()));
// Still 2 calls left.
assertEquals(2, mManager.getRemainingCallCount());
// TODO Make sure pinned shortcuts won't be deleted.
}
public void testDeleteAllDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2", "shortcut3");
assertEquals(2, mManager.getRemainingCallCount());
mManager.deleteAllDynamicShortcuts();
assertEquals(0, mManager.getDynamicShortcuts().size());
assertEquals(2, mManager.getRemainingCallCount());
// Note delete shouldn't affect throttling, so...
assertEquals(0, mManager.getDynamicShortcuts().size());
assertEquals(0, mManager.getDynamicShortcuts().size());
assertEquals(0, mManager.getDynamicShortcuts().size());
// This should still work.
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
assertEquals(3, mManager.getDynamicShortcuts().size());
// Still 1 call left
assertEquals(1, mManager.getRemainingCallCount());
// TODO Make sure pinned shortcuts won't be deleted.
}
public void testThrottling() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Reached the max
mInjectedCurrentTimeLillis++;
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Still throttled
mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Now it should work.
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); // fail
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
// 4 days later...
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
// Make sure getRemainingCallCount() itself gets reset without calling setDynamicShortcuts().
mInjectedCurrentTimeLillis = START_TIME + 8 * INTERVAL;
assertEquals(3, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
}
public void testThrottling_rewind() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis = 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.
mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Now rewind -- this will reset the counters.
mInjectedCurrentTimeLillis = START_TIME - 100000;
assertEquals(3, mManager.getRemainingCallCount());
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(2, mManager.getRemainingCallCount());
// Forward again, should be reset now.
mInjectedCurrentTimeLillis += INTERVAL;
assertEquals(3, mManager.getRemainingCallCount());
}
public void testThrottling_perPackage() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(2, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(1, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(0, mManager.getRemainingCallCount());
// Reached the max
mInjectedCurrentTimeLillis++;
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(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(Arrays.asList(si2)));
assertEquals(2, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
assertEquals(1, mManager.getRemainingCallCount());
// Back to the original caller, still throttled.
mInjectedClientPackage = CALLING_PACKAGE_1;
mInjectedCallingUid = CALLING_UID_1;
mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
assertEquals(0, mManager.getRemainingCallCount());
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertEquals(0, mManager.getRemainingCallCount());
// Now it should work.
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
mInjectedCurrentTimeLillis++;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
mInjectedCurrentTimeLillis++;
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
mInjectedClientPackage = CALLING_PACKAGE_2;
mInjectedCallingUid = CALLING_UID_2;
assertEquals(3, mManager.getRemainingCallCount());
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si2)));
}
public void testIcons() {
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512);
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));
final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_512x512));
// Set from package 1
setCaller(CALLING_PACKAGE_1);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcutWithIcon("res32x32", res32x32),
makeShortcutWithIcon("res64x64", res64x64),
makeShortcutWithIcon("bmp32x32", bmp32x32),
makeShortcutWithIcon("bmp64x64", bmp64x64),
makeShortcutWithIcon("bmp512x512", bmp512x512),
makeShortcut("none")
)));
// getDynamicShortcuts() shouldn't return icons, thus assertAllNotHaveIcon().
assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
"res32x32",
"res64x64",
"bmp32x32",
"bmp64x64",
"bmp512x512",
"none");
// Call from another caller with the same ID, just to make sure storage is per-package.
setCaller(CALLING_PACKAGE_2);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcutWithIcon("res32x32", res512x512),
makeShortcutWithIcon("res64x64", res512x512),
makeShortcutWithIcon("none", res512x512)
)));
assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
"res32x32",
"res64x64",
"none");
// Re-initialize and load from the files.
mService.saveDirtyInfo();
initService();
// Load from launcher.
Bitmap bmp;
setCaller(LAUNCHER_1);
// Check hasIconResource()/hasIconFile().
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, Arrays.asList("res32x32"),
getCallingUser())), "res32x32");
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, Arrays.asList("res64x64"), getCallingUser())),
"res64x64");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, Arrays.asList("bmp32x32"), getCallingUser())),
"bmp32x32");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, Arrays.asList("bmp64x64"), getCallingUser())),
"bmp64x64");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, Arrays.asList("bmp512x512"), getCallingUser())),
"bmp512x512");
// Check
assertEquals(
R.drawable.black_32x32,
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUser()));
assertEquals(
R.drawable.black_64x64,
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUser()));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
assertBitmapSize(32, 32, bmp);
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
assertBitmapSize(64, 64, bmp);
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
assertBitmapSize(128, 128, bmp);
// TODO Test the content URI case too.
}
private void checkShrinkBitmap(int expectedWidth, int expectedHeight, int resId, int maxSize) {
assertBitmapSize(expectedWidth, expectedHeight,
ShortcutService.shrinkBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), resId),
maxSize));
}
public void testShrinkBitmap() {
checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
checkShrinkBitmap(1024, 4096, R.drawable.black_1024x4096, 4096);
checkShrinkBitmap(1024, 4096, R.drawable.black_1024x4096, 4100);
checkShrinkBitmap(512, 2048, R.drawable.black_1024x4096, 2048);
checkShrinkBitmap(4096, 1024, R.drawable.black_4096x1024, 4096);
checkShrinkBitmap(4096, 1024, R.drawable.black_4096x1024, 4100);
checkShrinkBitmap(2048, 512, R.drawable.black_4096x1024, 2048);
}
private File openIconFileForWriteAndGetPath(int userId, String packageName)
throws IOException {
// Shortcut IDs aren't used in the path, so just pass the same ID.
final FileOutputStreamWithPath out =
mService.openIconFileForWrite(userId, makePackageShortcut(packageName, "id"));
out.close();
return out.getFile();
}
public void testOpenIconFileForWrite() throws IOException {
mInjectedCurrentTimeLillis = 1000;
final File p10_1_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
final File p10_1_2 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
final File p10_2_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
final File p10_2_2 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
final File p11_1_1 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
final File p11_1_2 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
mInjectedCurrentTimeLillis++;
final File p10_1_3 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
final File p10_1_4 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
final File p10_1_5 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
final File p10_2_3 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
final File p11_1_3 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
// Make sure their paths are all unique
assertAllUnique(Arrays.asList(
p10_1_1,
p10_1_2,
p10_1_3,
p10_1_4,
p10_1_5,
p10_2_1,
p10_2_2,
p10_2_3,
p11_1_1,
p11_1_2,
p11_1_3
));
// Check each set has the same parent.
assertEquals(p10_1_1.getParent(), p10_1_2.getParent());
assertEquals(p10_1_1.getParent(), p10_1_3.getParent());
assertEquals(p10_1_1.getParent(), p10_1_4.getParent());
assertEquals(p10_1_1.getParent(), p10_1_5.getParent());
assertEquals(p10_2_1.getParent(), p10_2_2.getParent());
assertEquals(p10_2_1.getParent(), p10_2_3.getParent());
assertEquals(p11_1_1.getParent(), p11_1_2.getParent());
assertEquals(p11_1_1.getParent(), p11_1_3.getParent());
// Check the parents are still unique.
assertAllUnique(Arrays.asList(
p10_1_1.getParent(),
p10_2_1.getParent(),
p11_1_1.getParent()
));
// All files created at the same time for the same package/user, expcet for the first ones,
// will have "_" in the path.
assertFalse(p10_1_1.getName().contains("_"));
assertTrue(p10_1_2.getName().contains("_"));
assertFalse(p10_1_3.getName().contains("_"));
assertTrue(p10_1_4.getName().contains("_"));
assertTrue(p10_1_5.getName().contains("_"));
assertFalse(p10_2_1.getName().contains("_"));
assertTrue(p10_2_2.getName().contains("_"));
assertFalse(p10_2_3.getName().contains("_"));
assertFalse(p11_1_1.getName().contains("_"));
assertTrue(p11_1_2.getName().contains("_"));
assertFalse(p11_1_3.getName().contains("_"));
}
public void testUpdateShortcuts() {
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcut("s1"),
makeShortcut("s2"),
makeShortcut("s3"),
makeShortcut("s4"),
makeShortcut("s5")
)));
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcut("s1"),
makeShortcut("s2"),
makeShortcut("s3"),
makeShortcut("s4"),
makeShortcut("s5")
)));
});
runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s2", "s3"),
getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s4", "s5"),
getCallingUser());
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
mManager.deleteDynamicShortcut("s1");
mManager.deleteDynamicShortcut("s2");
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
mManager.deleteDynamicShortcut("s1");
mManager.deleteDynamicShortcut("s3");
mManager.deleteDynamicShortcut("s5");
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertShortcutIds(assertAllDynamic(
mManager.getDynamicShortcuts()),
"s3", "s4", "s5");
assertShortcutIds(assertAllPinned(
mManager.getPinnedShortcuts()),
"s2", "s3");
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
assertShortcutIds(assertAllDynamic(
mManager.getDynamicShortcuts()),
"s2", "s4");
assertShortcutIds(assertAllPinned(
mManager.getPinnedShortcuts()),
"s4", "s5");
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
ShortcutInfo s2 = makeShortcutBuilder()
.setId("s2")
.setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
.build();
ShortcutInfo s4 = makeShortcutBuilder()
.setId("s4")
.setTitle("new title")
.build();
mManager.updateShortcuts(Arrays.asList(s2, s4));
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
ShortcutInfo s2 = makeShortcutBuilder()
.setId("s2")
.setIntent(makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
"key1", "val1"))
.build();
ShortcutInfo s4 = makeShortcutBuilder()
.setId("s4")
.setIntent(new Intent(Intent.ACTION_ALL_APPS))
.build();
mManager.updateShortcuts(Arrays.asList(s2, s4));
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertShortcutIds(assertAllDynamic(
mManager.getDynamicShortcuts()),
"s3", "s4", "s5");
assertShortcutIds(assertAllPinned(
mManager.getPinnedShortcuts()),
"s2", "s3");
ShortcutInfo s = getCallerShortcut("s2");
assertTrue(s.hasIconResource());
assertEquals(R.drawable.black_32x32, s.getIconResourceId());
assertEquals("Title-s2", s.getTitle());
s = getCallerShortcut("s4");
assertFalse(s.hasIconResource());
assertEquals(0, s.getIconResourceId());
assertEquals("new title", s.getTitle());
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
assertShortcutIds(assertAllDynamic(
mManager.getDynamicShortcuts()),
"s2", "s4");
assertShortcutIds(assertAllPinned(
mManager.getPinnedShortcuts()),
"s4", "s5");
ShortcutInfo s = getCallerShortcut("s2");
assertFalse(s.hasIconResource());
assertEquals(0, s.getIconResourceId());
assertEquals("Title-s2", s.getTitle());
assertEquals(Intent.ACTION_ANSWER, s.getIntent().getAction());
assertEquals(1, s.getIntent().getExtras().size());
s = getCallerShortcut("s4");
assertFalse(s.hasIconResource());
assertEquals(0, s.getIconResourceId());
assertEquals("Title-s4", s.getTitle());
assertEquals(Intent.ACTION_ALL_APPS, s.getIntent().getAction());
assertBundleEmpty(s.getIntent().getExtras());
});
// TODO Check with other fields too.
// TODO Check bitmap removal too.
}
// TODO: updateShortcuts()
// TODO: getPinnedShortcuts()
// === Test for launcher side APIs ===
private static ShortcutQuery buildQuery(long changedSince,
String packageName, ComponentName componentName,
/* @ShortcutQuery.QueryFlags */ int flags) {
final ShortcutQuery q = new ShortcutQuery();
q.setChangedSince(changedSince);
q.setPackage(packageName);
q.setActivity(componentName);
q.setQueryFlags(flags);
return q;
}
public void testGetShortcuts() {
// Set up shortcuts.
setCaller(CALLING_PACKAGE_1);
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 5000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 1000);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
setCaller(CALLING_PACKAGE_2);
final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
setCaller(CALLING_PACKAGE_3);
final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", 5000);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
setCaller(LAUNCHER_1);
// Get dynamic
assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s1", "s2"))));
// Get pinned
assertShortcutIds(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null,
ShortcutQuery.FLAG_GET_PINNED), getCallingUser())
/* none */);
// Get both, with timestamp
assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllNotKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2,
/* activity =*/ null,
ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC),
getCallingUser())),
"s2", "s3"))));
// FLAG_GET_KEY_FIELDS_ONLY
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2,
/* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser())),
"s2", "s3"))));
// Pin some shortcuts.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
Arrays.asList("s3", "s4"), getCallingUser());
// Pinned ones only
assertAllPinned(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllNotKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2,
/* activity =*/ null,
ShortcutQuery.FLAG_GET_PINNED),
getCallingUser())),
"s3"))));
// All packages.
assertShortcutIds(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 5000, /* package= */ null,
/* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED),
getCallingUser())),
"s1", "s3");
// TODO More tests: pinned but dynamic, filter by activity
}
public void testGetShortcutInfo() {
// Create shortcuts.
setCaller(CALLING_PACKAGE_1);
final ShortcutInfo s1_1 = makeShortcut(
"s1",
"Title 1",
makeComponent(ShortcutActivity.class),
/* icon =*/ null,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
final ShortcutInfo s1_2 = makeShortcut(
"s2",
"Title 2",
/* activity */ null,
/* icon =*/ null,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
dumpsysOnLogcat();
setCaller(CALLING_PACKAGE_2);
final ShortcutInfo s2_1 = makeShortcut(
"s1",
"ABC",
makeComponent(ShortcutActivity2.class),
/* icon =*/ null,
makeIntent(Intent.ACTION_ANSWER, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_1)));
dumpsysOnLogcat();
// Pin some.
setCaller(LAUNCHER_1);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
Arrays.asList("s2"), getCallingUser());
dumpsysOnLogcat();
// Delete some.
setCaller(CALLING_PACKAGE_1);
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
mManager.deleteDynamicShortcut("s2");
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
dumpsysOnLogcat();
setCaller(LAUNCHER_1);
List<ShortcutInfo> list;
// Note we don't guarantee the orders.
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
Arrays.asList("s2", "s1", "s3", null), getCallingUser())))),
"s1", "s2");
assertEquals("Title 1", findById(list, "s1").getTitle());
assertEquals("Title 2", findById(list, "s2").getTitle());
assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
Arrays.asList("s3"), getCallingUser())))
/* none */);
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_2,
Arrays.asList("s1", "s2", "s3"), getCallingUser()))),
"s1");
assertEquals("ABC", findById(list, "s1").getTitle());
}
public void testPinShortcutAndGetPinnedShortcuts() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
});
runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
});
// Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
Arrays.asList("s2", "s3"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
Arrays.asList("s3", "s4", "s5"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
Arrays.asList("s3"), getCallingUser()); // Note ID doesn't exist
});
// Delete some.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
mManager.deleteDynamicShortcut("s2");
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
mManager.deleteDynamicShortcut("s3");
assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
});
runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
mManager.deleteDynamicShortcut("s2");
assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
});
// Get pinned shortcuts from launcher
runWithCaller(LAUNCHER_1, USER_0, () -> {
// CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s2");
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s3", "s4");
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
/* none */);
});
}
public void testPinShortcutAndGetPinnedShortcuts_multi() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
dumpsysOnLogcat();
// Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
Arrays.asList("s3", "s4"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
Arrays.asList("s1", "s2", "s4"), getCallingUser());
});
dumpsysOnLogcat();
// Delete some.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
mManager.deleteDynamicShortcut("s3");
assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
});
dumpsysOnLogcat();
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
mManager.deleteDynamicShortcut("s1");
mManager.deleteDynamicShortcut("s3");
assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
});
dumpsysOnLogcat();
// Get pinned shortcuts from launcher
runWithCaller(LAUNCHER_1, USER_0, () -> {
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s3");
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s1", "s2");
assertShortcutIds(assertAllDynamicOrPinned(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s1", "s2", "s3");
assertShortcutIds(assertAllDynamicOrPinned(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s1", "s2");
});
dumpsysOnLogcat();
runWithCaller(LAUNCHER_2, USER_0, () -> {
// Launcher2 still has no pinned ones.
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
/* none */);
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
/* none */);
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s1", "s2");
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s2");
// Now pin some.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
Arrays.asList("s1", "s2"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
Arrays.asList("s1", "s2"), getCallingUser());
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s1", "s2");
// S1 was not visible to it, so shouldn't be pinned.
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s2");
});
// Re-initialize and load from the files.
mService.saveDirtyInfo();
initService();
runWithCaller(LAUNCHER_1, USER_0, () -> {
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s3");
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s1", "s2");
});
runWithCaller(LAUNCHER_2, USER_0, () -> {
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s1", "s2");
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s2");
});
// Delete all dynamic.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
mManager.deleteAllDynamicShortcuts();
assertEquals(0, mManager.getDynamicShortcuts().size());
assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
mManager.deleteAllDynamicShortcuts();
assertEquals(0, mManager.getDynamicShortcuts().size());
assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s2", "s1");
});
runWithCaller(LAUNCHER_1, USER_0, () -> {
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s3");
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s1", "s2");
// from all packages.
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, null,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s1", "s2", "s3");
// Update pined. Note s2 and s3 are actually available, but not visible to this
// launcher, so still can't be pinned.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s1", "s2", "s3", "s4"),
getCallingUser());
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s3");
});
// Re-publish s1.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
});
runWithCaller(LAUNCHER_1, USER_0, () -> {
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s3");
// Now "s1" is visible, so can be pinned.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s1", "s2", "s3", "s4"),
getCallingUser());
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s1", "s3");
});
// Now clear pinned shortcuts. First, from launcher 1.
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList(), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList(), getCallingUser());
assertEquals(0,
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
assertEquals(0,
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
});
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2");
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertEquals(0, mManager.getDynamicShortcuts().size());
assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s2");
});
// Clear all pins from launcher 2.
runWithCaller(LAUNCHER_2, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList(), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList(), getCallingUser());
assertEquals(0,
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
assertEquals(0,
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
});
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
assertEquals(0, mManager.getPinnedShortcuts().size());
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertEquals(0, mManager.getDynamicShortcuts().size());
assertEquals(0, mManager.getPinnedShortcuts().size());
});
}
public void testCreateShortcutIntent() {
// Create some shortcuts.
setCaller(CALLING_PACKAGE_1);
final ShortcutInfo s1_1 = makeShortcut(
"s1",
"Title 1",
makeComponent(ShortcutActivity.class),
/* icon =*/ null,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
final ShortcutInfo s1_2 = makeShortcut(
"s2",
"Title 2",
/* activity */ null,
/* icon =*/ null,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
setCaller(CALLING_PACKAGE_2);
final ShortcutInfo s2_1 = makeShortcut(
"s1",
"ABC",
makeComponent(ShortcutActivity.class),
/* icon =*/ null,
makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_1)));
// Pin all.
setCaller(LAUNCHER_1);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
Arrays.asList("s1", "s2"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
Arrays.asList("s1"), getCallingUser());
// Just to make it complicated, delete some.
setCaller(CALLING_PACKAGE_1);
mManager.deleteDynamicShortcut("s2");
// intent and check.
setCaller(LAUNCHER_1);
Intent intent;
intent = mInternal.createShortcutIntent(getCallingPackage(),
CALLING_PACKAGE_1, "s1", getCallingUserId());
assertEquals(ShortcutActivity2.class.getName(), intent.getComponent().getClassName());
intent = mInternal.createShortcutIntent(getCallingPackage(),
CALLING_PACKAGE_1, "s2", getCallingUserId());
assertEquals(ShortcutActivity3.class.getName(), intent.getComponent().getClassName());
intent = mInternal.createShortcutIntent(getCallingPackage(),
CALLING_PACKAGE_2, "s1", getCallingUserId());
assertEquals(ShortcutActivity.class.getName(), intent.getComponent().getClassName());
// TODO Check extra, etc
}
public void testLauncherCallback() throws Throwable {
// TODO Add "multi" version -- run the test with two launchers and make sure the callback
// argument only contains the ones that are actually visible to each launcher.
LauncherApps.Callback c0 = mock(LauncherApps.Callback.class);
// Set listeners
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.registerCallback(c0, new Handler(Looper.getMainLooper()));
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
waitOnMainThread();
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
eq(UserHandle.of(USER_0))
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3");
// From different package.
reset(c0);
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
waitOnMainThread();
shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_2),
shortcuts.capture(),
eq(UserHandle.of(USER_0))
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3");
// Different user, callback shouldn't be called.
reset(c0);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
waitOnMainThread();
verify(c0, times(0)).onShortcutsChanged(
anyString(),
any(List.class),
any(UserHandle.class)
);
// Test for addDynamicShortcut.
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.addDynamicShortcut(makeShortcut("s4")));
});
waitOnMainThread();
shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
eq(UserHandle.of(USER_0))
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3", "s4");
// Test for remove
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
mManager.deleteDynamicShortcut("s1");
});
waitOnMainThread();
shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
eq(UserHandle.of(USER_0))
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s2", "s3", "s4");
// Test for update
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.updateShortcuts(Arrays.asList(
makeShortcut("s1"), makeShortcut("s2"))));
});
waitOnMainThread();
shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
eq(UserHandle.of(USER_0))
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s2", "s3", "s4");
// Test for deleteAll
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
mManager.deleteAllDynamicShortcuts();
});
waitOnMainThread();
shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
eq(UserHandle.of(USER_0))
);
assertEquals(0, shortcuts.getValue().size());
}
// === Test for persisting ===
public void testSaveAndLoadUser_empty() {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
Log.i(TAG, "Saved state");
dumpsysOnLogcat();
dumpUserFile(0);
// Restore.
mService.saveDirtyInfo();
initService();
assertEquals(0, mManager.getDynamicShortcuts().size());
}
/**
* Try save and load, also stop/start the user.
*/
public void testSaveAndLoadUser() {
// First, create some shortcuts and save.
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.icon2));
final ShortcutInfo si1 = makeShortcut(
"s1",
"title1-1",
makeComponent(ShortcutActivity.class),
icon1,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
final ShortcutInfo si2 = makeShortcut(
"s2",
"title1-2",
/* activity */ null,
icon2,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_16x64);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.icon2));
final ShortcutInfo si1 = makeShortcut(
"s1",
"title2-1",
makeComponent(ShortcutActivity.class),
icon1,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
final ShortcutInfo si2 = makeShortcut(
"s2",
"title2-2",
/* activity */ null,
icon2,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
});
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.icon2));
final ShortcutInfo si1 = makeShortcut(
"s1",
"title10-1-1",
makeComponent(ShortcutActivity.class),
icon1,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
final ShortcutInfo si2 = makeShortcut(
"s2",
"title10-1-2",
/* activity */ null,
icon2,
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
});
mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM).setLauncherComponent(
mService, new ComponentName("pkg1", "class"));
// Restore.
mService.saveDirtyInfo();
initService();
// Before the load, the map should be empty.
assertEquals(0, mService.getShortcutsForTest().size());
// this will pre-load the per-user info.
mService.onStartUserLocked(UserHandle.USER_SYSTEM);
// Now it's loaded.
assertEquals(1, mService.getShortcutsForTest().size());
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
mManager.getDynamicShortcuts()))), "s1", "s2");
assertEquals(2, mManager.getRemainingCallCount());
assertEquals("title1-1", getCallerShortcut("s1").getTitle());
assertEquals("title1-2", getCallerShortcut("s2").getTitle());
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
mManager.getDynamicShortcuts()))), "s1", "s2");
assertEquals(2, mManager.getRemainingCallCount());
assertEquals("title2-1", getCallerShortcut("s1").getTitle());
assertEquals("title2-2", getCallerShortcut("s2").getTitle());
});
assertEquals("pkg1", mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM)
.getLauncherComponent().getPackageName());
// Start another user
mService.onStartUserLocked(USER_10);
// Now the size is 2.
assertEquals(2, mService.getShortcutsForTest().size());
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
mManager.getDynamicShortcuts()))), "s1", "s2");
assertEquals(2, mManager.getRemainingCallCount());
assertEquals("title10-1-1", getCallerShortcut("s1").getTitle());
assertEquals("title10-1-2", getCallerShortcut("s2").getTitle());
});
assertNull(mService.getShortcutsForTest().get(USER_10).getLauncherComponent());
// Try stopping the user
mService.onCleanupUserLocked(USER_10);
// Now it's unloaded.
assertEquals(1, mService.getShortcutsForTest().size());
// TODO Check all other fields
}
// TODO Detailed test for hasShortcutPermissionInner().
// TODO Add tests for the command line functions too.
}