LauncherApps should also throw when user is locked

otherwise it'd be racy.

Bug 30406401

Change-Id: I953eb6ae58e029d254d9fdbd5d05a0090b8d2391
This commit is contained in:
Makoto Onuki
2016-07-29 09:40:40 -07:00
parent 5faa852c57
commit 02f338e23d
4 changed files with 240 additions and 190 deletions

View File

@@ -492,7 +492,7 @@ public class LauncherApps {
* If the calling launcher application contains pinned shortcuts, they will still work, * If the calling launcher application contains pinned shortcuts, they will still work,
* even though the caller no longer has the shortcut host permission. * even though the caller no longer has the shortcut host permission.
* *
* <p>Returns {@code false} when the user is locked. * @throws IllegalStateException when the user is locked.
* *
* @see ShortcutManager * @see ShortcutManager
*/ */
@@ -510,13 +510,12 @@ public class LauncherApps {
* <p>Callers must be allowed to access the shortcut information, as defined in {@link * <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}. * #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 query result includes shortcuts matching this query.
* @param user The UserHandle of the profile. * @param user The UserHandle of the profile.
* *
* @return the IDs of {@link ShortcutInfo}s that match the query. * @return the IDs of {@link ShortcutInfo}s that match the query.
* @throws IllegalStateException when the user is locked, or when the {@code user} user
* is locked or not running.
* *
* @see ShortcutManager * @see ShortcutManager
*/ */
@@ -556,12 +555,11 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information, * <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}. * 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 packageName The target package name.
* @param shortcutIds The IDs of the shortcut to be pinned. * @param shortcutIds The IDs of the shortcut to be pinned.
* @param user The UserHandle of the profile. * @param user The UserHandle of the profile.
* @throws IllegalStateException when the user is locked, or when the {@code user} user
* is locked or not running.
* *
* @see ShortcutManager * @see ShortcutManager
*/ */
@@ -630,13 +628,12 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information, * <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}. * 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 * @param density The preferred density of the icon, zero for default density. Use
* density DPI values from {@link DisplayMetrics}. * density DPI values from {@link DisplayMetrics}.
* *
* @return The drawable associated with the shortcut. * @return The drawable associated with the shortcut.
* @throws IllegalStateException when the user is locked, or when the {@code user} user
* is locked or not running.
* *
* @see ShortcutManager * @see ShortcutManager
* @see #getShortcutBadgedIconDrawable(ShortcutInfo, int) * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
@@ -681,11 +678,10 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information, * <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}. * 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 * @param density Optional density for the icon, or 0 to use the default density. Use
* @return A badged icon for the shortcut. * @return A badged icon for the shortcut.
* @throws IllegalStateException when the user is locked, or when the {@code user} user
* is locked or not running.
* *
* @see ShortcutManager * @see ShortcutManager
* @see #getShortcutIconDrawable(ShortcutInfo, int) * @see #getShortcutIconDrawable(ShortcutInfo, int)
@@ -704,15 +700,13 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information, * <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}. * 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 packageName The target shortcut package name.
* @param shortcutId The target shortcut ID. * @param shortcutId The target shortcut ID.
* @param sourceBounds The Rect containing the source bounds of the clicked icon. * @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity. * @param startActivityOptions Options to pass to startActivity.
* @param user The UserHandle of the profile. * @param user The UserHandle of the profile.
* @throws IllegalStateException when the user is locked, or when the {@code user} user
* is locked or not running.
* *
* @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
* the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
@@ -730,13 +724,11 @@ public class LauncherApps {
* <p>The calling launcher application must be allowed to access the shortcut information, * <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}. * 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 shortcut The target shortcut.
* @param sourceBounds The Rect containing the source bounds of the clicked icon. * @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity. * @param startActivityOptions Options to pass to startActivity.
* @throws IllegalStateException when the user is locked, or when the {@code user} user
* is locked or not running.
* *
* @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
* the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)

View File

