Loading core/java/android/content/pm/ILauncherApps.aidl +5 −0 Original line number Original line Diff line number Diff line Loading @@ -98,4 +98,9 @@ interface ILauncherApps { in ComponentName componentName, int flags, in IShortcutChangeCallback callback, in ComponentName componentName, int flags, in IShortcutChangeCallback callback, int callbackId); int callbackId); void unregisterShortcutChangeCallback(String callingPackage, int callbackId); void unregisterShortcutChangeCallback(String callingPackage, int callbackId); void cacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); void uncacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); } } core/java/android/content/pm/LauncherApps.java +56 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemApi; Loading Loading @@ -1088,6 +1089,61 @@ public class LauncherApps { } } } } /** * Mark shortcuts as cached for a package. * * <p>Only dynamic long lived shortcuts can be cached. None dynamic or non long lived shortcuts * in the list will be ignored. * * <p>Unlike pinned shortcuts, where different callers can have different sets of pinned * shortcuts, cached state is per shortcut only, and even if multiple callers cache the same * shortcut, it can be uncached by any valid caller. * * @param packageName The target package name. * @param shortcutIds The IDs of the shortcut to be cached. * @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 * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public void cacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user) { logErrorForInvalidProfileAccess(user); try { mService.cacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove cached flag from shortcuts for a package. * * @param packageName The target package name. * @param shortcutIds The IDs of the shortcut to be uncached. * @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 * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public void uncacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user) { logErrorForInvalidProfileAccess(user); try { mService.uncacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** /** * @hide kept for testing. * @hide kept for testing. */ */ Loading core/java/android/content/pm/ShortcutServiceInternal.java +7 −0 Original line number Original line Diff line number Diff line Loading @@ -85,4 +85,11 @@ public abstract class ShortcutServiceInternal { public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage, public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage, int callingUid); int callingUid); public abstract void cacheShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId); public abstract void uncacheShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId); } } services/core/java/com/android/server/pm/LauncherAppsService.java +41 −0 Original line number Original line Diff line number Diff line Loading @@ -661,6 +661,23 @@ public class LauncherAppsService extends SystemService { } } } } private void ensureStrictAccessShortcutsPermission(@NonNull String callingPackage) { verifyCallingPackage(callingPackage); if (!injectHasAccessShortcutsPermission(injectBinderCallingPid(), injectBinderCallingUid())) { throw new SecurityException("Caller can't access shortcut information"); } } /** * Returns true if the caller has the "ACCESS_SHORTCUTS" permission. */ @VisibleForTesting boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) { return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; } @Override @Override public ParceledListSlice getShortcuts(String callingPackage, long changedSince, public ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName, List shortcutIds, List<LocusId> locusIds, String packageName, List shortcutIds, List<LocusId> locusIds, Loading Loading @@ -709,6 +726,30 @@ public class LauncherAppsService extends SystemService { callingPackage, packageName, ids, targetUser.getIdentifier()); callingPackage, packageName, ids, targetUser.getIdentifier()); } } @Override public void cacheShortcuts(String callingPackage, String packageName, List<String> ids, UserHandle targetUser) { ensureStrictAccessShortcutsPermission(callingPackage); if (!canAccessProfile(targetUser.getIdentifier(), "Cannot cache shortcuts")) { return; } mShortcutServiceInternal.cacheShortcuts(getCallingUserId(), callingPackage, packageName, ids, targetUser.getIdentifier()); } @Override public void uncacheShortcuts(String callingPackage, String packageName, List<String> ids, UserHandle targetUser) { ensureStrictAccessShortcutsPermission(callingPackage); if (!canAccessProfile(targetUser.getIdentifier(), "Cannot uncache shortcuts")) { return; } mShortcutServiceInternal.uncacheShortcuts(getCallingUserId(), callingPackage, packageName, ids, targetUser.getIdentifier()); } @Override @Override public int getShortcutIconResId(String callingPackage, String packageName, String id, public int getShortcutIconResId(String callingPackage, String packageName, String id, int targetUserId) { int targetUserId) { Loading services/core/java/com/android/server/pm/ShortcutService.java +63 −1 Original line number Original line Diff line number Diff line Loading @@ -1704,7 +1704,7 @@ public class ShortcutService extends IShortcutService.Stub { ShortcutInfo.validateIcon(shortcut.getIcon()); ShortcutInfo.validateIcon(shortcut.getIcon()); } } shortcut.replaceFlags(0); shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED); } } private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { Loading Loading @@ -2757,6 +2757,68 @@ public class ShortcutService extends IShortcutService.Stub { verifyStates(); verifyStates(); } } @Override public void cacheShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId) { updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds, userId, /* doCache= */ true); } @Override public void uncacheShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId) { updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds, userId, /* doCache= */ false); } private void updateCachedShortcutsInternal(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId, boolean doCache) { // Calling permission must be checked by LauncherAppsImpl. Preconditions.checkStringNotEmpty(packageName, "packageName"); Objects.requireNonNull(shortcutIds, "shortcutIds"); synchronized (mLock) { throwIfUserLockedL(userId); throwIfUserLockedL(launcherUserId); final int idSize = shortcutIds.size(); final ShortcutPackage sp = getUserShortcutsLocked(userId) .getPackageShortcutsIfExists(packageName); if (idSize == 0 || sp == null) { return; } for (int i = 0; i < idSize; i++) { final String id = Preconditions.checkStringNotEmpty(shortcutIds.get(i)); final ShortcutInfo si = sp.findShortcutById(id); if (si == null || doCache == si.isCached()) { continue; } if (doCache) { if (si.isDynamic() && si.isLongLived()) { si.addFlags(ShortcutInfo.FLAG_CACHED); } else { Log.w(TAG, "Only dynamic long lived shortcuts can get cached. Ignoring" + "shortcut " + si.getId()); } } else { if (si.isDynamic()) { si.clearFlags(ShortcutInfo.FLAG_CACHED); } else { sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); } } } } packageShortcutsChanged(packageName, userId); verifyStates(); } @Override @Override public Intent[] createShortcutIntents(int launcherUserId, public Intent[] createShortcutIntents(int launcherUserId, @NonNull String callingPackage, @NonNull String callingPackage, Loading Loading
core/java/android/content/pm/ILauncherApps.aidl +5 −0 Original line number Original line Diff line number Diff line Loading @@ -98,4 +98,9 @@ interface ILauncherApps { in ComponentName componentName, int flags, in IShortcutChangeCallback callback, in ComponentName componentName, int flags, in IShortcutChangeCallback callback, int callbackId); int callbackId); void unregisterShortcutChangeCallback(String callingPackage, int callbackId); void unregisterShortcutChangeCallback(String callingPackage, int callbackId); void cacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); void uncacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); } }
core/java/android/content/pm/LauncherApps.java +56 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemApi; Loading Loading @@ -1088,6 +1089,61 @@ public class LauncherApps { } } } } /** * Mark shortcuts as cached for a package. * * <p>Only dynamic long lived shortcuts can be cached. None dynamic or non long lived shortcuts * in the list will be ignored. * * <p>Unlike pinned shortcuts, where different callers can have different sets of pinned * shortcuts, cached state is per shortcut only, and even if multiple callers cache the same * shortcut, it can be uncached by any valid caller. * * @param packageName The target package name. * @param shortcutIds The IDs of the shortcut to be cached. * @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 * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public void cacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user) { logErrorForInvalidProfileAccess(user); try { mService.cacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove cached flag from shortcuts for a package. * * @param packageName The target package name. * @param shortcutIds The IDs of the shortcut to be uncached. * @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 * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public void uncacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user) { logErrorForInvalidProfileAccess(user); try { mService.uncacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** /** * @hide kept for testing. * @hide kept for testing. */ */ Loading
core/java/android/content/pm/ShortcutServiceInternal.java +7 −0 Original line number Original line Diff line number Diff line Loading @@ -85,4 +85,11 @@ public abstract class ShortcutServiceInternal { public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage, public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage, int callingUid); int callingUid); public abstract void cacheShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId); public abstract void uncacheShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId); } }
services/core/java/com/android/server/pm/LauncherAppsService.java +41 −0 Original line number Original line Diff line number Diff line Loading @@ -661,6 +661,23 @@ public class LauncherAppsService extends SystemService { } } } } private void ensureStrictAccessShortcutsPermission(@NonNull String callingPackage) { verifyCallingPackage(callingPackage); if (!injectHasAccessShortcutsPermission(injectBinderCallingPid(), injectBinderCallingUid())) { throw new SecurityException("Caller can't access shortcut information"); } } /** * Returns true if the caller has the "ACCESS_SHORTCUTS" permission. */ @VisibleForTesting boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) { return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; } @Override @Override public ParceledListSlice getShortcuts(String callingPackage, long changedSince, public ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName, List shortcutIds, List<LocusId> locusIds, String packageName, List shortcutIds, List<LocusId> locusIds, Loading Loading @@ -709,6 +726,30 @@ public class LauncherAppsService extends SystemService { callingPackage, packageName, ids, targetUser.getIdentifier()); callingPackage, packageName, ids, targetUser.getIdentifier()); } } @Override public void cacheShortcuts(String callingPackage, String packageName, List<String> ids, UserHandle targetUser) { ensureStrictAccessShortcutsPermission(callingPackage); if (!canAccessProfile(targetUser.getIdentifier(), "Cannot cache shortcuts")) { return; } mShortcutServiceInternal.cacheShortcuts(getCallingUserId(), callingPackage, packageName, ids, targetUser.getIdentifier()); } @Override public void uncacheShortcuts(String callingPackage, String packageName, List<String> ids, UserHandle targetUser) { ensureStrictAccessShortcutsPermission(callingPackage); if (!canAccessProfile(targetUser.getIdentifier(), "Cannot uncache shortcuts")) { return; } mShortcutServiceInternal.uncacheShortcuts(getCallingUserId(), callingPackage, packageName, ids, targetUser.getIdentifier()); } @Override @Override public int getShortcutIconResId(String callingPackage, String packageName, String id, public int getShortcutIconResId(String callingPackage, String packageName, String id, int targetUserId) { int targetUserId) { Loading
services/core/java/com/android/server/pm/ShortcutService.java +63 −1 Original line number Original line Diff line number Diff line Loading @@ -1704,7 +1704,7 @@ public class ShortcutService extends IShortcutService.Stub { ShortcutInfo.validateIcon(shortcut.getIcon()); ShortcutInfo.validateIcon(shortcut.getIcon()); } } shortcut.replaceFlags(0); shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED); } } private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { Loading Loading @@ -2757,6 +2757,68 @@ public class ShortcutService extends IShortcutService.Stub { verifyStates(); verifyStates(); } } @Override public void cacheShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId) { updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds, userId, /* doCache= */ true); } @Override public void uncacheShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId) { updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds, userId, /* doCache= */ false); } private void updateCachedShortcutsInternal(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId, boolean doCache) { // Calling permission must be checked by LauncherAppsImpl. Preconditions.checkStringNotEmpty(packageName, "packageName"); Objects.requireNonNull(shortcutIds, "shortcutIds"); synchronized (mLock) { throwIfUserLockedL(userId); throwIfUserLockedL(launcherUserId); final int idSize = shortcutIds.size(); final ShortcutPackage sp = getUserShortcutsLocked(userId) .getPackageShortcutsIfExists(packageName); if (idSize == 0 || sp == null) { return; } for (int i = 0; i < idSize; i++) { final String id = Preconditions.checkStringNotEmpty(shortcutIds.get(i)); final ShortcutInfo si = sp.findShortcutById(id); if (si == null || doCache == si.isCached()) { continue; } if (doCache) { if (si.isDynamic() && si.isLongLived()) { si.addFlags(ShortcutInfo.FLAG_CACHED); } else { Log.w(TAG, "Only dynamic long lived shortcuts can get cached. Ignoring" + "shortcut " + si.getId()); } } else { if (si.isDynamic()) { si.clearFlags(ShortcutInfo.FLAG_CACHED); } else { sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); } } } } packageShortcutsChanged(packageName, userId); verifyStates(); } @Override @Override public Intent[] createShortcutIntents(int launcherUserId, public Intent[] createShortcutIntents(int launcherUserId, @NonNull String callingPackage, @NonNull String callingPackage, Loading