diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl index c91334a5470fc..9d381ef586d48 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -26,29 +26,29 @@ import com.android.internal.infra.AndroidFuture; /** {@hide} */ interface IShortcutService { - oneway void setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, - int userId, in AndroidFuture callback); + AndroidFuture setDynamicShortcuts(String packageName, + in ParceledListSlice shortcutInfoList, int userId); - oneway void addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, - int userId, in AndroidFuture callback); + AndroidFuture addDynamicShortcuts(String packageName, + in ParceledListSlice shortcutInfoList, int userId); - oneway void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId); + AndroidFuture removeDynamicShortcuts(String packageName, in List shortcutIds, int userId); - oneway void removeAllDynamicShortcuts(String packageName, int userId); + AndroidFuture removeAllDynamicShortcuts(String packageName, int userId); - oneway void updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId, - in AndroidFuture callback); + AndroidFuture updateShortcuts(String packageName, in ParceledListSlice shortcuts, + int userId); - oneway void requestPinShortcut(String packageName, in ShortcutInfo shortcut, - in IntentSender resultIntent, int userId, in AndroidFuture callback); + AndroidFuture requestPinShortcut(String packageName, in ShortcutInfo shortcut, + in IntentSender resultIntent, int userId); - oneway void createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, - int userId, in AndroidFuture callback); + AndroidFuture createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, + int userId); - oneway void disableShortcuts(String packageName, in List shortcutIds, + AndroidFuture disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage, int disabledMessageResId, int userId); - oneway void enableShortcuts(String packageName, in List shortcutIds, int userId); + AndroidFuture enableShortcuts(String packageName, in List shortcutIds, int userId); int getMaxShortcutCountPerActivity(String packageName, int userId); @@ -58,31 +58,30 @@ interface IShortcutService { int getIconMaxDimensions(String packageName, int userId); - oneway void reportShortcutUsed(String packageName, String shortcutId, int userId); + AndroidFuture reportShortcutUsed(String packageName, String shortcutId, int userId); - oneway void resetThrottling(); // system only API for developer opsions + void resetThrottling(); // system only API for developer opsions - oneway void onApplicationActive(String packageName, int userId); // system only API for sysUI + AndroidFuture onApplicationActive(String packageName, int userId); // system only API for sysUI byte[] getBackupPayload(int user); - oneway void applyRestore(in byte[] payload, int user); + AndroidFuture applyRestore(in byte[] payload, int user); boolean isRequestPinItemSupported(int user, int requestType); // System API used by framework's ShareSheet (ChooserActivity) - oneway void getShareTargets(String packageName, in IntentFilter filter, int userId, - in AndroidFuture callback); + AndroidFuture getShareTargets(String packageName, in IntentFilter filter, + int userId); boolean hasShareTargets(String packageName, String packageToCheck, int userId); - oneway void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId); + AndroidFuture removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId); - oneway void getShortcuts(String packageName, int matchFlags, int userId, - in AndroidFuture> callback); + AndroidFuture getShortcuts(String packageName, int matchFlags, int userId); - oneway void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId); + AndroidFuture pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId); - oneway void updateShortcutVisibility(String callingPkg, String packageName, + AndroidFuture updateShortcutVisibility(String callingPkg, String packageName, in byte[] certificate, in boolean visible, int userId); } diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index f584ff33fa50d..2a36c11445c9f 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -145,14 +145,13 @@ public class ShortcutManager { */ @WorkerThread public boolean setDynamicShortcuts(@NonNull List shortcutInfoList) { - final AndroidFuture future = new AndroidFuture<>(); try { - mService.setDynamicShortcuts(mContext.getPackageName(), - new ParceledListSlice(shortcutInfoList), injectMyUserId(), future); + return ((boolean) getFutureOrThrow(mService.setDynamicShortcuts( + mContext.getPackageName(), new ParceledListSlice( + shortcutInfoList), injectMyUserId()))); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future); } /** @@ -167,14 +166,12 @@ public class ShortcutManager { @WorkerThread @NonNull public List getDynamicShortcuts() { - final AndroidFuture> future = new AndroidFuture<>(); try { - mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_DYNAMIC, injectMyUserId(), - future); + return getFutureOrThrow(mService.getShortcuts(mContext.getPackageName(), + FLAG_MATCH_DYNAMIC, injectMyUserId())).getList(); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future).getList(); } /** @@ -189,14 +186,12 @@ public class ShortcutManager { @WorkerThread @NonNull public List getManifestShortcuts() { - final AndroidFuture> future = new AndroidFuture<>(); try { - mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_MANIFEST, injectMyUserId(), - future); + return getFutureOrThrow(mService.getShortcuts(mContext.getPackageName(), + FLAG_MATCH_MANIFEST, injectMyUserId())).getList(); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future).getList(); } /** @@ -220,13 +215,12 @@ public class ShortcutManager { @WorkerThread @NonNull public List getShortcuts(@ShortcutMatchFlags int matchFlags) { - final AndroidFuture> future = new AndroidFuture<>(); try { - mService.getShortcuts(mContext.getPackageName(), matchFlags, injectMyUserId(), future); + return getFutureOrThrow(mService.getShortcuts(mContext.getPackageName(), matchFlags, + injectMyUserId())).getList(); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future).getList(); } /** @@ -244,14 +238,13 @@ public class ShortcutManager { */ @WorkerThread public boolean addDynamicShortcuts(@NonNull List shortcutInfoList) { - final AndroidFuture future = new AndroidFuture<>(); try { - mService.addDynamicShortcuts(mContext.getPackageName(), - new ParceledListSlice(shortcutInfoList), injectMyUserId(), future); + return (boolean) getFutureOrThrow(mService.addDynamicShortcuts( + mContext.getPackageName(), new ParceledListSlice(shortcutInfoList), + injectMyUserId())); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future); } /** @@ -261,8 +254,8 @@ public class ShortcutManager { */ public void removeDynamicShortcuts(@NonNull List shortcutIds) { try { - mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds, - injectMyUserId()); + getFutureOrThrow(mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds, + injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -275,7 +268,8 @@ public class ShortcutManager { */ public void removeAllDynamicShortcuts() { try { - mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId()); + getFutureOrThrow(mService.removeAllDynamicShortcuts(mContext.getPackageName(), + injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -288,8 +282,8 @@ public class ShortcutManager { */ public void removeLongLivedShortcuts(@NonNull List shortcutIds) { try { - mService.removeLongLivedShortcuts(mContext.getPackageName(), shortcutIds, - injectMyUserId()); + getFutureOrThrow(mService.removeLongLivedShortcuts(mContext.getPackageName(), + shortcutIds, injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -307,14 +301,12 @@ public class ShortcutManager { @WorkerThread @NonNull public List getPinnedShortcuts() { - final AndroidFuture> future = new AndroidFuture<>(); try { - mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED, injectMyUserId(), - future); + return getFutureOrThrow(mService.getShortcuts(mContext.getPackageName(), + FLAG_MATCH_PINNED, injectMyUserId())).getList(); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future).getList(); } /** @@ -331,14 +323,12 @@ public class ShortcutManager { */ @WorkerThread public boolean updateShortcuts(@NonNull List shortcutInfoList) { - final AndroidFuture future = new AndroidFuture<>(); try { - mService.updateShortcuts(mContext.getPackageName(), - new ParceledListSlice(shortcutInfoList), injectMyUserId(), future); + return (boolean) getFutureOrThrow(mService.updateShortcuts(mContext.getPackageName(), + new ParceledListSlice(shortcutInfoList), injectMyUserId())); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future); } /** @@ -352,9 +342,9 @@ public class ShortcutManager { */ public void disableShortcuts(@NonNull List shortcutIds) { try { - mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + getFutureOrThrow(mService.disableShortcuts(mContext.getPackageName(), shortcutIds, /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0, - injectMyUserId()); + injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -365,9 +355,9 @@ public class ShortcutManager { */ public void disableShortcuts(@NonNull List shortcutIds, int disabledMessageResId) { try { - mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + getFutureOrThrow(mService.disableShortcuts(mContext.getPackageName(), shortcutIds, /* disabledMessage =*/ null, disabledMessageResId, - injectMyUserId()); + injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -393,9 +383,9 @@ public class ShortcutManager { */ public void disableShortcuts(@NonNull List shortcutIds, CharSequence disabledMessage) { try { - mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + getFutureOrThrow(mService.disableShortcuts(mContext.getPackageName(), shortcutIds, disabledMessage, /* disabledMessageResId =*/ 0, - injectMyUserId()); + injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -411,7 +401,8 @@ public class ShortcutManager { */ public void enableShortcuts(@NonNull List shortcutIds) { try { - mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId()); + getFutureOrThrow(mService.enableShortcuts( + mContext.getPackageName(), shortcutIds, injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -532,8 +523,8 @@ public class ShortcutManager { */ public void reportShortcutUsed(String shortcutId) { try { - mService.reportShortcutUsed(mContext.getPackageName(), shortcutId, - injectMyUserId()); + getFutureOrThrow(mService.reportShortcutUsed(mContext.getPackageName(), shortcutId, + injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -610,14 +601,12 @@ public class ShortcutManager { @WorkerThread public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut, @Nullable IntentSender resultIntent) { - final AndroidFuture future = new AndroidFuture<>(); try { - mService.requestPinShortcut(mContext.getPackageName(), shortcut, - resultIntent, injectMyUserId(), future); + return (boolean) getFutureOrThrow(mService.requestPinShortcut(mContext.getPackageName(), + shortcut, resultIntent, injectMyUserId())); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future); } /** @@ -639,14 +628,12 @@ public class ShortcutManager { */ @WorkerThread public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) { - final AndroidFuture future = new AndroidFuture<>(); try { - mService.createShortcutResultIntent(mContext.getPackageName(), shortcut, - injectMyUserId(), future); + return getFutureOrThrow(mService.createShortcutResultIntent(mContext.getPackageName(), + shortcut, injectMyUserId())); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future); } /** @@ -659,7 +646,7 @@ public class ShortcutManager { */ public void onApplicationActive(@NonNull String packageName, @UserIdInt int userId) { try { - mService.onApplicationActive(packageName, userId); + getFutureOrThrow(mService.onApplicationActive(packageName, userId)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -684,13 +671,12 @@ public class ShortcutManager { @SystemApi @RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS) public List getShareTargets(@NonNull IntentFilter filter) { - final AndroidFuture future = new AndroidFuture<>(); try { - mService.getShareTargets(mContext.getPackageName(), filter, injectMyUserId(), future); + return getFutureOrThrow(mService.getShareTargets(mContext.getPackageName(), filter, + injectMyUserId())).getList(); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } - return getFutureOrThrow(future).getList(); } /** @@ -797,7 +783,8 @@ public class ShortcutManager { */ public void pushDynamicShortcut(@NonNull ShortcutInfo shortcut) { try { - mService.pushDynamicShortcut(mContext.getPackageName(), shortcut, injectMyUserId()); + getFutureOrThrow(mService.pushDynamicShortcut( + mContext.getPackageName(), shortcut, injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -813,8 +800,8 @@ public class ShortcutManager { public void updateShortcutVisibility(@NonNull final String packageName, @Nullable final byte[] certificate, final boolean visible) { try { - mService.updateShortcutVisibility(mContext.getPackageName(), packageName, certificate, - visible, injectMyUserId()); + getFutureOrThrow(mService.updateShortcutVisibility(mContext.getPackageName(), + packageName, certificate, visible, injectMyUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index ada65860018a6..83fe095769f06 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -158,6 +158,8 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String KEY_BITMAPS = "bitmaps"; private static final String KEY_BITMAP_BYTES = "bitmapBytes"; + private final Object mLock = new Object(); + /** * An temp in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs. */ @@ -168,6 +170,11 @@ class ShortcutPackage extends ShortcutPackageItem { */ private final ArrayList mShareTargets = new ArrayList<>(0); + /** + * All external packages that have gained access to the shortcuts from this package + */ + private final Map mPackageIdentifiers = new ArrayMap<>(0); + /** * # of times the package has called rate-limited APIs. */ @@ -182,13 +189,6 @@ class ShortcutPackage extends ShortcutPackageItem { private long mLastKnownForegroundElapsedTime; - private final Object mLock = new Object(); - - /** - * All external packages that have gained access to the shortcuts from this package - */ - private final Map mPackageIdentifiers = new ArrayMap<>(0); - private boolean mIsInitilized; private ShortcutPackage(ShortcutUser shortcutUser, diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index ee3335fba9152..a69e9db753e25 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1692,6 +1692,17 @@ public class ShortcutService extends IShortcutService.Stub { "Ephemeral apps can't use ShortcutManager"); } + private boolean verifyCaller(@NonNull String packageName, @UserIdInt int userId, + @NonNull AndroidFuture ret) { + try { + verifyCaller(packageName, userId); + } catch (Exception e) { + ret.completeExceptionally(e); + return false; + } + return true; + } + private void verifyShortcutInfoPackage(String callerPackage, ShortcutInfo si) { if (si == null) { return; @@ -1719,6 +1730,11 @@ public class ShortcutService extends IShortcutService.Stub { new Thread(r).start(); } + void injectPostToHandlerIfAppSearch(Runnable r) { + // TODO: move to background thread when app search is enabled. + r.run(); + } + /** * @throws IllegalArgumentException if {@code numShortcuts} is bigger than * {@link #getMaxActivityShortcuts()}. @@ -1904,351 +1920,400 @@ public class ShortcutService extends IShortcutService.Stub { // === APIs === @Override - public void setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, - @UserIdInt int userId, @NonNull AndroidFuture callback) { - try { - verifyCaller(packageName, userId); - - final List newShortcuts = (List) shortcutInfoList.getList(); - verifyShortcutInfoPackages(packageName, newShortcuts); - final int size = newShortcuts.size(); - - final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( - injectBinderCallingPid(), injectBinderCallingUid()); - - List changedShortcuts = null; - List removedShortcuts = null; - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, - userId); - - ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); - ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); - - fillInDefaultActivity(newShortcuts); - - ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET); - - // Throttling. - if (!ps.tryApiCall(unlimited)) { - callback.complete(false); - return; - } - - // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). - ps.clearAllImplicitRanks(); - assignImplicitRanks(newShortcuts); - - for (int i = 0; i < size; i++) { - fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false); - } - - ArrayList cachedOrPinned = new ArrayList<>(); - ps.findAll(cachedOrPinned, AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED, - (ShortcutInfo si) -> si.isVisibleToPublisher() - && si.isDynamic() && (si.isCached() || si.isPinned()), - ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); - - // First, remove all un-pinned and non-cached; dynamic shortcuts - removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); - - // Then, add/update all. We need to make sure to take over "pinned" flag. - for (int i = 0; i < size; i++) { - final ShortcutInfo newShortcut = newShortcuts.get(i); - ps.addOrReplaceDynamicShortcut(newShortcut); - } - - // Lastly, adjust the ranks. - ps.adjustRanks(); - - changedShortcuts = prepareChangedShortcuts( - cachedOrPinned, newShortcuts, removedShortcuts, ps); - } - - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); - - verifyStates(); - - callback.complete(true); - } catch (Exception e) { - callback.completeExceptionally(e); + public AndroidFuture setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, + @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } + final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( + injectBinderCallingPid(), injectBinderCallingUid()); + injectPostToHandlerIfAppSearch(() -> { + try { + final List newShortcuts = + (List) shortcutInfoList.getList(); + verifyShortcutInfoPackages(packageName, newShortcuts); + final int size = newShortcuts.size(); + + List changedShortcuts = null; + List removedShortcuts = null; + + synchronized (mLock) { + throwIfUserLockedL(userId); + + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); + + ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); + ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); + + fillInDefaultActivity(newShortcuts); + + ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET); + + // Throttling. + if (!ps.tryApiCall(unlimited)) { + ret.complete(false); + return; + } + + // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). + ps.clearAllImplicitRanks(); + assignImplicitRanks(newShortcuts); + + for (int i = 0; i < size; i++) { + fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false); + } + + ArrayList cachedOrPinned = new ArrayList<>(); + ps.findAll(cachedOrPinned, + AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED, + (ShortcutInfo si) -> si.isVisibleToPublisher() + && si.isDynamic() && (si.isCached() || si.isPinned()), + ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); + + // First, remove all un-pinned and non-cached; dynamic shortcuts + removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); + + // Then, add/update all. We need to make sure to take over "pinned" flag. + for (int i = 0; i < size; i++) { + final ShortcutInfo newShortcut = newShortcuts.get(i); + ps.addOrReplaceDynamicShortcut(newShortcut); + } + + // Lastly, adjust the ranks. + ps.adjustRanks(); + + changedShortcuts = prepareChangedShortcuts( + cachedOrPinned, newShortcuts, removedShortcuts, ps); + } + + + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + + verifyStates(); + } catch (Exception e) { + ret.completeExceptionally(e); + } + ret.complete(true); + }); + return ret; } @Override - public void updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, - @UserIdInt int userId, AndroidFuture callback) { - try { - verifyCaller(packageName, userId); - - final List newShortcuts = (List) shortcutInfoList.getList(); - verifyShortcutInfoPackages(packageName, newShortcuts); - final int size = newShortcuts.size(); - - final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( - injectBinderCallingPid(), injectBinderCallingUid()); - - final List changedShortcuts = new ArrayList<>(1); - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, - userId); - - ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); - ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); - - // For update, don't fill in the default activity. Having null activity means - // "don't update the activity" here. - - ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE); - - // Throttling. - if (!ps.tryApiCall(unlimited)) { - callback.complete(false); - return; - } - - // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). - ps.clearAllImplicitRanks(); - assignImplicitRanks(newShortcuts); - - for (int i = 0; i < size; i++) { - final ShortcutInfo source = newShortcuts.get(i); - fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); - - ps.mutateShortcut(source.getId(), null, target -> { - // Invisible shortcuts can't be updated. - if (target == null || !target.isVisibleToPublisher()) { - return; - } - - if (target.isEnabled() != source.isEnabled()) { - Slog.w(TAG, "ShortcutInfo.enabled cannot be changed with" - + " updateShortcuts()"); - } - - if (target.isLongLived() != source.isLongLived()) { - Slog.w(TAG, - "ShortcutInfo.longLived cannot be changed with" - + " updateShortcuts()"); - } - - // When updating the rank, we need to insert between existing ranks, so set - // this setRankChanged, and also copy the implicit rank fo adjustRanks(). - if (source.hasRank()) { - target.setRankChanged(); - target.setImplicitRank(source.getImplicitRank()); - } - - final boolean replacingIcon = (source.getIcon() != null); - if (replacingIcon) { - removeIconLocked(target); - } - - // Note copyNonNullFieldsFrom() does the "updatable with?" check too. - target.copyNonNullFieldsFrom(source); - target.setTimestamp(injectCurrentTimeMillis()); - - if (replacingIcon) { - saveIconAndFixUpShortcutLocked(target); - } - - // When we're updating any resource related fields, re-extract the res - // names and the values. - if (replacingIcon || source.hasStringResources()) { - fixUpShortcutResourceNamesAndValues(target); - } - - changedShortcuts.add(target); - }); - } - - // Lastly, adjust the ranks. - ps.adjustRanks(); - } - packageShortcutsChanged(packageName, userId, - changedShortcuts.isEmpty() ? null : changedShortcuts, null); - - verifyStates(); - - callback.complete(true); - } catch (Exception e) { - callback.completeExceptionally(e); + public AndroidFuture updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, + @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } + final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( + injectBinderCallingPid(), injectBinderCallingUid()); + injectPostToHandlerIfAppSearch(() -> { + try { + final List newShortcuts = + (List) shortcutInfoList.getList(); + verifyShortcutInfoPackages(packageName, newShortcuts); + final int size = newShortcuts.size(); + + final List changedShortcuts = new ArrayList<>(1); + + synchronized (mLock) { + throwIfUserLockedL(userId); + + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); + + ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); + ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); + + // For update, don't fill in the default activity. Having null activity means + // "don't update the activity" here. + + ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE); + + // Throttling. + if (!ps.tryApiCall(unlimited)) { + ret.complete(false); + return; + } + + // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). + ps.clearAllImplicitRanks(); + assignImplicitRanks(newShortcuts); + + for (int i = 0; i < size; i++) { + final ShortcutInfo source = newShortcuts.get(i); + fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); + + ps.mutateShortcut(source.getId(), null, target -> { + // Invisible shortcuts can't be updated. + if (target == null || !target.isVisibleToPublisher()) { + return; + } + + if (target.isEnabled() != source.isEnabled()) { + Slog.w(TAG, "ShortcutInfo.enabled cannot be changed with" + + " updateShortcuts()"); + } + + if (target.isLongLived() != source.isLongLived()) { + Slog.w(TAG, + "ShortcutInfo.longLived cannot be changed with" + + " updateShortcuts()"); + } + + // When updating the rank, we need to insert between existing ranks, + // so set this setRankChanged, and also copy the implicit rank fo + // adjustRanks(). + if (source.hasRank()) { + target.setRankChanged(); + target.setImplicitRank(source.getImplicitRank()); + } + + final boolean replacingIcon = (source.getIcon() != null); + if (replacingIcon) { + removeIconLocked(target); + } + + // Note copyNonNullFieldsFrom() does the "updatable with?" check too. + target.copyNonNullFieldsFrom(source); + target.setTimestamp(injectCurrentTimeMillis()); + + if (replacingIcon) { + saveIconAndFixUpShortcutLocked(target); + } + + // When we're updating any resource related fields, re-extract the res + // names and the values. + if (replacingIcon || source.hasStringResources()) { + fixUpShortcutResourceNamesAndValues(target); + } + + changedShortcuts.add(target); + }); + } + + // Lastly, adjust the ranks. + ps.adjustRanks(); + } + packageShortcutsChanged(packageName, userId, + changedShortcuts.isEmpty() ? null : changedShortcuts, null); + + verifyStates(); + + ret.complete(true); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } @Override - public void addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, - @UserIdInt int userId, AndroidFuture callback) { - try { - verifyCaller(packageName, userId); + public AndroidFuture addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, + @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; + } + final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( + injectBinderCallingPid(), injectBinderCallingUid()); + injectPostToHandlerIfAppSearch(() -> { + try { + final List newShortcuts = + (List) shortcutInfoList.getList(); + verifyShortcutInfoPackages(packageName, newShortcuts); + final int size = newShortcuts.size(); - final List newShortcuts = (List) shortcutInfoList.getList(); - verifyShortcutInfoPackages(packageName, newShortcuts); - final int size = newShortcuts.size(); + List changedShortcuts = null; - final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( - injectBinderCallingPid(), injectBinderCallingUid()); + synchronized (mLock) { + throwIfUserLockedL(userId); - List changedShortcuts = null; + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); - synchronized (mLock) { - throwIfUserLockedL(userId); + ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); + ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, - userId); + fillInDefaultActivity(newShortcuts); - ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); - ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); + ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD); - fillInDefaultActivity(newShortcuts); + // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). + ps.clearAllImplicitRanks(); + assignImplicitRanks(newShortcuts); - ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD); + // Throttling. + if (!ps.tryApiCall(unlimited)) { + ret.complete(false); + return; + } + for (int i = 0; i < size; i++) { + final ShortcutInfo newShortcut = newShortcuts.get(i); - // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). - ps.clearAllImplicitRanks(); - assignImplicitRanks(newShortcuts); + // Validate the shortcut. + fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false); - // Throttling. - if (!ps.tryApiCall(unlimited)) { - callback.complete(false); - return; + // When ranks are changing, we need to insert between ranks, so set the + // "rank changed" flag. + newShortcut.setRankChanged(); + + // Add it. + ps.addOrReplaceDynamicShortcut(newShortcut); + + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(newShortcut); + } + + // Lastly, adjust the ranks. + ps.adjustRanks(); } - for (int i = 0; i < size; i++) { - final ShortcutInfo newShortcut = newShortcuts.get(i); + packageShortcutsChanged(packageName, userId, changedShortcuts, null); + + verifyStates(); + + ret.complete(true); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; + } + + @Override + public AndroidFuture pushDynamicShortcut(String packageName, ShortcutInfo shortcut, + @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; + } + injectPostToHandlerIfAppSearch(() -> { + try { + verifyShortcutInfoPackage(packageName, shortcut); + + List changedShortcuts = new ArrayList<>(); + List removedShortcuts = null; + + synchronized (mLock) { + throwIfUserLockedL(userId); + + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); + + ps.ensureNotImmutable(shortcut.getId(), /*ignoreInvisible=*/ true); + fillInDefaultActivity(Arrays.asList(shortcut)); + + if (!shortcut.hasRank()) { + shortcut.setRank(0); + } + // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). + ps.clearAllImplicitRanks(); + shortcut.setImplicitRank(0); // Validate the shortcut. - fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false); + fixUpIncomingShortcutInfo(shortcut, /* forUpdate= */ false); // When ranks are changing, we need to insert between ranks, so set the // "rank changed" flag. - newShortcut.setRankChanged(); + shortcut.setRankChanged(); - // Add it. - ps.addOrReplaceDynamicShortcut(newShortcut); + // Push it. + boolean deleted = ps.pushDynamicShortcut(shortcut, changedShortcuts); - if (changedShortcuts == null) { - changedShortcuts = new ArrayList<>(1); + if (deleted) { + if (changedShortcuts.isEmpty()) { + ret.complete(null); + return; // Failed to push. + } + removedShortcuts = Collections.singletonList(changedShortcuts.get(0)); + changedShortcuts.clear(); } - changedShortcuts.add(newShortcut); + changedShortcuts.add(shortcut); + + // Lastly, adjust the ranks. + ps.adjustRanks(); } - // Lastly, adjust the ranks. - ps.adjustRanks(); + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + + verifyStates(); + + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); } - packageShortcutsChanged(packageName, userId, changedShortcuts, null); - - verifyStates(); - - callback.complete(true); - } catch (Exception e) { - callback.completeExceptionally(e); - } + }); + return ret; } @Override - public void pushDynamicShortcut(String packageName, ShortcutInfo shortcut, - @UserIdInt int userId) { - verifyCaller(packageName, userId); - verifyShortcutInfoPackage(packageName, shortcut); - - List changedShortcuts = new ArrayList<>(); - List removedShortcuts = null; - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); - - ps.ensureNotImmutable(shortcut.getId(), /*ignoreInvisible=*/ true); - fillInDefaultActivity(Arrays.asList(shortcut)); - - if (!shortcut.hasRank()) { - shortcut.setRank(0); - } - // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). - ps.clearAllImplicitRanks(); - shortcut.setImplicitRank(0); - - // Validate the shortcut. - fixUpIncomingShortcutInfo(shortcut, /* forUpdate= */ false); - - // When ranks are changing, we need to insert between ranks, so set the - // "rank changed" flag. - shortcut.setRankChanged(); - - // Push it. - boolean deleted = ps.pushDynamicShortcut(shortcut, changedShortcuts); - - if (deleted) { - if (changedShortcuts.isEmpty()) { - return; // Failed to push. + public AndroidFuture updateShortcutVisibility(String callingPkg, String packageName, + byte[] certificate, boolean visible, int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + injectPostToHandlerIfAppSearch(() -> { + try { + synchronized (mLock) { + getPackageShortcutsForPublisherLocked(callingPkg, userId) + .updateVisibility(packageName, certificate, visible); } - removedShortcuts = Collections.singletonList(changedShortcuts.get(0)); - changedShortcuts.clear(); + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); } - changedShortcuts.add(shortcut); - - // Lastly, adjust the ranks. - ps.adjustRanks(); - } - - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); - - verifyStates(); + }); + return ret; } @Override - public void updateShortcutVisibility(String callingPkg, String packageName, byte[] certificate, - boolean visible, int userId) { - synchronized (mLock) { - getPackageShortcutsForPublisherLocked(callingPkg, userId) - .updateVisibility(packageName, certificate, visible); - } - } - - @Override - public void requestPinShortcut(String packageName, ShortcutInfo shortcut, - IntentSender resultIntent, int userId, AndroidFuture callback) { - try { - Objects.requireNonNull(shortcut); - Objects.requireNonNull(callback); - Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); - callback.complete( - requestPinItem(packageName, userId, shortcut, null, null, resultIntent)); - } catch (Exception e) { - callback.completeExceptionally(e); - } - } - - @Override - public void createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId, - AndroidFuture callback) - throws RemoteException { - try { - Objects.requireNonNull(shortcut); - Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); - verifyCaller(packageName, userId); - verifyShortcutInfoPackage(packageName, shortcut); - - final Intent ret; - synchronized (mLock) { - throwIfUserLockedL(userId); - - // Send request to the launcher, if supported. - ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId); + public AndroidFuture requestPinShortcut(String packageName, ShortcutInfo shortcut, + IntentSender resultIntent, int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + final int callingPid = injectBinderCallingPid(); + final int callingUid = injectBinderCallingUid(); + injectPostToHandlerIfAppSearch(() -> { + try { + ret.complete( + requestPinItem(packageName, userId, shortcut, null, null, resultIntent, + callingPid, callingUid)); + } catch (Exception e) { + ret.completeExceptionally(e); } + }); + return ret; + } - verifyStates(); - callback.complete(ret); - } catch (Exception e) { - callback.completeExceptionally(e); + @Override + public AndroidFuture createShortcutResultIntent( + String packageName, ShortcutInfo shortcut, int userId) throws RemoteException { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } + injectPostToHandlerIfAppSearch(() -> { + try { + Objects.requireNonNull(shortcut); + Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); + verifyShortcutInfoPackage(packageName, shortcut); + final Intent intent; + synchronized (mLock) { + throwIfUserLockedL(userId); + + // Send request to the launcher, if supported. + intent = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, + userId); + } + + verifyStates(); + ret.complete(intent); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } /** @@ -2258,9 +2323,16 @@ public class ShortcutService extends IShortcutService.Stub { */ private boolean requestPinItem(String callingPackage, int userId, ShortcutInfo shortcut, AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent) { + return requestPinItem(callingPackage, userId, shortcut, appWidget, extras, resultIntent, + injectBinderCallingPid(), injectBinderCallingUid()); + } + + private boolean requestPinItem(String callingPackage, int userId, ShortcutInfo shortcut, + AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent, + int callingPid, int callingUid) { verifyCaller(callingPackage, userId); if (shortcut == null || !injectHasAccessShortcutsPermission( - injectBinderCallingPid(), injectBinderCallingUid())) { + callingPid, callingUid)) { // Verify if caller is the shortcut owner, only if caller doesn't have ACCESS_SHORTCUTS. verifyShortcutInfoPackage(callingPackage, shortcut); } @@ -2269,7 +2341,7 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { throwIfUserLockedL(userId); - Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()), + Preconditions.checkState(isUidForegroundLocked(callingUid), "Calling application must have a foreground activity or a foreground service"); // If it's a pin shortcut request, and there's already a shortcut with the same ID @@ -2301,247 +2373,327 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public void disableShortcuts(String packageName, List shortcutIds, + public AndroidFuture disableShortcuts(String packageName, List shortcutIds, CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) { - verifyCaller(packageName, userId); - Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); - - List changedShortcuts = null; - List removedShortcuts = null; - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); - - ps.ensureImmutableShortcutsNotIncludedWithIds((List) shortcutIds, - /*ignoreInvisible=*/ true); - - final String disabledMessageString = - (disabledMessage == null) ? null : disabledMessage.toString(); - - for (int i = shortcutIds.size() - 1; i >= 0; i--) { - final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)); - if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { - continue; - } - - final ShortcutInfo deleted = ps.disableWithId(id, - disabledMessageString, disabledMessageResId, - /* overrideImmutable=*/ false, /*ignoreInvisible=*/ true, - ShortcutInfo.DISABLED_REASON_BY_APP); - - if (deleted == null) { - if (changedShortcuts == null) { - changedShortcuts = new ArrayList<>(1); - } - changedShortcuts.add(ps.findShortcutById(id)); - } else { - if (removedShortcuts == null) { - removedShortcuts = new ArrayList<>(1); - } - removedShortcuts.add(deleted); - } - } - - // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. - ps.adjustRanks(); + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } + injectPostToHandlerIfAppSearch(() -> { + try { + Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); + List changedShortcuts = null; + List removedShortcuts = null; - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + synchronized (mLock) { + throwIfUserLockedL(userId); - verifyStates(); + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); + + ps.ensureImmutableShortcutsNotIncludedWithIds((List) shortcutIds, + /*ignoreInvisible=*/ true); + + final String disabledMessageString = + (disabledMessage == null) ? null : disabledMessage.toString(); + + for (int i = shortcutIds.size() - 1; i >= 0; i--) { + final String id = Preconditions.checkStringNotEmpty( + (String) shortcutIds.get(i)); + if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { + continue; + } + + final ShortcutInfo deleted = ps.disableWithId(id, + disabledMessageString, disabledMessageResId, + /* overrideImmutable=*/ false, /*ignoreInvisible=*/ true, + ShortcutInfo.DISABLED_REASON_BY_APP); + + if (deleted == null) { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(ps.findShortcutById(id)); + } else { + if (removedShortcuts == null) { + removedShortcuts = new ArrayList<>(1); + } + removedShortcuts.add(deleted); + } + } + + // We may have removed dynamic shortcuts which may have left a gap, + // so adjust the ranks. + ps.adjustRanks(); + } + + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + + verifyStates(); + + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } @Override - public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { - verifyCaller(packageName, userId); - Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); - - List changedShortcuts = null; - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); - - ps.ensureImmutableShortcutsNotIncludedWithIds((List) shortcutIds, - /*ignoreInvisible=*/ true); - - for (int i = shortcutIds.size() - 1; i >= 0; i--) { - final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)); - if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { - continue; - } - ps.enableWithId(id); - - if (changedShortcuts == null) { - changedShortcuts = new ArrayList<>(1); - } - changedShortcuts.add(ps.findShortcutById(id)); - } + public AndroidFuture enableShortcuts( + String packageName, List shortcutIds, @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } + injectPostToHandlerIfAppSearch(() -> { + try { + Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); + List changedShortcuts = null; - packageShortcutsChanged(packageName, userId, changedShortcuts, null); + synchronized (mLock) { + throwIfUserLockedL(userId); - verifyStates(); + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); + + ps.ensureImmutableShortcutsNotIncludedWithIds((List) shortcutIds, + /*ignoreInvisible=*/ true); + + for (int i = shortcutIds.size() - 1; i >= 0; i--) { + final String id = Preconditions.checkStringNotEmpty( + (String) shortcutIds.get(i)); + if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { + continue; + } + ps.enableWithId(id); + + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(ps.findShortcutById(id)); + } + } + + packageShortcutsChanged(packageName, userId, changedShortcuts, null); + + verifyStates(); + + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } @Override - public void removeDynamicShortcuts(String packageName, List shortcutIds, + public AndroidFuture removeDynamicShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { - verifyCaller(packageName, userId); - Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; + } + injectPostToHandlerIfAppSearch(() -> { + try { + Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); + List changedShortcuts = null; + List removedShortcuts = null; - List changedShortcuts = null; - List removedShortcuts = null; + synchronized (mLock) { + throwIfUserLockedL(userId); - synchronized (mLock) { - throwIfUserLockedL(userId); + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); + ps.ensureImmutableShortcutsNotIncludedWithIds((List) shortcutIds, + /*ignoreInvisible=*/ true); - ps.ensureImmutableShortcutsNotIncludedWithIds((List) shortcutIds, - /*ignoreInvisible=*/ true); + for (int i = shortcutIds.size() - 1; i >= 0; i--) { + final String id = Preconditions.checkStringNotEmpty( + (String) shortcutIds.get(i)); + if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { + continue; + } - for (int i = shortcutIds.size() - 1; i >= 0; i--) { - final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)); - if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { - continue; - } - - ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true); - if (removed == null) { - if (changedShortcuts == null) { - changedShortcuts = new ArrayList<>(1); + ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ + true); + if (removed == null) { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(ps.findShortcutById(id)); + } else { + if (removedShortcuts == null) { + removedShortcuts = new ArrayList<>(1); + } + removedShortcuts.add(removed); + } } - changedShortcuts.add(ps.findShortcutById(id)); - } else { - if (removedShortcuts == null) { - removedShortcuts = new ArrayList<>(1); - } - removedShortcuts.add(removed); + + // We may have removed dynamic shortcuts which may have left a gap, + // so adjust the ranks. + ps.adjustRanks(); } + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + + verifyStates(); + + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); } - - // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. - ps.adjustRanks(); - } - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); - - verifyStates(); + }); + return ret; } @Override - public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { - verifyCaller(packageName, userId); - - List changedShortcuts = new ArrayList<>(); - List removedShortcuts = null; - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); - - // Dynamic shortcuts that are either cached or pinned will not get deleted. - ps.findAll(changedShortcuts, AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED, - (ShortcutInfo si) -> si.isVisibleToPublisher() - && si.isDynamic() && (si.isCached() || si.isPinned()), - ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); - - removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); - changedShortcuts = prepareChangedShortcuts( - changedShortcuts, null, removedShortcuts, ps); + public AndroidFuture removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } + injectPostToHandlerIfAppSearch(() -> { + try { + List changedShortcuts = new ArrayList<>(); + List removedShortcuts = null; - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + synchronized (mLock) { + throwIfUserLockedL(userId); - verifyStates(); + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); + + // Dynamic shortcuts that are either cached or pinned will not get deleted. + ps.findAll(changedShortcuts, + AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED, + (ShortcutInfo si) -> si.isVisibleToPublisher() + && si.isDynamic() && (si.isCached() || si.isPinned()), + ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); + + removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); + changedShortcuts = prepareChangedShortcuts( + changedShortcuts, null, removedShortcuts, ps); + } + + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + + verifyStates(); + + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } @Override - public void removeLongLivedShortcuts(String packageName, List shortcutIds, + public AndroidFuture removeLongLivedShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { - verifyCaller(packageName, userId); - Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); - - List changedShortcuts = null; - List removedShortcuts = null; - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); - - ps.ensureImmutableShortcutsNotIncludedWithIds((List) shortcutIds, - /*ignoreInvisible=*/ true); - - for (int i = shortcutIds.size() - 1; i >= 0; i--) { - final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)); - if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { - continue; - } - - ShortcutInfo removed = ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); - if (removed != null) { - if (removedShortcuts == null) { - removedShortcuts = new ArrayList<>(1); - } - removedShortcuts.add(removed); - } else { - if (changedShortcuts == null) { - changedShortcuts = new ArrayList<>(1); - } - changedShortcuts.add(ps.findShortcutById(id)); - } - } - - // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. - ps.adjustRanks(); + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + injectPostToHandlerIfAppSearch(() -> { + try { + Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); + List changedShortcuts = null; + List removedShortcuts = null; - verifyStates(); + synchronized (mLock) { + throwIfUserLockedL(userId); + + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); + + ps.ensureImmutableShortcutsNotIncludedWithIds((List) shortcutIds, + /*ignoreInvisible=*/ true); + + for (int i = shortcutIds.size() - 1; i >= 0; i--) { + final String id = Preconditions.checkStringNotEmpty( + (String) shortcutIds.get(i)); + if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { + continue; + } + + ShortcutInfo removed = ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ + true); + if (removed != null) { + if (removedShortcuts == null) { + removedShortcuts = new ArrayList<>(1); + } + removedShortcuts.add(removed); + } else { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(ps.findShortcutById(id)); + } + } + + // We may have removed dynamic shortcuts which may have left a gap, + // so adjust the ranks. + ps.adjustRanks(); + } + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + + verifyStates(); + + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } @Override - public void getShortcuts(String packageName, - @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId, - AndroidFuture> callback) { - try { - verifyCaller(packageName, userId); - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0; - final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0; - final boolean matchManifest = - (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0; - final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0; - - final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0) - | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0) - | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0) - | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0); - - final String query = AppSearchShortcutInfo.QUERY_IS_VISIBLE_TO_PUBLISHER + " " - + createQuery(matchDynamic, matchPinned, matchManifest, matchCached); - - callback.complete(getShortcutsWithQueryLocked( - packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, query, - (ShortcutInfo si) -> - si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0)); - } - } catch (Exception e) { - callback.completeExceptionally(e); + public AndroidFuture getShortcuts(String packageName, + @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } + injectPostToHandlerIfAppSearch(() -> { + try { + synchronized (mLock) { + throwIfUserLockedL(userId); + + final boolean matchDynamic = + (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0; + final boolean matchPinned = + (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0; + final boolean matchManifest = + (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0; + final boolean matchCached = + (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0; + + final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0) + | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0) + | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0) + | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0); + + final String query = AppSearchShortcutInfo.QUERY_IS_VISIBLE_TO_PUBLISHER + " " + + createQuery(matchDynamic, matchPinned, matchManifest, matchCached); + + ret.complete(getShortcutsWithQueryLocked( + packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, query, + (ShortcutInfo si) -> + si.isVisibleToPublisher() + && (si.getFlags() & shortcutFlags) != 0)); + } + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } @Override - public void getShareTargets(String packageName, IntentFilter filter, @UserIdInt int userId, - AndroidFuture callback) { + public AndroidFuture getShareTargets( + String packageName, IntentFilter filter, @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); try { Preconditions.checkStringNotEmpty(packageName, "packageName"); Objects.requireNonNull(filter, "intentFilter"); @@ -2549,21 +2701,29 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, "getShareTargets"); - - synchronized (mLock) { - throwIfUserLockedL(userId); - - final List shortcutInfoList = new ArrayList<>(); - - final ShortcutUser user = getUserShortcutsLocked(userId); - user.forAllPackages( - p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter))); - - callback.complete(new ParceledListSlice<>(shortcutInfoList)); - } } catch (Exception e) { - callback.completeExceptionally(e); + ret.completeExceptionally(e); + return ret; } + injectPostToHandlerIfAppSearch(() -> { + try { + synchronized (mLock) { + throwIfUserLockedL(userId); + + final List shortcutInfoList = + new ArrayList<>(); + + final ShortcutUser user = getUserShortcutsLocked(userId); + user.forAllPackages( + p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter))); + + ret.complete(new ParceledListSlice<>(shortcutInfoList)); + } + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } @Override @@ -2660,34 +2820,48 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public void reportShortcutUsed(String packageName, String shortcutId, int userId) { - verifyCaller(packageName, userId); - - Objects.requireNonNull(shortcutId); - - if (DEBUG) { - Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d", - shortcutId, packageName, userId)); + public AndroidFuture reportShortcutUsed(String packageName, String shortcutId, int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + if (!verifyCaller(packageName, userId, ret)) { + return ret; } + injectPostToHandlerIfAppSearch(() -> { + try { + Objects.requireNonNull(shortcutId); - synchronized (mLock) { - throwIfUserLockedL(userId); + if (DEBUG) { + Slog.d(TAG, String.format( + "reportShortcutUsed: Shortcut %s package %s used on user %d", + shortcutId, packageName, userId)); + } - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); + synchronized (mLock) { + throwIfUserLockedL(userId); - if (ps.findShortcutById(shortcutId) == null) { - Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s", - packageName, shortcutId)); - return; + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); + + if (ps.findShortcutById(shortcutId) == null) { + Log.w(TAG, String.format( + "reportShortcutUsed: package %s doesn't have shortcut %s", + packageName, shortcutId)); + ret.complete(false); + return; + } + } + + final long token = injectClearCallingIdentity(); + try { + mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId); + } finally { + injectRestoreCallingIdentity(token); + } + ret.complete(true); + } catch (Exception e) { + ret.completeExceptionally(e); } - } - - final long token = injectClearCallingIdentity(); - try { - mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId); - } finally { - injectRestoreCallingIdentity(token); - } + }); + return ret; } @Override @@ -2734,22 +2908,36 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public void onApplicationActive(String packageName, int userId) { + public AndroidFuture onApplicationActive(String packageName, int userId) { + final AndroidFuture ret = new AndroidFuture<>(); if (DEBUG) { Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId); } - enforceResetThrottlingPermission(); - - synchronized (mLock) { - if (!isUserUnlockedL(userId)) { - // This is called by system UI, so no need to throw. Just ignore. - return; - } - - getPackageShortcutsLocked(packageName, userId) - .resetRateLimitingForCommandLineNoSaving(); - saveUserLocked(userId); + try { + enforceResetThrottlingPermission(); + } catch (Exception e) { + ret.completeExceptionally(e); + return ret; } + injectPostToHandlerIfAppSearch(() -> { + try { + synchronized (mLock) { + if (!isUserUnlockedL(userId)) { + // This is called by system UI, so no need to throw. Just ignore. + ret.complete(null); + return; + } + + getPackageShortcutsLocked(packageName, userId) + .resetRateLimitingForCommandLineNoSaving(); + saveUserLocked(userId); + } + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } // We override this method in unit tests to do a simpler check. @@ -3203,9 +3391,8 @@ public class ShortcutService extends IShortcutService.Stub { @Override public List getShareTargets( @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId) { - final AndroidFuture future = new AndroidFuture<>(); - ShortcutService.this.getShareTargets( - callingPackage, intentFilter, userId, future); + final AndroidFuture future = ShortcutService.this.getShareTargets( + callingPackage, intentFilter, userId); try { return future.get().getList(); } catch (InterruptedException | ExecutionException e) { @@ -4331,56 +4518,72 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public void applyRestore(byte[] payload, @UserIdInt int userId) { - enforceSystem(); - if (DEBUG || DEBUG_REBOOT) { - Slog.d(TAG, "Restoring user " + userId); + public AndroidFuture applyRestore(byte[] payload, @UserIdInt int userId) { + final AndroidFuture ret = new AndroidFuture<>(); + try { + enforceSystem(); + } catch (Exception e) { + ret.completeExceptionally(e); + return ret; } - synchronized (mLock) { - if (!isUserUnlockedL(userId)) { - wtf("Can't restore: user " + userId + " is locked or not running"); - return; - } - - // Note we print the file timestamps in dumpsys too, but also printing the timestamp - // in the files anyway. - mShortcutDumpFiles.save("restore-0-start.txt", pw -> { - pw.print("Start time: "); - dumpCurrentTime(pw); - pw.println(); - }); - mShortcutDumpFiles.save("restore-1-payload.xml", payload); - - // Actually do restore. - final ShortcutUser restored; - final ByteArrayInputStream is = new ByteArrayInputStream(payload); + injectPostToHandler(() -> { try { - restored = loadUserInternal(userId, is, /* fromBackup */ true); - } catch (XmlPullParserException | IOException | InvalidFileFormatException e) { - Slog.w(TAG, "Restoration failed.", e); - return; - } - mShortcutDumpFiles.save("restore-2.txt", this::dumpInner); + if (DEBUG || DEBUG_REBOOT) { + Slog.d(TAG, "Restoring user " + userId); + } + synchronized (mLock) { + if (!isUserUnlockedL(userId)) { + wtf("Can't restore: user " + userId + " is locked or not running"); + ret.complete(null); + return; + } - getUserShortcutsLocked(userId).mergeRestoredFile(restored); + // Note we print the file timestamps in dumpsys too, but also printing the + // timestamp in the files anyway. + mShortcutDumpFiles.save("restore-0-start.txt", pw -> { + pw.print("Start time: "); + dumpCurrentTime(pw); + pw.println(); + }); + mShortcutDumpFiles.save("restore-1-payload.xml", payload); - mShortcutDumpFiles.save("restore-3.txt", this::dumpInner); + // Actually do restore. + final ShortcutUser restored; + final ByteArrayInputStream is = new ByteArrayInputStream(payload); + try { + restored = loadUserInternal(userId, is, /* fromBackup */ true); + } catch (XmlPullParserException | IOException | InvalidFileFormatException e) { + Slog.w(TAG, "Restoration failed.", e); + ret.complete(null); + return; + } + mShortcutDumpFiles.save("restore-2.txt", this::dumpInner); - // Rescan all packages to re-publish manifest shortcuts and do other checks. - rescanUpdatedPackagesLocked(userId, - 0 // lastScanTime = 0; rescan all packages. + getUserShortcutsLocked(userId).mergeRestoredFile(restored); + + mShortcutDumpFiles.save("restore-3.txt", this::dumpInner); + + // Rescan all packages to re-publish manifest shortcuts and do other checks. + rescanUpdatedPackagesLocked(userId, + 0 // lastScanTime = 0; rescan all packages. ); - mShortcutDumpFiles.save("restore-4.txt", this::dumpInner); + mShortcutDumpFiles.save("restore-4.txt", this::dumpInner); - mShortcutDumpFiles.save("restore-5-finish.txt", pw -> { - pw.print("Finish time: "); - dumpCurrentTime(pw); - pw.println(); - }); + mShortcutDumpFiles.save("restore-5-finish.txt", pw -> { + pw.print("Finish time: "); + dumpCurrentTime(pw); + pw.println(); + }); - saveUserLocked(userId); - } + saveUserLocked(userId); + } + ret.complete(null); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + return ret; } // === Dump ===