Merge "Fix launcher side callback" into nyc-dev

This commit is contained in:
Makoto Onuki
2016-03-15 21:02:47 +00:00
committed by Android (Google) Code Review
6 changed files with 340 additions and 95 deletions

View File

@@ -34,7 +34,7 @@ import java.util.List;
* {@hide}
*/
interface ILauncherApps {
void addOnAppsChangedListener(in IOnAppsChangedListener listener);
void addOnAppsChangedListener(String callingPackage, in IOnAppsChangedListener listener);
void removeOnAppsChangedListener(in IOnAppsChangedListener listener);
ParceledListSlice getLauncherActivities(String packageName, in UserHandle user);
ResolveInfo resolveActivity(in Intent intent, in UserHandle user);

View File

@@ -565,7 +565,8 @@ public class LauncherApps {
addCallbackLocked(callback, handler);
if (addedFirstCallback) {
try {
mService.addOnAppsChangedListener(mAppsChangedListener);
mService.addOnAppsChangedListener(mContext.getPackageName(),
mAppsChangedListener);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}

View File

@@ -36,8 +36,7 @@ import java.util.List;
*/
public abstract class ShortcutServiceInternal {
public interface ShortcutChangeListener {
void onShortcutChanged(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @UserIdInt int userId);
void onShortcutChanged(@NonNull String packageName, @UserIdInt int userId);
}
public abstract List<ShortcutInfo>

View File

@@ -28,6 +28,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.ILauncherApps;
import android.content.pm.IOnAppsChangedListener;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -74,9 +75,20 @@ public class LauncherAppsService extends SystemService {
@Override
public void onStart() {
Binder.LOG_RUNTIME_EXCEPTION = true;
publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
}
static class BroadcastCookie {
public final UserHandle user;
public final String packageName;
BroadcastCookie(UserHandle userHandle, String packageName) {
this.user = userHandle;
this.packageName = packageName;
}
}
@VisibleForTesting
static class LauncherAppsImpl extends ILauncherApps.Stub {
private static final boolean DEBUG = false;
@@ -113,7 +125,8 @@ public class LauncherAppsService extends SystemService {
* android.content.pm.IOnAppsChangedListener)
*/
@Override
public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
public void addOnAppsChangedListener(String callingPackage, IOnAppsChangedListener listener)
throws RemoteException {
synchronized (mListeners) {
if (DEBUG) {
Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
@@ -125,7 +138,8 @@ public class LauncherAppsService extends SystemService {
startWatchingPackageBroadcasts();
}
mListeners.unregister(listener);
mListeners.register(listener, Binder.getCallingUserHandle());
mListeners.register(listener, new BroadcastCookie(UserHandle.of(getCallingUserId()),
callingPackage));
}
}
@@ -490,41 +504,44 @@ public class LauncherAppsService extends SystemService {
}
}
private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
/** Checks if user is a profile of or same as listeningUser.
* and the user is enabled. */
private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
String debugMsg) {
if (user.getIdentifier() == listeningUser.getIdentifier()) {
if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
/** Checks if user is a profile of or same as listeningUser.
* and the user is enabled. */
boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
String debugMsg) {
if (user.getIdentifier() == listeningUser.getIdentifier()) {
if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
return true;
}
long ident = Binder.clearCallingIdentity();
try {
UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
if (userInfo == null || listeningUserInfo == null
|| userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
|| userInfo.profileGroupId != listeningUserInfo.profileGroupId
|| !userInfo.isEnabled()) {
if (DEBUG) {
Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
+ debugMsg);
}
return false;
} else {
if (DEBUG) {
Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
+ debugMsg);
}
return true;
}
long ident = Binder.clearCallingIdentity();
try {
UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
if (userInfo == null || listeningUserInfo == null
|| userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
|| userInfo.profileGroupId != listeningUserInfo.profileGroupId
|| !userInfo.isEnabled()) {
if (DEBUG) {
Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
+ debugMsg);
}
return false;
} else {
if (DEBUG) {
Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
+ debugMsg);
}
return true;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
void postToPackageMonitor(Runnable r) {
mPackageMonitor.getRegisteredHandler().post(r);
}
private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
// TODO Simplify with lambdas.
@@ -534,8 +551,8 @@ public class LauncherAppsService extends SystemService {
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onPackageAdded")) continue;
try {
listener.onPackageAdded(user, packageName);
} catch (RemoteException re) {
@@ -553,8 +570,8 @@ public class LauncherAppsService extends SystemService {
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onPackageRemoved")) continue;
try {
listener.onPackageRemoved(user, packageName);
} catch (RemoteException re) {
@@ -572,8 +589,8 @@ public class LauncherAppsService extends SystemService {
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onPackageModified")) continue;
try {
listener.onPackageChanged(user, packageName);
} catch (RemoteException re) {
@@ -591,8 +608,8 @@ public class LauncherAppsService extends SystemService {
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onPackagesAvailable")) continue;
try {
listener.onPackagesAvailable(user, packages, isReplacing());
} catch (RemoteException re) {
@@ -610,8 +627,8 @@ public class LauncherAppsService extends SystemService {
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnavailable")) continue;
try {
listener.onPackagesUnavailable(user, packages, isReplacing());
} catch (RemoteException re) {
@@ -629,8 +646,8 @@ public class LauncherAppsService extends SystemService {
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, listeningUser, "onPackagesSuspended")) continue;
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onPackagesSuspended")) continue;
try {
listener.onPackagesSuspended(user, packages);
} catch (RemoteException re) {
@@ -648,8 +665,8 @@ public class LauncherAppsService extends SystemService {
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnsuspended")) continue;
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnsuspended")) continue;
try {
listener.onPackagesUnsuspended(user, packages);
} catch (RemoteException re) {
@@ -663,20 +680,39 @@ public class LauncherAppsService extends SystemService {
@Override
public void onShortcutChanged(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @UserIdInt int userId) {
@UserIdInt int userId) {
postToPackageMonitor(() -> onShortcutChangedInner(packageName, userId));
}
private void onShortcutChangedInner(@NonNull String packageName,
@UserIdInt int userId) {
final UserHandle user = UserHandle.of(userId);
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, listeningUser, "onShortcutChanged")) continue;
// STOPSHIP Skip if the receiver doesn't have the permission.
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
// Make sure the caller has the permission.
if (!mShortcutServiceInternal.hasShortcutHostPermission(cookie.packageName,
cookie.user.getIdentifier())) {
continue;
}
// Each launcher has a different set of pinned shortcuts, so we need to do a
// query in here.
// (As of now, only one launcher has the permission at a time, so it's bit
// moot, but we may change the permission model eventually.)
final List<ShortcutInfo> list =
mShortcutServiceInternal.getShortcuts(cookie.packageName,
/* changedSince= */ 0, packageName, /* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC
, userId);
try {
listener.onShortcutChanged(user, packageName,
new ParceledListSlice<>(shortcuts));
new ParceledListSlice<>(list));
} catch (RemoteException re) {
Slog.d(TAG, "Callback failed ", re);
}

View File

@@ -814,7 +814,7 @@ public class ShortcutService extends IShortcutService.Stub {
return;
}
final long token = Binder.clearCallingIdentity();
final long token = injectClearCallingIdentity();
try {
// Clear icon info on the shortcut.
shortcut.setIconResourceId(0);
@@ -891,7 +891,7 @@ public class ShortcutService extends IShortcutService.Stub {
shortcut.clearIcon();
}
} finally {
Binder.restoreCallingIdentity(token);
injectRestoreCallingIdentity(token);
}
}
@@ -992,6 +992,10 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
void postToHandler(Runnable r) {
mHandler.post(r);
}
/**
* Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
*/
@@ -1011,17 +1015,16 @@ public class ShortcutService extends IShortcutService.Stub {
}
private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
final ArrayList<ShortcutChangeListener> copy;
final List<ShortcutInfo> shortcuts = new ArrayList<>();
synchronized (mLock) {
copy = new ArrayList<>(mListeners);
getPackageShortcutsLocked(packageName, userId)
.findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
}
for (int i = copy.size() - 1; i >= 0; i--) {
copy.get(i).onShortcutChanged(packageName, shortcuts, userId);
}
postToHandler(() -> {
final ArrayList<ShortcutChangeListener> copy;
synchronized (mLock) {
copy = new ArrayList<>(mListeners);
}
// Note onShortcutChanged() needs to be called with the system service permissions.
for (int i = copy.size() - 1; i >= 0; i--) {
copy.get(i).onShortcutChanged(packageName, userId);
}
});
}
/**
@@ -1799,6 +1802,16 @@ public class ShortcutService extends IShortcutService.Stub {
return UserHandle.getUserId(injectBinderCallingUid());
}
// Injection point.
long injectClearCallingIdentity() {
return Binder.clearCallingIdentity();
}
// Injection point.
void injectRestoreCallingIdentity(long token) {
Binder.restoreCallingIdentity(token);
}
File injectSystemDataPath() {
return Environment.getDataSystemDirectory();
}

View File

@@ -15,15 +15,22 @@
*/
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.when;
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;
@@ -40,10 +47,13 @@ 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.AndroidTestCase;
import android.test.InstrumentationTestCase;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -59,6 +69,7 @@ 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;
@@ -92,7 +103,7 @@ import java.util.Set;
*
*/
@SmallTest
public class ShortcutManagerTest extends AndroidTestCase {
public class ShortcutManagerTest extends InstrumentationTestCase {
private static final String TAG = "ShortcutManagerTest";
/**
@@ -118,7 +129,14 @@ public class ShortcutManagerTest extends AndroidTestCase {
@Override
public Resources getResources() {
return ShortcutManagerTest.this.getContext().getResources();
return getTestContext().getResources();
}
@Override
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
IntentFilter filter, String broadcastPermission, Handler scheduler) {
// ignore.
return null;
}
}
@@ -132,13 +150,24 @@ public class ShortcutManagerTest extends AndroidTestCase {
/** 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 {
public ShortcutServiceTestable(Context context) {
super(context);
final ServiceContext mContext;
public ShortcutServiceTestable(ServiceContext context) {
super(context);
mContext = context;
}
@Override
@@ -153,6 +182,16 @@ public class ShortcutManagerTest extends AndroidTestCase {
+ 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;
@@ -204,6 +243,13 @@ public class ShortcutManagerTest extends AndroidTestCase {
// 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();
r.run();
mContext.injectRestoreCallingIdentity(token);
}
}
/** ShortcutManager with injection override methods. */
@@ -219,12 +265,20 @@ public class ShortcutManagerTest extends AndroidTestCase {
}
private class LauncherAppImplTestable extends LauncherAppsImpl {
public LauncherAppImplTestable(Context context) {
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
}
@@ -232,6 +286,20 @@ public class ShortcutManagerTest extends AndroidTestCase {
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 postToPackageMonitor(Runnable r) {
final long token = mContext.injectClearCallingIdentity();
r.run();
mContext.injectRestoreCallingIdentity(token);
}
}
private class LauncherAppsTestable extends LauncherApps {
@@ -289,6 +357,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
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;
@@ -326,7 +395,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
mInjectedPackageUidMap.put(LAUNCHER_1, LAUNCHER_UID_1);
mInjectedPackageUidMap.put(LAUNCHER_2, LAUNCHER_UID_2);
mInjectedFilePathRoot = new File(getContext().getCacheDir(), "test-files");
mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
// Empty the data directory.
if (mInjectedFilePathRoot.exists()) {
@@ -339,6 +408,10 @@ public class ShortcutManagerTest extends AndroidTestCase {
setCaller(CALLING_PACKAGE_1);
}
private Context getTestContext() {
return getInstrumentation().getContext();
}
/** (Re-) init the manager and the service. */
private void initService() {
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
@@ -442,6 +515,10 @@ public class ShortcutManagerTest extends AndroidTestCase {
+ "/" + ShortcutService.FILENAME_USER_PACKAGES);
}
private void waitOnMainThread() throws Throwable {
runTestOnUiThread(() -> {});
}
private static Bundle makeBundle(Object... keysAndValues) {
Preconditions.checkState((keysAndValues.length % 2) == 0);
@@ -879,9 +956,9 @@ public class ShortcutManagerTest extends AndroidTestCase {
}
public void testSetDynamicShortcuts() {
final Icon icon1 = Icon.createWithResource(mContext, R.drawable.icon1);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
mContext.getResources(), R.drawable.icon2));
getTestContext().getResources(), R.drawable.icon2));
final ShortcutInfo si1 = makeShortcut(
"shortcut1",
@@ -1163,16 +1240,16 @@ public class ShortcutManagerTest extends AndroidTestCase {
}
public void testIcons() {
final Icon res32x32 = Icon.createWithResource(mContext, R.drawable.black_32x32);
final Icon res64x64 = Icon.createWithResource(mContext, R.drawable.black_64x64);
final Icon res512x512 = Icon.createWithResource(mContext, R.drawable.black_512x512);
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(
mContext.getResources(), R.drawable.black_32x32));
getTestContext().getResources(), R.drawable.black_32x32));
final Icon bmp64x64 = Icon.createWithBitmap(BitmapFactory.decodeResource(
mContext.getResources(), R.drawable.black_64x64));
getTestContext().getResources(), R.drawable.black_64x64));
final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource(
mContext.getResources(), R.drawable.black_512x512));
getTestContext().getResources(), R.drawable.black_512x512));
// Set from package 1
setCaller(CALLING_PACKAGE_1);
@@ -1278,7 +1355,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
private void checkShrinkBitmap(int expectedWidth, int expectedHeight, int resId, int maxSize) {
assertBitmapSize(expectedWidth, expectedHeight,
ShortcutService.shrinkBitmap(BitmapFactory.decodeResource(
mContext.getResources(), resId),
getTestContext().getResources(), resId),
maxSize));
}
@@ -1433,7 +1510,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
ShortcutInfo s2 = makeShortcutBuilder()
.setId("s2")
.setIcon(Icon.createWithResource(mContext, R.drawable.black_32x32))
.setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
.build();
ShortcutInfo s4 = makeShortcutBuilder()
@@ -1802,6 +1879,125 @@ public class ShortcutManagerTest extends AndroidTestCase {
// TODO Check extra, etc
}
public void testLauncherCallback() throws Throwable {
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() {
@@ -1823,9 +2019,9 @@ public class ShortcutManagerTest extends AndroidTestCase {
public void testSaveAndLoadUser() {
// First, create some shortcuts and save.
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
final Icon icon1 = Icon.createWithResource(mContext, R.drawable.black_64x16);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
mContext.getResources(), R.drawable.icon2));
getTestContext().getResources(), R.drawable.icon2));
final ShortcutInfo si1 = makeShortcut(
"s1",
@@ -1850,9 +2046,9 @@ public class ShortcutManagerTest extends AndroidTestCase {
assertEquals(2, mManager.getRemainingCallCount());
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
final Icon icon1 = Icon.createWithResource(mContext, R.drawable.black_16x64);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_16x64);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
mContext.getResources(), R.drawable.icon2));
getTestContext().getResources(), R.drawable.icon2));
final ShortcutInfo si1 = makeShortcut(
"s1",
@@ -1877,9 +2073,9 @@ public class ShortcutManagerTest extends AndroidTestCase {
assertEquals(2, mManager.getRemainingCallCount());
});
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final Icon icon1 = Icon.createWithResource(mContext, R.drawable.black_64x64);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
mContext.getResources(), R.drawable.icon2));
getTestContext().getResources(), R.drawable.icon2));
final ShortcutInfo si1 = makeShortcut(
"s1",