From 41cce320805dbcc1d928a6717640ac5692bd5538 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Fri, 17 Sep 2021 23:09:29 +0000 Subject: [PATCH] Remove AndroidFuture from ShortcutService's internal api AndroidFuture is not meant to be used as a return type, this CL removes the cases where AndroidFuture is used as a return type. After closely scrutinize some of the api are not required to be async in system level to support AppSearch integration in the future. Here's the list of API that can was previously made async at service-level but could actually stay synchronous: - setDynamicShortcuts/addDynamicShortcuts: Dynamic shortcuts are stored in system ram only unless it is also a long-lived shortcut, yet long-lived shortcuts are persisted into AppSearch in a separate background thread, client process should not need to wait for writing to AppSearch to finish. - updateShortcuts: Long-lived shortcuts that lives in AppSearch might be updated by this api, but similar to setDynamicShortcuts/addDynamicShortcuts client process is not required to wait for writing to AppSearch to finish. - removeDynamicShortcuts/removeAllDynamicShortcuts: The method signature doesn't return anything in the first place, so client process is not required to wait for AppSearch. - disableShortcuts/enableShortcuts: Similarily, since the method signature doesn't return anything, client process is not required to wait for AppSearch. - reportShortcutUsed/onApplicationActive/applyRestore: No return type. - removeLongLivedShortcut: The operation itself should be async, but since it has no return type, client process is not required to wait. API that should become async at service level but remain sync in client process are: - requestPinShortcut: To support drag and drop from AllApps+, client process should be waiting for the pin request to complete since long-lived shortcuts that doesn't exists in system memory might still be surfaced in AllApps+. Since read from AppSearch is async, the client process will need to wait for the read to finish synchrounously. - createShortcutResultIntent: Similarily, a client process might call this api to launch a long-lived shortcut that doesn't exists in system memory. So the client process needs to wait for read from AppSearch to finish. API that should be async but is not covered in this CL - getShareTargets: Sharing shortcuts could be potentially backed up long-lived shortcuts that doesn't live in system ram, but this api is already being used by PeopleService, thus should be covered in a separate CL as it would need to be reviewed by another team. Bug: 197277083 Test: atest ShortcutManagerTest1 ShortcutManagerTest2 ShortcutManagerTest3 ShortcutManagerTest4 ShortcutManagerTest5 ShortcutManagerTest6 ShortcutManagerTest7 ShortcutManagerTest8 ShortcutManagerTest9 ShortcutManagerTest10 ShortcutManagerTest11 Test: atest CtsShortcutManagerTestCases Change-Id: I8970cc77073d329fe00a349d9fa190f917b7851d --- .../android/content/pm/IShortcutService.aidl | 48 +- .../android/content/pm/ShortcutManager.java | 80 +- .../android/server/pm/ShortcutService.java | 1263 +++++++---------- 3 files changed, 549 insertions(+), 842 deletions(-) diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl index 804a06bdaae9b..c9735b05cba48 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -26,29 +26,28 @@ import com.android.internal.infra.AndroidFuture; /** {@hide} */ interface IShortcutService { - AndroidFuture setDynamicShortcuts(String packageName, - in ParceledListSlice shortcutInfoList, int userId); - - AndroidFuture addDynamicShortcuts(String packageName, - in ParceledListSlice shortcutInfoList, int userId); - - AndroidFuture removeDynamicShortcuts(String packageName, in List shortcutIds, int userId); - - AndroidFuture removeAllDynamicShortcuts(String packageName, int userId); - - AndroidFuture updateShortcuts(String packageName, in ParceledListSlice shortcuts, + boolean setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, int userId); - AndroidFuture requestPinShortcut(String packageName, in ShortcutInfo shortcut, - in IntentSender resultIntent, int userId); - - AndroidFuture createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, + boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, int userId); - AndroidFuture disableShortcuts(String packageName, in List shortcutIds, + void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId); + + void removeAllDynamicShortcuts(String packageName, int userId); + + boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId); + + void requestPinShortcut(String packageName, in ShortcutInfo shortcut, + in IntentSender resultIntent, int userId, in AndroidFuture ret); + + void createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, int userId, + in AndroidFuture ret); + + void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage, int disabledMessageResId, int userId); - AndroidFuture enableShortcuts(String packageName, in List shortcutIds, int userId); + void enableShortcuts(String packageName, in List shortcutIds, int userId); int getMaxShortcutCountPerActivity(String packageName, int userId); @@ -58,27 +57,26 @@ interface IShortcutService { int getIconMaxDimensions(String packageName, int userId); - AndroidFuture reportShortcutUsed(String packageName, String shortcutId, int userId); + void reportShortcutUsed(String packageName, String shortcutId, int userId); void resetThrottling(); // system only API for developer opsions - AndroidFuture onApplicationActive(String packageName, int userId); // system only API for sysUI + void onApplicationActive(String packageName, int userId); // system only API for sysUI byte[] getBackupPayload(int user); - AndroidFuture applyRestore(in byte[] payload, int user); + void applyRestore(in byte[] payload, int user); boolean isRequestPinItemSupported(int user, int requestType); // System API used by framework's ShareSheet (ChooserActivity) - AndroidFuture getShareTargets(String packageName, in IntentFilter filter, - int userId); + ParceledListSlice getShareTargets(String packageName, in IntentFilter filter, int userId); boolean hasShareTargets(String packageName, String packageToCheck, int userId); - AndroidFuture removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId); + void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId); - AndroidFuture getShortcuts(String packageName, int matchFlags, int userId); + ParceledListSlice getShortcuts(String packageName, int matchFlags, int userId); - AndroidFuture pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId); + void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId); } diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index d77fa91701df5..be0d934f51333 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -145,9 +145,8 @@ public class ShortcutManager { @WorkerThread public boolean setDynamicShortcuts(@NonNull List shortcutInfoList) { try { - return ((boolean) getFutureOrThrow(mService.setDynamicShortcuts( - mContext.getPackageName(), new ParceledListSlice( - shortcutInfoList), injectMyUserId()))); + return mService.setDynamicShortcuts(mContext.getPackageName(), new ParceledListSlice( + shortcutInfoList), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -166,8 +165,8 @@ public class ShortcutManager { @NonNull public List getDynamicShortcuts() { try { - return getFutureOrThrow(mService.getShortcuts(mContext.getPackageName(), - FLAG_MATCH_DYNAMIC, injectMyUserId())).getList(); + return mService.getShortcuts(mContext.getPackageName(), + FLAG_MATCH_DYNAMIC, injectMyUserId()).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -186,8 +185,8 @@ public class ShortcutManager { @NonNull public List getManifestShortcuts() { try { - return getFutureOrThrow(mService.getShortcuts(mContext.getPackageName(), - FLAG_MATCH_MANIFEST, injectMyUserId())).getList(); + return mService.getShortcuts(mContext.getPackageName(), + FLAG_MATCH_MANIFEST, injectMyUserId()).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -215,8 +214,8 @@ public class ShortcutManager { @NonNull public List getShortcuts(@ShortcutMatchFlags int matchFlags) { try { - return getFutureOrThrow(mService.getShortcuts(mContext.getPackageName(), matchFlags, - injectMyUserId())).getList(); + return mService.getShortcuts(mContext.getPackageName(), matchFlags, + injectMyUserId()).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -238,9 +237,8 @@ public class ShortcutManager { @WorkerThread public boolean addDynamicShortcuts(@NonNull List shortcutInfoList) { try { - return (boolean) getFutureOrThrow(mService.addDynamicShortcuts( - mContext.getPackageName(), new ParceledListSlice(shortcutInfoList), - injectMyUserId())); + return mService.addDynamicShortcuts(mContext.getPackageName(), + new ParceledListSlice(shortcutInfoList), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -253,8 +251,8 @@ public class ShortcutManager { */ public void removeDynamicShortcuts(@NonNull List shortcutIds) { try { - getFutureOrThrow(mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds, - injectMyUserId())); + mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds, + injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -267,8 +265,7 @@ public class ShortcutManager { */ public void removeAllDynamicShortcuts() { try { - getFutureOrThrow(mService.removeAllDynamicShortcuts(mContext.getPackageName(), - injectMyUserId())); + mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -281,8 +278,8 @@ public class ShortcutManager { */ public void removeLongLivedShortcuts(@NonNull List shortcutIds) { try { - getFutureOrThrow(mService.removeLongLivedShortcuts(mContext.getPackageName(), - shortcutIds, injectMyUserId())); + mService.removeLongLivedShortcuts(mContext.getPackageName(), shortcutIds, + injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -301,8 +298,8 @@ public class ShortcutManager { @NonNull public List getPinnedShortcuts() { try { - return getFutureOrThrow(mService.getShortcuts(mContext.getPackageName(), - FLAG_MATCH_PINNED, injectMyUserId())).getList(); + return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED, + injectMyUserId()).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -323,8 +320,8 @@ public class ShortcutManager { @WorkerThread public boolean updateShortcuts(@NonNull List shortcutInfoList) { try { - return (boolean) getFutureOrThrow(mService.updateShortcuts(mContext.getPackageName(), - new ParceledListSlice(shortcutInfoList), injectMyUserId())); + return mService.updateShortcuts(mContext.getPackageName(), + new ParceledListSlice(shortcutInfoList), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -341,9 +338,9 @@ public class ShortcutManager { */ public void disableShortcuts(@NonNull List shortcutIds) { try { - getFutureOrThrow(mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0, - injectMyUserId())); + injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -354,9 +351,9 @@ public class ShortcutManager { */ public void disableShortcuts(@NonNull List shortcutIds, int disabledMessageResId) { try { - getFutureOrThrow(mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, /* disabledMessage =*/ null, disabledMessageResId, - injectMyUserId())); + injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -382,9 +379,9 @@ public class ShortcutManager { */ public void disableShortcuts(@NonNull List shortcutIds, CharSequence disabledMessage) { try { - getFutureOrThrow(mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, disabledMessage, /* disabledMessageResId =*/ 0, - injectMyUserId())); + injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -400,8 +397,7 @@ public class ShortcutManager { */ public void enableShortcuts(@NonNull List shortcutIds) { try { - getFutureOrThrow(mService.enableShortcuts( - mContext.getPackageName(), shortcutIds, injectMyUserId())); + mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -522,8 +518,7 @@ public class ShortcutManager { */ public void reportShortcutUsed(String shortcutId) { try { - getFutureOrThrow(mService.reportShortcutUsed(mContext.getPackageName(), shortcutId, - injectMyUserId())); + mService.reportShortcutUsed(mContext.getPackageName(), shortcutId, injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -601,8 +596,10 @@ public class ShortcutManager { public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut, @Nullable IntentSender resultIntent) { try { - return (boolean) getFutureOrThrow(mService.requestPinShortcut(mContext.getPackageName(), - shortcut, resultIntent, injectMyUserId())); + AndroidFuture ret = new AndroidFuture<>(); + mService.requestPinShortcut(mContext.getPackageName(), shortcut, resultIntent, + injectMyUserId(), ret); + return Boolean.parseBoolean(getFutureOrThrow(ret)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -627,9 +624,11 @@ public class ShortcutManager { */ @WorkerThread public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) { + final AndroidFuture ret = new AndroidFuture<>(); try { - return getFutureOrThrow(mService.createShortcutResultIntent(mContext.getPackageName(), - shortcut, injectMyUserId())); + mService.createShortcutResultIntent(mContext.getPackageName(), + shortcut, injectMyUserId(), ret); + return getFutureOrThrow(ret); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -645,7 +644,7 @@ public class ShortcutManager { */ public void onApplicationActive(@NonNull String packageName, @UserIdInt int userId) { try { - getFutureOrThrow(mService.onApplicationActive(packageName, userId)); + mService.onApplicationActive(packageName, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -671,8 +670,8 @@ public class ShortcutManager { @RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS) public List getShareTargets(@NonNull IntentFilter filter) { try { - return getFutureOrThrow(mService.getShareTargets(mContext.getPackageName(), filter, - injectMyUserId())).getList(); + return mService.getShareTargets( + mContext.getPackageName(), filter, injectMyUserId()).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -782,8 +781,7 @@ public class ShortcutManager { */ public void pushDynamicShortcut(@NonNull ShortcutInfo shortcut) { try { - getFutureOrThrow(mService.pushDynamicShortcut( - mContext.getPackageName(), shortcut, injectMyUserId())); + mService.pushDynamicShortcut(mContext.getPackageName(), shortcut, injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 4eb3b544b34f7..687a165e40323 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -149,7 +149,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Predicate; @@ -1707,17 +1706,6 @@ 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; @@ -1935,385 +1923,317 @@ public class ShortcutService extends IShortcutService.Stub { // === APIs === @Override - public AndroidFuture setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, + public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { - final AndroidFuture ret = new AndroidFuture<>(); - if (!verifyCaller(packageName, userId, ret)) { - return ret; - } + verifyCaller(packageName, userId); + 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; - 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.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); - ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); + ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); + ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); - fillInDefaultActivity(newShortcuts); + fillInDefaultActivity(newShortcuts); - ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET); + 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(); - - ret.complete(true); - } catch (Exception e) { - ret.completeExceptionally(e); + // Throttling. + if (!ps.tryApiCall(unlimited)) { + return false; } - }); - return ret; + + // 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(); + + return true; } @Override - public AndroidFuture updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, + public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { - final AndroidFuture ret = new AndroidFuture<>(); - if (!verifyCaller(packageName, userId, ret)) { - return ret; - } + verifyCaller(packageName, userId); + 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(); - final List changedShortcuts = new ArrayList<>(1); + final List changedShortcuts = new ArrayList<>(1); - synchronized (mLock) { - throwIfUserLockedL(userId); + synchronized (mLock) { + throwIfUserLockedL(userId); - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, - userId); + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); - ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); - ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); + 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. + // For update, don't fill in the default activity. Having null activity means + // "don't update the activity" here. - ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE); + ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE); - // Throttling. - if (!ps.tryApiCall(unlimited)) { - ret.complete(false); + // Throttling. + if (!ps.tryApiCall(unlimited)) { + return false; + } + + // 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; } - // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). - ps.clearAllImplicitRanks(); - assignImplicitRanks(newShortcuts); + if (target.isEnabled() != source.isEnabled()) { + Slog.w(TAG, "ShortcutInfo.enabled cannot be changed with" + + " updateShortcuts()"); + } - 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" + if (target.isLongLived() != source.isLongLived()) { + Slog.w(TAG, + "ShortcutInfo.longLived 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); + // 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()); + } - verifyStates(); + final boolean replacingIcon = (source.getIcon() != null); + if (replacingIcon) { + removeIconLocked(target); + } - ret.complete(true); - } catch (Exception e) { - ret.completeExceptionally(e); + // 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); + }); } - }); - return ret; + + // Lastly, adjust the ranks. + ps.adjustRanks(); + } + packageShortcutsChanged(packageName, userId, + changedShortcuts.isEmpty() ? null : changedShortcuts, null); + + verifyStates(); + + return true; } @Override - public AndroidFuture addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, + public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId) { - final AndroidFuture ret = new AndroidFuture<>(); - if (!verifyCaller(packageName, userId, ret)) { - return ret; - } + verifyCaller(packageName, userId); + 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; + List changedShortcuts = null; - synchronized (mLock) { - throwIfUserLockedL(userId); + synchronized (mLock) { + throwIfUserLockedL(userId); - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, - userId); + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); - ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); - ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); + ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); + ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts); - fillInDefaultActivity(newShortcuts); + fillInDefaultActivity(newShortcuts); - ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD); + ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD); - // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). - ps.clearAllImplicitRanks(); - assignImplicitRanks(newShortcuts); + // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). + ps.clearAllImplicitRanks(); + assignImplicitRanks(newShortcuts); - // Throttling. - if (!ps.tryApiCall(unlimited)) { - ret.complete(false); - return; - } - for (int i = 0; i < size; i++) { - final ShortcutInfo newShortcut = newShortcuts.get(i); - - // Validate the shortcut. - fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false); - - // 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(); - } - packageShortcutsChanged(packageName, userId, changedShortcuts, null); - - verifyStates(); - - ret.complete(true); - } catch (Exception e) { - ret.completeExceptionally(e); + // Throttling. + if (!ps.tryApiCall(unlimited)) { + return false; } - }); - return ret; + for (int i = 0; i < size; i++) { + final ShortcutInfo newShortcut = newShortcuts.get(i); + + // Validate the shortcut. + fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false); + + // 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(); + } + packageShortcutsChanged(packageName, userId, changedShortcuts, null); + verifyStates(); + return true; } @Override - public AndroidFuture pushDynamicShortcut(String packageName, ShortcutInfo shortcut, + public void 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); + verifyCaller(packageName, userId); + verifyShortcutInfoPackage(packageName, shortcut); - List changedShortcuts = new ArrayList<>(); - List removedShortcuts = null; + List changedShortcuts = new ArrayList<>(); + List removedShortcuts = null; - synchronized (mLock) { - throwIfUserLockedL(userId); + synchronized (mLock) { + throwIfUserLockedL(userId); - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, - userId); + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, + userId); - ps.ensureNotImmutable(shortcut.getId(), /*ignoreInvisible=*/ true); - fillInDefaultActivity(Arrays.asList(shortcut)); + 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()) { - ret.complete(null); - return; // Failed to push. - } - removedShortcuts = Collections.singletonList(changedShortcuts.get(0)); - changedShortcuts.clear(); - } - changedShortcuts.add(shortcut); - - // Lastly, adjust the ranks. - ps.adjustRanks(); - } - - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); - - reportShortcutUsedInternal(packageName, shortcut.getId(), userId); - - verifyStates(); - - ret.complete(null); - } catch (Exception e) { - ret.completeExceptionally(e); + if (!shortcut.hasRank()) { + shortcut.setRank(0); } - }); - return ret; + // 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. + } + removedShortcuts = Collections.singletonList(changedShortcuts.get(0)); + changedShortcuts.clear(); + } + changedShortcuts.add(shortcut); + + // Lastly, adjust the ranks. + ps.adjustRanks(); + } + + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + + reportShortcutUsedInternal(packageName, shortcut.getId(), userId); + + verifyStates(); } @Override - 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; + public void requestPinShortcut(String packageName, ShortcutInfo shortcut, + IntentSender resultIntent, int userId, AndroidFuture ret) { + Objects.requireNonNull(shortcut); + Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); + ret.complete(String.valueOf(requestPinItem( + packageName, userId, shortcut, null, null, resultIntent))); } @Override - public AndroidFuture createShortcutResultIntent( - String packageName, ShortcutInfo shortcut, int userId) throws RemoteException { - final AndroidFuture ret = new AndroidFuture<>(); - if (!verifyCaller(packageName, userId, ret)) { - return ret; + public void createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId, + AndroidFuture ret) throws RemoteException { + Objects.requireNonNull(shortcut); + Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); + verifyCaller(packageName, userId); + verifyShortcutInfoPackage(packageName, shortcut); + final Intent intent; + synchronized (mLock) { + throwIfUserLockedL(userId); + // Send request to the launcher, if supported. + intent = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId); } - 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; + verifyStates(); + ret.complete(intent); } /** @@ -2373,357 +2293,213 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public AndroidFuture disableShortcuts(String packageName, List shortcutIds, + public void disableShortcuts(String packageName, List shortcutIds, CharSequence disabledMessage, int disabledMessageResId, @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; - 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(); + 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); } - - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); - - verifyStates(); - - ret.complete(null); - } catch (Exception e) { - ret.completeExceptionally(e); } - }); - return ret; + // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. + ps.adjustRanks(); + } + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + verifyStates(); } @Override - 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; - - 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 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; } - - packageShortcutsChanged(packageName, userId, changedShortcuts, null); - - verifyStates(); - - ret.complete(null); - } catch (Exception e) { - ret.completeExceptionally(e); + ps.enableWithId(id); + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(ps.findShortcutById(id)); } - }); - return ret; + } + packageShortcutsChanged(packageName, userId, changedShortcuts, null); + verifyStates(); } + @Override - public AndroidFuture removeDynamicShortcuts(String packageName, List shortcutIds, + public void removeDynamicShortcuts(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; - List removedShortcuts = null; + 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.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); - } + 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.deleteDynamicWithId(id, /*ignoreInvisible=*/ true); + if (removed == null) { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); } - - // We may have removed dynamic shortcuts which may have left a gap, - // so adjust the ranks. - ps.adjustRanks(); + changedShortcuts.add(ps.findShortcutById(id)); + } else { + if (removedShortcuts == null) { + removedShortcuts = new ArrayList<>(1); + } + removedShortcuts.add(removed); } - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); - - verifyStates(); - - ret.complete(null); - } catch (Exception e) { - ret.completeExceptionally(e); } - }); - return ret; - } - - @Override - public AndroidFuture removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { - final AndroidFuture ret = new AndroidFuture<>(); - if (!verifyCaller(packageName, userId, ret)) { - return ret; + // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. + ps.adjustRanks(); } - injectPostToHandlerIfAppSearch(() -> { - try { - 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); - } - - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); - - verifyStates(); - - ret.complete(null); - } catch (Exception e) { - ret.completeExceptionally(e); - } - }); - return ret; + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + verifyStates(); } @Override - public AndroidFuture removeLongLivedShortcuts(String packageName, List shortcutIds, + 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); + } + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + verifyStates(); + } + + @Override + public void removeLongLivedShortcuts(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; - 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(); + 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)); } - packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); - - verifyStates(); - - ret.complete(null); - } catch (Exception e) { - ret.completeExceptionally(e); } - }); - return ret; + // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. + ps.adjustRanks(); + } + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); + verifyStates(); } @Override - public AndroidFuture getShortcuts(String packageName, + public ParceledListSlice getShortcuts(String packageName, @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId) { - final AndroidFuture ret = new AndroidFuture<>(); - if (!verifyCaller(packageName, userId, ret)) { - return ret; + 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); + return getShortcutsWithQueryLocked( + packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, query, + (ShortcutInfo si) -> + si.isVisibleToPublisher() + && (si.getFlags() & shortcutFlags) != 0); } - 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 AndroidFuture getShareTargets( - String packageName, IntentFilter filter, @UserIdInt int userId) { - final AndroidFuture ret = new AndroidFuture<>(); - try { - Preconditions.checkStringNotEmpty(packageName, "packageName"); - Objects.requireNonNull(filter, "intentFilter"); - - verifyCaller(packageName, userId); - enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, - "getShareTargets"); - } catch (Exception e) { - ret.completeExceptionally(e); - return ret; + public ParceledListSlice getShareTargets(String packageName, + IntentFilter filter, @UserIdInt int userId) { + Preconditions.checkStringNotEmpty(packageName, "packageName"); + Objects.requireNonNull(filter, "intentFilter"); + 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))); + return new ParceledListSlice<>(shortcutInfoList); } - 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 @@ -2820,43 +2596,23 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public AndroidFuture reportShortcutUsed(String packageName, String shortcutId, int userId) { - final AndroidFuture ret = new AndroidFuture<>(); - if (!verifyCaller(packageName, userId, ret)) { - return ret; + 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)); } - injectPostToHandlerIfAppSearch(() -> { - try { - Objects.requireNonNull(shortcutId); - - if (DEBUG) { - Slog.d(TAG, String.format( - "reportShortcutUsed: Shortcut %s package %s used on user %d", - shortcutId, packageName, userId)); - } - - synchronized (mLock) { - throwIfUserLockedL(userId); - - 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; - } - } - - reportShortcutUsedInternal(packageName, shortcutId, userId); - ret.complete(true); - } catch (Exception e) { - ret.completeExceptionally(e); + synchronized (mLock) { + throwIfUserLockedL(userId); + 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)); + return; } - }); - return ret; + } + reportShortcutUsedInternal(packageName, shortcutId, userId); } private void reportShortcutUsedInternal(String packageName, String shortcutId, int userId) { @@ -2914,36 +2670,20 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public AndroidFuture onApplicationActive(String packageName, int userId) { - final AndroidFuture ret = new AndroidFuture<>(); + public void onApplicationActive(String packageName, int userId) { if (DEBUG) { Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + 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); + enforceResetThrottlingPermission(); + synchronized (mLock) { + if (!isUserUnlockedL(userId)) { + // This is called by system UI, so no need to throw. Just ignore. + return; } - }); - return ret; + getPackageShortcutsLocked(packageName, userId) + .resetRateLimitingForCommandLineNoSaving(); + saveUserLocked(userId); + } } // We override this method in unit tests to do a simpler check. @@ -3395,13 +3135,8 @@ public class ShortcutService extends IShortcutService.Stub { @Override public List getShareTargets( @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId) { - final AndroidFuture future = ShortcutService.this.getShareTargets( - callingPackage, intentFilter, userId); - try { - return future.get().getList(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } + return ShortcutService.this.getShareTargets( + callingPackage, intentFilter, userId).getList(); } @Override @@ -4523,72 +4258,48 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public AndroidFuture applyRestore(byte[] payload, @UserIdInt int userId) { - final AndroidFuture ret = new AndroidFuture<>(); - try { - enforceSystem(); - } catch (Exception e) { - ret.completeExceptionally(e); - return ret; + public void applyRestore(byte[] payload, @UserIdInt int userId) { + enforceSystem(); + if (DEBUG || DEBUG_REBOOT) { + Slog.d(TAG, "Restoring user " + userId); } - injectPostToHandler(() -> { - try { - 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; - } - - // 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); - 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); - - 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-5-finish.txt", pw -> { - pw.print("Finish time: "); - dumpCurrentTime(pw); - pw.println(); - }); - - saveUserLocked(userId); - } - ret.complete(null); - } catch (Exception e) { - ret.completeExceptionally(e); + synchronized (mLock) { + if (!isUserUnlockedL(userId)) { + wtf("Can't restore: user " + userId + " is locked or not running"); + return; } - }); - return ret; + // 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); + 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); + 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-5-finish.txt", pw -> { + pw.print("Finish time: "); + dumpCurrentTime(pw); + pw.println(); + }); + saveUserLocked(userId); + } } // === Dump ===