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:
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user