Prevent shortcut access when user is locked

Also add some test utility methods that'll be used by CTS.

Bug 30406401

Change-Id: I9396acebbdb3cbe64f6e85877ee62a2131cd6e61
This commit is contained in:
Makoto Onuki
2016-07-26 15:50:50 -07:00
parent 9f3930d3f7
commit 9c850012ca
10 changed files with 258 additions and 34 deletions

View File

@@ -492,6 +492,8 @@ public class LauncherApps {
* If the calling launcher application contains pinned shortcuts, they will still work,
* even though the caller no longer has the shortcut host permission.
*
* <p>Returns {@code false} when the user is locked.
*
* @see ShortcutManager
*/
public boolean hasShortcutHostPermission() {
@@ -508,6 +510,9 @@ public class LauncherApps {
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
* <p>Returns am empty list when the user is locked, or when the {@code user} user
* is locked or not running.
*
* @param query result includes shortcuts matching this query.
* @param user The UserHandle of the profile.
*
@@ -551,6 +556,9 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
* <p>Call will be ignored when the user is locked, or when the {@code user} user
* is locked or not running.
*
* @param packageName The target package name.
* @param shortcutIds The IDs of the shortcut to be pinned.
* @param user The UserHandle of the profile.
@@ -622,6 +630,9 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
* <p>Returns {@code null} when the user is locked, or when the user owning the shortcut
* is locked or not running.
*
* @param density The preferred density of the icon, zero for default density. Use
* density DPI values from {@link DisplayMetrics}.
*
@@ -670,6 +681,9 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
* <p>Returns {@code 0} when the user is locked, or when the user owning the shortcut
* is locked or not running.
*
* @param density Optional density for the icon, or 0 to use the default density. Use
* @return A badged icon for the shortcut.
*
@@ -690,6 +704,10 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
* <p>Throws {@link android.content.ActivityNotFoundException}
* when the user is locked, or when the {@code user} user
* is locked or not running.
*
* @param packageName The target shortcut package name.
* @param shortcutId The target shortcut ID.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
@@ -712,6 +730,10 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
* <p>Throws {@link android.content.ActivityNotFoundException}
* when the user is locked, or when the user owning the shortcut
* is locked or not running.
*
* @param shortcut The target shortcut.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.

View File

@@ -434,6 +434,12 @@ import java.util.List;
* <h3>Launcher API</h3>
*
* The {@link LauncherApps} class provides APIs for launcher applications to access shortcuts.
*
*
* <h3>Direct Boot and Shortcuts</h3>
*
* All shortcut information is stored in credential encrypted storage, so no shortcuts can be
* accessed when the user is locked.
*/
public class ShortcutManager {
private static final String TAG = "ShortcutManager";
@@ -469,6 +475,8 @@ public class ShortcutManager {
*
* @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded,
* or when trying to update immutable shortcuts.
*
* @throws IllegalStateException when the user is locked.
*/
public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
try {
@@ -481,6 +489,8 @@ public class ShortcutManager {
/**
* Return all dynamic shortcuts from the caller application.
*
* @throws IllegalStateException when the user is locked.
*/
@NonNull
public List<ShortcutInfo> getDynamicShortcuts() {
@@ -494,6 +504,8 @@ public class ShortcutManager {
/**
* Return all manifest shortcuts from the caller application.
*
* @throws IllegalStateException when the user is locked.
*/
@NonNull
public List<ShortcutInfo> getManifestShortcuts() {
@@ -515,6 +527,8 @@ public class ShortcutManager {
*
* @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded,
* or when trying to update immutable shortcuts.
*
* @throws IllegalStateException when the user is locked.
*/
public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
try {
@@ -527,6 +541,8 @@ public class ShortcutManager {
/**
* Delete dynamic shortcuts by ID.
*
* @throws IllegalStateException when the user is locked.
*/
public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) {
try {
@@ -539,6 +555,8 @@ public class ShortcutManager {
/**
* Delete all dynamic shortcuts from the caller application.
*
* @throws IllegalStateException when the user is locked.
*/
public void removeAllDynamicShortcuts() {
try {
@@ -550,6 +568,8 @@ public class ShortcutManager {
/**
* Return all pinned shortcuts from the caller application.
*
* @throws IllegalStateException when the user is locked.
*/
@NonNull
public List<ShortcutInfo> getPinnedShortcuts() {
@@ -570,6 +590,8 @@ public class ShortcutManager {
* @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
*
* @throws IllegalArgumentException If trying to update immutable shortcuts.
*
* @throws IllegalStateException when the user is locked.
*/
public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) {
try {
@@ -585,6 +607,8 @@ public class ShortcutManager {
* class.
*
* @throws IllegalArgumentException If trying to disable immutable shortcuts.
*
* @throws IllegalStateException when the user is locked.
*/
public void disableShortcuts(@NonNull List<String> shortcutIds) {
try {
@@ -622,6 +646,8 @@ public class ShortcutManager {
* For more details, see the Javadoc for the {@link ShortcutManager} class.
*
* @throws IllegalArgumentException If trying to disable immutable shortcuts.
*
* @throws IllegalStateException when the user is locked.
*/
public void disableShortcuts(@NonNull List<String> shortcutIds, CharSequence disabledMessage) {
try {
@@ -638,6 +664,8 @@ public class ShortcutManager {
* already enabled, this method does nothing.
*
* @throws IllegalArgumentException If trying to enable immutable shortcuts.
*
* @throws IllegalStateException when the user is locked.
*/
public void enableShortcuts(@NonNull List<String> shortcutIds) {
try {
@@ -704,6 +732,8 @@ public class ShortcutManager {
* Return {@code true} when rate-limiting is active for the caller application.
*
* <p>See the class level javadoc for details.
*
* @throws IllegalStateException when the user is locked.
*/
public boolean isRateLimitingActive() {
try {
@@ -747,6 +777,8 @@ public class ShortcutManager {
* <p>The information is accessible via {@link UsageStatsManager#queryEvents}
* Typically, launcher applications use this information to build a prediction model
* so that they can promote the shortcuts that are likely to be used at the moment.
*
* @throws IllegalStateException when the user is locked.
*/
public void reportShortcutUsed(String shortcutId) {
try {

View File

@@ -259,7 +259,7 @@ public class ShortcutParser {
continue;
}
ShortcutService.warnForInvalidTag(depth, tag);
Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
}
} finally {
if (parser != null) {

View File

@@ -1037,6 +1037,24 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
private boolean isUserUnlocked(@UserIdInt int userId) {
final long token = injectClearCallingIdentity();
try {
// Weird: when SystemService.onUnlockUser() is called, the user state is still
// unlocking, as opposed to unlocked. So we need to accept the "unlocking" state too.
// We know when the user is unlocking, the CE storage is already unlocked.
return mUserManager.isUserUnlockingOrUnlocked(userId);
} finally {
injectRestoreCallingIdentity(token);
}
}
void throwIfUserLocked(@UserIdInt int userId) {
if (!isUserUnlocked(userId)) {
throw new IllegalStateException("User " + userId + " is locked or not running");
}
}
@GuardedBy("mLock")
@NonNull
private boolean isUserLoadedLocked(@UserIdInt int userId) {
@@ -1047,6 +1065,11 @@ public class ShortcutService extends IShortcutService.Stub {
@GuardedBy("mLock")
@NonNull
ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
if (!isUserUnlocked(userId)) {
wtf("User still locked");
return new ShortcutUser(this, userId);
}
ShortcutUser userPackages = mUsers.get(userId);
if (userPackages == null) {
userPackages = loadUserLocked(userId);
@@ -1535,6 +1558,7 @@ public class ShortcutService extends IShortcutService.Stub {
public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
@@ -1585,6 +1609,7 @@ public class ShortcutService extends IShortcutService.Stub {
public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
@@ -1664,6 +1689,7 @@ public class ShortcutService extends IShortcutService.Stub {
public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
@@ -1715,6 +1741,7 @@ public class ShortcutService extends IShortcutService.Stub {
CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
throwIfUserLocked(userId);
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
@@ -1743,6 +1770,7 @@ public class ShortcutService extends IShortcutService.Stub {
public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
throwIfUserLocked(userId);
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
@@ -1764,6 +1792,7 @@ public class ShortcutService extends IShortcutService.Stub {
@UserIdInt int userId) {
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
throwIfUserLocked(userId);
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
@@ -1787,6 +1816,7 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
@@ -1802,6 +1832,8 @@ public class ShortcutService extends IShortcutService.Stub {
public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
@@ -1813,6 +1845,8 @@ public class ShortcutService extends IShortcutService.Stub {
public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
@@ -1824,6 +1858,8 @@ public class ShortcutService extends IShortcutService.Stub {
public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
@@ -1854,6 +1890,7 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
@@ -1865,6 +1902,7 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) {
return getNextResetTimeLocked();
@@ -1883,6 +1921,7 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
verifyCaller(packageName, userId);
throwIfUserLocked(userId);
Preconditions.checkNotNull(shortcutId);
@@ -1951,6 +1990,10 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
}
enforceResetThrottlingPermission();
if (!isUserUnlocked(userId)) {
// This is called by system UI, so no need to throw. Just ignore.
return;
}
resetPackageThrottling(packageName, userId);
}
@@ -1968,6 +2011,8 @@ public class ShortcutService extends IShortcutService.Stub {
// even when hasShortcutPermission() is overridden.
@VisibleForTesting
boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
throwIfUserLocked(userId);
synchronized (mLock) {
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -2125,6 +2170,10 @@ public class ShortcutService extends IShortcutService.Stub {
@Nullable ComponentName componentName,
int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return ret;
}
final boolean cloneKeyFieldOnly =
((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO
@@ -2202,6 +2251,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return false;
}
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2218,6 +2271,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return null;
}
final ShortcutPackage p = getUserShortcutsLocked(userId)
.getPackageShortcutsIfExists(packageName);
if (p == null) {
@@ -2239,6 +2296,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return;
}
synchronized (mLock) {
final ShortcutLauncher launcher =
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
@@ -2259,6 +2320,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return null;
}
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2289,6 +2354,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return 0;
}
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2313,6 +2382,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return null;
}
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2345,6 +2418,9 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage) {
if (!isUserUnlocked(launcherUserId)) {
return false;
}
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
}
@@ -2395,7 +2471,7 @@ public class ShortcutService extends IShortcutService.Stub {
final long token = injectClearCallingIdentity();
try {
if (!mUserManager.isUserUnlocked(userId)) {
if (!isUserUnlocked(userId)) {
if (DEBUG) {
Slog.d(TAG, "Ignoring package broadcast " + action
+ " for locked/stopped user " + userId);
@@ -3208,7 +3284,7 @@ public class ShortcutService extends IShortcutService.Stub {
case "--user":
if (takeUser) {
mUserId = UserHandle.parseUserArg(getNextArgRequired());
if (!mUserManager.isUserUnlocked(mUserId)) {
if (!isUserUnlocked(mUserId)) {
throw new CommandException(
"User " + mUserId + " is not running or locked");
}

View File

@@ -687,19 +687,25 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
// Set up isUserRunning and isUserUnlocked.
when(mMockUserManager.isUserRunning(anyInt())).thenAnswer(new AnswerWithSystemCheck<>(
inv -> mRunningUsers.get((Integer) inv.getArguments()[0])));
inv -> b(mRunningUsers.get((Integer) inv.getArguments()[0]))));
when(mMockUserManager.isUserUnlocked(anyInt())).thenAnswer(new AnswerWithSystemCheck<>(
inv -> {
final int userId = (Integer) inv.getArguments()[0];
return mRunningUsers.get(userId) && mUnlockedUsers.get(userId);
}));
when(mMockUserManager.isUserUnlocked(anyInt()))
.thenAnswer(new AnswerWithSystemCheck<>(inv -> {
final int userId = (Integer) inv.getArguments()[0];
return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId));
}));
// isUserUnlockingOrUnlocked() return the same value as isUserUnlocked().
when(mMockUserManager.isUserUnlockingOrUnlocked(anyInt()))
.thenAnswer(new AnswerWithSystemCheck<>(inv -> {
final int userId = (Integer) inv.getArguments()[0];
return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId));
}));
// User 0 is always running
// User 0 and P0 are always running
mRunningUsers.put(USER_0, true);
mRunningUsers.put(USER_10, false);
mRunningUsers.put(USER_11, false);
mRunningUsers.put(USER_P0, false);
mRunningUsers.put(USER_P0, true);
// Unlock all users by default.
mUnlockedUsers.put(USER_0, true);
@@ -715,6 +721,10 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
setCaller(CALLING_PACKAGE_1);
}
private static boolean b(Boolean value) {
return (value != null && value);
}
/**
* Returns a boolean but also checks if the current UID is SYSTEM_UID.
*/
@@ -1726,6 +1736,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
}
protected void prepareCrossProfileDataSet() {
mRunningUsers.put(USER_10, true); // this test needs user 10.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),

View File

@@ -294,6 +294,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check max number
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
});
@@ -340,6 +342,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check fields.
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
});
@@ -348,6 +352,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
public void testPublishWithNoActivity() {
// If activity is not explicitly set, use the default one.
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
// s1 and s3 has no activities.
final ShortcutInfo si1 = new ShortcutInfo.Builder(mClientContext, "si1")
@@ -450,6 +456,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
public void testPublishWithNoActivity_noMainActivityInPackage() {
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
final ShortcutInfo si1 = new ShortcutInfo.Builder(mClientContext, "si1")
.setShortLabel("label1")
@@ -722,6 +730,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
// For USER-10, let's try without updating the times.
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("10s1", bmp32x32),
@@ -1071,6 +1081,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check bitmap removal too.
mRunningUsers.put(USER_11, true);
runWithCaller(CALLING_PACKAGE_2, USER_11, () -> {
mManager.updateShortcuts(list());
});
@@ -2048,6 +2060,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
@@ -2769,6 +2784,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.areAllWithKeyFieldsOnly()
.areAllDynamic();
mRunningUsers.put(USER_10, true);
// Different user, callback shouldn't be called.
assertForLauncherCallback(mLauncherApps, () -> {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -2980,27 +2997,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertCallbackReceived(cP0_1, HANDLE_USER_0, CALLING_PACKAGE_3,
"s1", "s2", "s3", "s4", "s5", "s6");
// Work profile, but not running, so don't send notifications.
resetAll(all);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
mManager.removeDynamicShortcuts(list());
});
waitOnMainThread();
assertCallbackNotReceived(c0_1);
assertCallbackNotReceived(c0_2);
assertCallbackNotReceived(c0_3);
assertCallbackNotReceived(c0_4);
assertCallbackNotReceived(cP0_1);
assertCallbackNotReceived(c10_1);
assertCallbackNotReceived(c10_2);
assertCallbackNotReceived(c11_1);
// Work profile, now running.
mRunningUsers.clear();
mRunningUsers.put(USER_P0, true);
// Work profile.
resetAll(all);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
mManager.removeDynamicShortcuts(list());
@@ -3017,7 +3014,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertCallbackReceived(cP0_1, HANDLE_USER_P0, CALLING_PACKAGE_1, "s1", "s2", "s3", "s4");
// Normal secondary user.
mRunningUsers.clear();
mRunningUsers.put(USER_10, true);
resetAll(all);
@@ -3112,6 +3108,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
});
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
@@ -3223,6 +3222,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
HANDLE_USER_0);
});
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s10_1"))));
@@ -3558,6 +3559,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
@@ -3876,6 +3880,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
setCaller(CALLING_PACKAGE_3, USER_0);
assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
mRunningUsers.put(USER_10, true);
setCaller(CALLING_PACKAGE_1, USER_10);
assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
@@ -3988,6 +3994,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
setCaller(CALLING_PACKAGE_3, USER_0);
assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
mRunningUsers.put(USER_10, true);
setCaller(CALLING_PACKAGE_1, USER_10);
assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
@@ -4114,6 +4122,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
makeShortcutWithIcon("s1", res32x32))));
});
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("s1", res32x32),
@@ -5441,6 +5451,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
// Try on another user, with some packages uninstalled.
mRunningUsers.put(USER_10, true);
uninstallPackage(USER_10, CALLING_PACKAGE_1);
uninstallPackage(USER_10, CALLING_PACKAGE_3);
@@ -5621,6 +5633,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
uninstallPackage(USER_10, CALLING_PACKAGE_4);
mService.handleUnlockUser(USER_0);
mRunningUsers.put(USER_10, true);
mService.handleUnlockUser(USER_10);
// Originally no manifest shortcuts.

View File

@@ -851,6 +851,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
}
public void testShortcutInfoSaveAndLoad() throws InterruptedException {
mRunningUsers.put(USER_10, true);
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
@@ -930,6 +932,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
}
public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
mRunningUsers.put(USER_10, true);
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
@@ -1794,6 +1798,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
}
public void testReportShortcutUsed() {
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
reset(mMockUsageStatsManagerInternal);

View File

@@ -183,6 +183,8 @@ public class ShortcutManagerTest6 extends BaseShortcutManagerTest {
}
public void testHasShortcutHostPermissionInner_multiUser() {
mRunningUsers.put(USER_10, true);
prepareGetHomeActivitiesAsUser(
/* preferred */ null,
list(getSystemLauncher(), getFallbackLauncher()),
@@ -220,6 +222,8 @@ public class ShortcutManagerTest6 extends BaseShortcutManagerTest {
}
public void testHasShortcutHostPermissionInner_clearCache() {
mRunningUsers.put(USER_10, true);
prepareGetHomeActivitiesAsUser(
/* preferred */ null,
list(getSystemLauncher(), getFallbackLauncher()),
@@ -242,12 +246,18 @@ public class ShortcutManagerTest6 extends BaseShortcutManagerTest {
assertEquals(cn(CALLING_PACKAGE_2, "name"),
mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
// Test it on a non-running user.
// Send ACTION_PREFERRED_ACTIVITY_CHANGED on user 10.
// But the user is not running, so will be ignored.
mRunningUsers.put(USER_10, false);
mService.mPackageMonitor.onReceive(mServiceContext,
new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED).putExtra(
Intent.EXTRA_USER_HANDLE, USER_10));
// Need to run the user again to access the internal status.
mRunningUsers.put(USER_10, true);
assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
mService.getUserShortcutsLocked(USER_0).getCachedLauncher());

View File

@@ -151,10 +151,14 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
mInjectedCallingUid = Process.SHELL_UID;
mRunningUsers.put(USER_10, false);
assertTrue(resultContains(
callShellCommand("reset-throttling", "--user", "10"),
"User 10 is not running or locked"));
mRunningUsers.put(USER_10, true);
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.getRemainingCallCount() < 3);
});

View File

@@ -55,6 +55,8 @@ import junit.framework.Assert;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.json.JSONException;
import org.json.JSONObject;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -86,7 +88,7 @@ import java.util.function.Predicate;
public class ShortcutManagerTestUtils {
private static final String TAG = "ShortcutManagerUtils";
private static final boolean ENABLE_DUMPSYS = false; // DO NOT SUBMIT WITH true
private static final boolean ENABLE_DUMPSYS = true; // DO NOT SUBMIT WITH true
private static final int STANDARD_TIMEOUT_SEC = 5;
@@ -233,6 +235,29 @@ public class ShortcutManagerTestUtils {
+ " --user " + userId + " " + packageName);
}
public static void anyContains(List<String> result, String expected) {
for (String l : result) {
if (l.contains(expected)) {
return;
}
}
fail("Result didn't contain '" + expected + "': was\n" + result);
}
public static void enableComponent(Instrumentation instrumentation, ComponentName cn,
boolean enable) {
final String word = (enable ? "enable" : "disable");
runCommand(instrumentation,
"pm " + word + " " + cn.flattenToString()
, result ->concatResult(result).contains(word));
}
public static void appOps(Instrumentation instrumentation, String packageName,
String op, String mode) {
runCommand(instrumentation, "appops set " + packageName + " " + op + " " + mode);
}
public static void dumpsysShortcut(Instrumentation instrumentation) {
if (!ENABLE_DUMPSYS) {
return;
@@ -243,6 +268,18 @@ public class ShortcutManagerTestUtils {
}
}
public static JSONObject getCheckinDump(Instrumentation instrumentation) throws JSONException {
return new JSONObject(concatResult(runCommand(instrumentation, "dumpsys shortcut -c")));
}
public static boolean isLowRamDevice(Instrumentation instrumentation) throws JSONException {
return getCheckinDump(instrumentation).getBoolean("lowRam");
}
public static int getIconSize(Instrumentation instrumentation) throws JSONException {
return getCheckinDump(instrumentation).getInt("iconSize");
}
public static Bundle makeBundle(Object... keysAndValues) {
assertTrue((keysAndValues.length % 2) == 0);
@@ -1014,4 +1051,15 @@ public class ShortcutManagerTestUtils {
return asserter;
}
public static void retryUntil(BooleanSupplier checker, String message) {
final long timeOut = System.currentTimeMillis() + 30 * 1000; // wait for 30 seconds.
while (!checker.getAsBoolean()) {
try {
Thread.sleep(200);
} catch (InterruptedException ignore) {
}
}
assertTrue(message, checker.getAsBoolean());
}
}