Merge "Fix launcher side callback" into nyc-dev
am: ed1b89878b
* commit 'ed1b89878bdf1fa9b5e4b2dfc766383d531d4315':
Fix launcher side callback
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user