@@ -768,6 +768,7 @@ public class LauncherAppsService extends SystemService {
private void onShortcutChangedInner(@NonNull String packageName, private void onShortcutChangedInner(@NonNull String packageName,
@UserIdInt int userId) { @UserIdInt int userId) {
try {
final UserHandle user = UserHandle.of(userId); final UserHandle user = UserHandle.of(userId);
final int n = mListeners.beginBroadcast(); final int n = mListeners.beginBroadcast();
@@ -803,6 +804,10 @@ public class LauncherAppsService extends SystemService {
} }
} }
mListeners.finishBroadcast(); mListeners.finishBroadcast();
} catch (RuntimeException e) {
// When the user is locked we get IllegalState, so just catch all.
Log.w(TAG, e.getMessage(), e);
}
} }
} }

View File

@@ -77,6 +77,7 @@ import android.util.KeyValueListParser;
import android.util.Log; import android.util.Log;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.util.SparseLongArray; import android.util.SparseLongArray;
import android.util.TypedValue; import android.util.TypedValue;
@@ -85,7 +86,6 @@ import android.view.IWindowManager;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread; import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
@@ -301,6 +301,9 @@ public class ShortcutService extends IShortcutService.Stub {
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES; | PackageManager.MATCH_UNINSTALLED_PACKAGES;
@GuardedBy("mLock")
final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
// Stats // Stats
@VisibleForTesting @VisibleForTesting
interface Stats { interface Stats {
@@ -522,6 +525,8 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, "handleUnlockUser: user=" + userId); Slog.d(TAG, "handleUnlockUser: user=" + userId);
} }
synchronized (mLock) { synchronized (mLock) {
mUnlockedUsers.put(userId, true);
// Preload the user's shortcuts. // Preload the user's shortcuts.
// Also see if the locale has changed. // Also see if the locale has changed.
// Note as of nyc, the locale is per-user, so the locale shouldn't change // Note as of nyc, the locale is per-user, so the locale shouldn't change
@@ -534,8 +539,13 @@ public class ShortcutService extends IShortcutService.Stub {
/** lifecycle event */ /** lifecycle event */
void handleCleanupUser(int userId) { void handleCleanupUser(int userId) {
if (DEBUG) {
Slog.d(TAG, "handleCleanupUser: user=" + userId);
}
synchronized (mLock) { synchronized (mLock) {
unloadUserLocked(userId); unloadUserLocked(userId);
mUnlockedUsers.put(userId, false);
} }
} }
@@ -978,6 +988,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "saveDirtyInfo"); Slog.d(TAG, "saveDirtyInfo");
} }
try {
synchronized (mLock) { synchronized (mLock) {
for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) { for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
final int userId = mDirtyUserIds.get(i); final int userId = mDirtyUserIds.get(i);
@@ -989,6 +1000,9 @@ public class ShortcutService extends IShortcutService.Stub {
} }
mDirtyUserIds.clear(); mDirtyUserIds.clear();
} }
} catch (Exception e) {
wtf("Exception in saveDirtyInfo", e);
}
} }
/** Return the last reset time. */ /** Return the last reset time. */
@@ -1037,20 +1051,14 @@ public class ShortcutService extends IShortcutService.Stub {
} }
} }
private boolean isUserUnlocked(@UserIdInt int userId) { // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L".
final long token = injectClearCallingIdentity(); protected boolean isUserUnlockedL(@UserIdInt int userId) {
try { return mUnlockedUsers.get(userId);
// 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) { // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L".
if (!isUserUnlocked(userId)) { void throwIfUserLockedL(@UserIdInt int userId) {
if (!isUserUnlockedL(userId)) {
throw new IllegalStateException("User " + userId + " is locked or not running"); throw new IllegalStateException("User " + userId + " is locked or not running");
} }
} }
@@ -1065,9 +1073,8 @@ public class ShortcutService extends IShortcutService.Stub {
@GuardedBy("mLock") @GuardedBy("mLock")
@NonNull @NonNull
ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) { ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
if (!isUserUnlocked(userId)) { if (!isUserUnlockedL(userId)) {
wtf("User still locked"); wtf("User still locked");
return new ShortcutUser(this, userId);
} }
ShortcutUser userPackages = mUsers.get(userId); ShortcutUser userPackages = mUsers.get(userId);
@@ -1471,23 +1478,22 @@ public class ShortcutService extends IShortcutService.Stub {
} }
private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) { private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
final long token = injectClearCallingIdentity();
try {
if (!mUserManager.isUserRunning(userId)) {
return;
}
} finally {
injectRestoreCallingIdentity(token);
}
injectPostToHandler(() -> { injectPostToHandler(() -> {
try {
final ArrayList<ShortcutChangeListener> copy; final ArrayList<ShortcutChangeListener> copy;
synchronized (mLock) { synchronized (mLock) {
if (!isUserUnlockedL(userId)) {
return;
}
copy = new ArrayList<>(mListeners); copy = new ArrayList<>(mListeners);
} }
// Note onShortcutChanged() needs to be called with the system service permissions. // Note onShortcutChanged() needs to be called with the system service permissions.
for (int i = copy.size() - 1; i >= 0; i--) { for (int i = copy.size() - 1; i >= 0; i--) {
copy.get(i).onShortcutChanged(packageName, userId); copy.get(i).onShortcutChanged(packageName, userId);
} }
} catch (Exception ignore) {
}
}); });
} }
@@ -1558,12 +1564,13 @@ public class ShortcutService extends IShortcutService.Stub {
public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) { @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size(); final int size = newShortcuts.size();
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
@@ -1609,12 +1616,13 @@ public class ShortcutService extends IShortcutService.Stub {
public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) { @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size(); final int size = newShortcuts.size();
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
@@ -1689,12 +1697,13 @@ public class ShortcutService extends IShortcutService.Stub {
public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) { @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size(); final int size = newShortcuts.size();
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
@@ -1741,9 +1750,10 @@ public class ShortcutService extends IShortcutService.Stub {
CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) { CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
@@ -1770,9 +1780,10 @@ public class ShortcutService extends IShortcutService.Stub {
public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
@@ -1792,9 +1803,10 @@ public class ShortcutService extends IShortcutService.Stub {
@UserIdInt int userId) { @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
@@ -1816,9 +1828,10 @@ public class ShortcutService extends IShortcutService.Stub {
@Override @Override
public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
ps.deleteAllDynamicShortcuts(); ps.deleteAllDynamicShortcuts();
@@ -1832,9 +1845,10 @@ public class ShortcutService extends IShortcutService.Stub {
public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName, public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
@UserIdInt int userId) { @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
return getShortcutsWithQueryLocked( return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
ShortcutInfo::isDynamic); ShortcutInfo::isDynamic);
@@ -1845,9 +1859,10 @@ public class ShortcutService extends IShortcutService.Stub {
public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName, public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
@UserIdInt int userId) { @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
return getShortcutsWithQueryLocked( return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
ShortcutInfo::isManifestShortcut); ShortcutInfo::isManifestShortcut);
@@ -1858,9 +1873,10 @@ public class ShortcutService extends IShortcutService.Stub {
public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName, public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
@UserIdInt int userId) { @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
return getShortcutsWithQueryLocked( return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
ShortcutInfo::isPinned); ShortcutInfo::isPinned);
@@ -1890,9 +1906,10 @@ public class ShortcutService extends IShortcutService.Stub {
@Override @Override
public int getRemainingCallCount(String packageName, @UserIdInt int userId) { public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
return mMaxUpdatesPerInterval - ps.getApiCallCount(); return mMaxUpdatesPerInterval - ps.getApiCallCount();
@@ -1902,9 +1919,10 @@ public class ShortcutService extends IShortcutService.Stub {
@Override @Override
public long getRateLimitResetTime(String packageName, @UserIdInt int userId) { public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
return getNextResetTimeLocked(); return getNextResetTimeLocked();
} }
} }
@@ -1921,7 +1939,6 @@ public class ShortcutService extends IShortcutService.Stub {
@Override @Override
public void reportShortcutUsed(String packageName, String shortcutId, int userId) { public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
throwIfUserLocked(userId);
Preconditions.checkNotNull(shortcutId); Preconditions.checkNotNull(shortcutId);
@@ -1931,6 +1948,8 @@ public class ShortcutService extends IShortcutService.Stub {
} }
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName); ps.getUser().onCalledByPublisher(packageName);
@@ -1962,6 +1981,11 @@ public class ShortcutService extends IShortcutService.Stub {
void resetThrottlingInner(@UserIdInt int userId) { void resetThrottlingInner(@UserIdInt int userId) {
synchronized (mLock) { synchronized (mLock) {
if (!isUserUnlockedL(userId)) {
Log.w(TAG, "User " + userId + " is locked or not running");
return;
}
getUserShortcutsLocked(userId).resetThrottling(); getUserShortcutsLocked(userId).resetThrottling();
} }
scheduleSaveUser(userId); scheduleSaveUser(userId);
@@ -1976,25 +2000,23 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.i(TAG, "ShortcutManager: throttling counter reset for all users"); Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
} }
void resetPackageThrottling(String packageName, int userId) {
synchronized (mLock) {
getPackageShortcutsLocked(packageName, userId)
.resetRateLimitingForCommandLineNoSaving();
saveUserLocked(userId);
}
}
@Override @Override
public void onApplicationActive(String packageName, int userId) { public void onApplicationActive(String packageName, int userId) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId); Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
} }
enforceResetThrottlingPermission(); enforceResetThrottlingPermission();
if (!isUserUnlocked(userId)) {
synchronized (mLock) {
if (!isUserUnlockedL(userId)) {
// This is called by system UI, so no need to throw. Just ignore. // This is called by system UI, so no need to throw. Just ignore.
return; return;
} }
resetPackageThrottling(packageName, userId);
getPackageShortcutsLocked(packageName, userId)
.resetRateLimitingForCommandLineNoSaving();
saveUserLocked(userId);
}
} }
// We override this method in unit tests to do a simpler check. // We override this method in unit tests to do a simpler check.
@@ -2011,9 +2033,9 @@ public class ShortcutService extends IShortcutService.Stub {
// even when hasShortcutPermission() is overridden. // even when hasShortcutPermission() is overridden.
@VisibleForTesting @VisibleForTesting
boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
throwIfUserLocked(userId);
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutUser user = getUserShortcutsLocked(userId); final ShortcutUser user = getUserShortcutsLocked(userId);
// Always trust the in-memory cache. // Always trust the in-memory cache.
@@ -2170,9 +2192,6 @@ public class ShortcutService extends IShortcutService.Stub {
@Nullable ComponentName componentName, @Nullable ComponentName componentName,
int queryFlags, int userId) { int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>(); final ArrayList<ShortcutInfo> ret = new ArrayList<>();
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return ret;
}
final boolean cloneKeyFieldOnly = final boolean cloneKeyFieldOnly =
((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0); ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
@@ -2183,6 +2202,9 @@ public class ShortcutService extends IShortcutService.Stub {
} }
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
throwIfUserLockedL(launcherUserId);
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(); .attemptToRestoreIfNeededAndSave();
@@ -2251,11 +2273,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return false;
}
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
throwIfUserLockedL(launcherUserId);
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(); .attemptToRestoreIfNeededAndSave();
@@ -2271,9 +2292,8 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) { throwIfUserLockedL(userId);
return null; throwIfUserLockedL(launcherUserId);
}
final ShortcutPackage p = getUserShortcutsLocked(userId) final ShortcutPackage p = getUserShortcutsLocked(userId)
.getPackageShortcutsIfExists(packageName); .getPackageShortcutsIfExists(packageName);
@@ -2296,11 +2316,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkNotNull(shortcutIds, "shortcutIds"); Preconditions.checkNotNull(shortcutIds, "shortcutIds");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return;
}
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
throwIfUserLockedL(launcherUserId);
final ShortcutLauncher launcher = final ShortcutLauncher launcher =
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId); getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
launcher.attemptToRestoreIfNeededAndSave(); launcher.attemptToRestoreIfNeededAndSave();
@@ -2320,11 +2339,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty"); Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return null;
}
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
throwIfUserLockedL(launcherUserId);
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(); .attemptToRestoreIfNeededAndSave();
@@ -2354,11 +2372,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkNotNull(packageName, "packageName"); Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId"); Preconditions.checkNotNull(shortcutId, "shortcutId");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return 0;
}
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
throwIfUserLockedL(launcherUserId);
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(); .attemptToRestoreIfNeededAndSave();
@@ -2382,11 +2399,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkNotNull(packageName, "packageName"); Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId"); Preconditions.checkNotNull(shortcutId, "shortcutId");
if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
return null;
}
synchronized (mLock) { synchronized (mLock) {
throwIfUserLockedL(userId);
throwIfUserLockedL(launcherUserId);
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(); .attemptToRestoreIfNeededAndSave();
@@ -2418,9 +2434,6 @@ public class ShortcutService extends IShortcutService.Stub {
@Override @Override
public boolean hasShortcutHostPermission(int launcherUserId, public boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage) { @NonNull String callingPackage) {
if (!isUserUnlocked(launcherUserId)) {
return false;
}
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId); return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
} }
} }
@@ -2431,9 +2444,13 @@ public class ShortcutService extends IShortcutService.Stub {
if (!mBootCompleted.get()) { if (!mBootCompleted.get()) {
return; // Boot not completed, ignore the broadcast. return; // Boot not completed, ignore the broadcast.
} }
try {
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
handleLocaleChanged(); handleLocaleChanged();
} }
} catch (Exception e) {
wtf("Exception in mReceiver.onReceive", e);
}
} }
}; };
@@ -2443,6 +2460,7 @@ public class ShortcutService extends IShortcutService.Stub {
} }
scheduleSaveBaseState(); scheduleSaveBaseState();
synchronized (mLock) {
final long token = injectClearCallingIdentity(); final long token = injectClearCallingIdentity();
try { try {
forEachLoadedUserLocked(user -> user.detectLocaleChange()); forEachLoadedUserLocked(user -> user.detectLocaleChange());
@@ -2450,6 +2468,7 @@ public class ShortcutService extends IShortcutService.Stub {
injectRestoreCallingIdentity(token); injectRestoreCallingIdentity(token);
} }
} }
}
/** /**
* Package event callbacks. * Package event callbacks.
@@ -2470,8 +2489,8 @@ public class ShortcutService extends IShortcutService.Stub {
// but we still check it in unit tests. // but we still check it in unit tests.
final long token = injectClearCallingIdentity(); final long token = injectClearCallingIdentity();
try { try {
synchronized (mLock) {
if (!isUserUnlocked(userId)) { if (!isUserUnlockedL(userId)) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "Ignoring package broadcast " + action Slog.d(TAG, "Ignoring package broadcast " + action
+ " for locked/stopped user " + userId); + " for locked/stopped user " + userId);
@@ -2481,7 +2500,6 @@ public class ShortcutService extends IShortcutService.Stub {
// Whenever we get one of those package broadcasts, or get // Whenever we get one of those package broadcasts, or get
// ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache. // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache.
synchronized (mLock) {
final ShortcutUser user = getUserShortcutsLocked(userId); final ShortcutUser user = getUserShortcutsLocked(userId);
user.clearLauncher(); user.clearLauncher();
} }
@@ -2521,6 +2539,8 @@ public class ShortcutService extends IShortcutService.Stub {
handlePackageDataCleared(packageName, userId); handlePackageDataCleared(packageName, userId);
break; break;
} }
} catch (Exception e) {
wtf("Exception in mPackageMonitor.onReceive", e);
} finally { } finally {
injectRestoreCallingIdentity(token); injectRestoreCallingIdentity(token);
} }
@@ -3031,9 +3051,14 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, "Backing up user " + userId); Slog.d(TAG, "Backing up user " + userId);
} }
synchronized (mLock) { synchronized (mLock) {
if (!isUserUnlockedL(userId)) {
wtf("Can't backup: user " + userId + " is locked or not running");
return null;
}
final ShortcutUser user = getUserShortcutsLocked(userId); final ShortcutUser user = getUserShortcutsLocked(userId);
if (user == null) { if (user == null) {
Slog.w(TAG, "Can't backup: user not found: id=" + userId); wtf("Can't backup: user not found: id=" + userId);
return null; return null;
} }
@@ -3058,6 +3083,11 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "Restoring user " + userId); Slog.d(TAG, "Restoring user " + userId);
} }
synchronized (mLock) {
if (!isUserUnlockedL(userId)) {
wtf("Can't restore: user " + userId + " is locked or not running");
return;
}
final ShortcutUser user; final ShortcutUser user;
final ByteArrayInputStream is = new ByteArrayInputStream(payload); final ByteArrayInputStream is = new ByteArrayInputStream(payload);
try { try {
@@ -3066,7 +3096,6 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.w(TAG, "Restoration failed.", e); Slog.w(TAG, "Restoration failed.", e);
return; return;
} }
synchronized (mLock) {
mUsers.put(userId, user); mUsers.put(userId, user);
// Then purge all the save images. // Then purge all the save images.
@@ -3276,7 +3305,7 @@ public class ShortcutService extends IShortcutService.Stub {
private int mUserId = UserHandle.USER_SYSTEM; private int mUserId = UserHandle.USER_SYSTEM;
private void parseOptions(boolean takeUser) private void parseOptionsLocked(boolean takeUser)
throws CommandException { throws CommandException {
String opt; String opt;
while ((opt = getNextOption()) != null) { while ((opt = getNextOption()) != null) {
@@ -3284,7 +3313,7 @@ public class ShortcutService extends IShortcutService.Stub {
case "--user": case "--user":
if (takeUser) { if (takeUser) {
mUserId = UserHandle.parseUserArg(getNextArgRequired()); mUserId = UserHandle.parseUserArg(getNextArgRequired());
if (!isUserUnlocked(mUserId)) { if (!isUserUnlockedL(mUserId)) {
throw new CommandException( throw new CommandException(
"User " + mUserId + " is not running or locked"); "User " + mUserId + " is not running or locked");
} }
@@ -3376,12 +3405,14 @@ public class ShortcutService extends IShortcutService.Stub {
} }
private void handleResetThrottling() throws CommandException { private void handleResetThrottling() throws CommandException {
parseOptions(/* takeUser =*/ true); synchronized (mLock) {
parseOptionsLocked(/* takeUser =*/ true);
Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId); Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
resetThrottlingInner(mUserId); resetThrottlingInner(mUserId);
} }
}
private void handleResetAllThrottling() { private void handleResetAllThrottling() {
Slog.i(TAG, "cmd: handleResetAllThrottling"); Slog.i(TAG, "cmd: handleResetAllThrottling");
@@ -3426,28 +3457,35 @@ public class ShortcutService extends IShortcutService.Stub {
} }
private void handleClearDefaultLauncher() throws CommandException { private void handleClearDefaultLauncher() throws CommandException {
parseOptions(/* takeUser =*/ true); synchronized (mLock) {
parseOptionsLocked(/* takeUser =*/ true);
clearLauncher(); clearLauncher();
} }
}
private void handleGetDefaultLauncher() throws CommandException { private void handleGetDefaultLauncher() throws CommandException {
parseOptions(/* takeUser =*/ true); synchronized (mLock) {
parseOptionsLocked(/* takeUser =*/ true);
clearLauncher(); clearLauncher();
showLauncher(); showLauncher();
} }
}
private void handleUnloadUser() throws CommandException { private void handleUnloadUser() throws CommandException {
parseOptions(/* takeUser =*/ true); synchronized (mLock) {
parseOptionsLocked(/* takeUser =*/ true);
Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId); Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
ShortcutService.this.handleCleanupUser(mUserId); ShortcutService.this.handleCleanupUser(mUserId);
} }
}
private void handleClearShortcuts() throws CommandException { private void handleClearShortcuts() throws CommandException {
parseOptions(/* takeUser =*/ true); synchronized (mLock) {
parseOptionsLocked(/* takeUser =*/ true);
final String packageName = getNextArgRequired(); final String packageName = getNextArgRequired();
Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName); Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
@@ -3455,6 +3493,7 @@ public class ShortcutService extends IShortcutService.Stub {
ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId, ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
/* appStillExists = */ true); /* appStillExists = */ true);
} }
}
private void handleVerifyStates() throws CommandException { private void handleVerifyStates() throws CommandException {
try { try {

View File

@@ -245,6 +245,20 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
mContext.injectRestoreCallingIdentity(token); mContext.injectRestoreCallingIdentity(token);
} }
@Override
protected boolean isUserUnlockedL(@UserIdInt int userId) {
// Note due to a late change, now ShortcutManager doesn't use
// UserManager.isUserUnlockingOrUnlocked(). But all unit tests are still using it,
// so we convert here.
final long token = injectClearCallingIdentity();
try {
return mMockUserManager.isUserUnlockingOrUnlocked(userId);
} finally {
injectRestoreCallingIdentity(token);
}
}
@Override @Override
int injectDipToPixel(int dip) { int injectDipToPixel(int dip) {
return dip; return dip;