From 4750fe5036c5706cf93ddc9807a2eac3076197a8 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Mon, 22 Mar 2021 16:41:42 -0700 Subject: [PATCH] Include LauncherApps#getShortcutIntents Add new api to retrieve pending intents from specified shortcut. Bug: 151359749 CTS-Coverage-Bug: 13975407 Test: atest ShortcutManagerTest12 Change-Id: I6c413615d1392805f601561550dcb85bfe242b37 --- core/api/current.txt | 1 + .../android/content/pm/ILauncherApps.aidl | 2 + .../java/android/content/pm/LauncherApps.java | 24 ++++++++++ .../server/pm/LauncherAppsService.java | 45 +++++++++++++++++++ .../server/pm/BaseShortcutManagerTest.java | 15 +++++++ .../server/pm/ShortcutManagerTest12.java | 19 ++++++++ 6 files changed, 106 insertions(+) diff --git a/core/api/current.txt b/core/api/current.txt index bb501e89b3b35..811a7116d98ff 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12131,6 +12131,7 @@ package android.content.pm { method @Nullable public android.content.IntentSender getShortcutConfigActivityIntent(@NonNull android.content.pm.LauncherActivityInfo); method public java.util.List getShortcutConfigActivityList(@Nullable String, @NonNull android.os.UserHandle); method public android.graphics.drawable.Drawable getShortcutIconDrawable(@NonNull android.content.pm.ShortcutInfo, int); + method @Nullable public android.app.PendingIntent getShortcutIntent(@NonNull String, @NonNull String, @Nullable android.os.Bundle, @NonNull android.os.UserHandle); method @Nullable public java.util.List getShortcuts(@NonNull android.content.pm.LauncherApps.ShortcutQuery, @NonNull android.os.UserHandle); method @Nullable public android.os.Bundle getSuspendedPackageLauncherExtras(String, android.os.UserHandle); method public boolean hasShortcutHostPermission(); diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index d688614f6caad..37fd3ffdeafa5 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -90,6 +90,8 @@ interface ILauncherApps { String callingPackage, String packageName, in UserHandle user); IntentSender getShortcutConfigActivityIntent(String callingPackage, in ComponentName component, in UserHandle user); + PendingIntent getShortcutIntent(String callingPackage, String packageName, String shortcutId, + in Bundle opts, in UserHandle user); // Unregister is performed using package installer void registerPackageInstallerCallback(String callingPackage, diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 80fecc1a3195b..8b9b73683575e 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -842,6 +842,30 @@ public class LauncherApps { } } + /** + * Returns PendingIntent associated with specified shortcut. + * + * @param packageName The packageName of the shortcut + * @param shortcutId The id of the shortcut + * @param opts Options to pass to the PendingIntent + * @param user The UserHandle of the profile + */ + @Nullable + public PendingIntent getShortcutIntent(@NonNull final String packageName, + @NonNull final String shortcutId, @Nullable final Bundle opts, + @NonNull final UserHandle user) { + logErrorForInvalidProfileAccess(user); + if (DEBUG) { + Log.i(TAG, "GetShortcutIntent " + packageName + "/" + shortcutId + " " + user); + } + try { + return mService.getShortcutIntent( + mContext.getPackageName(), packageName, shortcutId, opts, user); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + /** * Retrieves a list of config activities for creating {@link ShortcutInfo}. * diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 044e186140a48..27e0ffc73d9d1 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.app.ActivityOptions.KEY_SPLASH_SCREEN_THEME; import static android.app.PendingIntent.FLAG_IMMUTABLE; +import static android.app.PendingIntent.FLAG_MUTABLE; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS; @@ -648,6 +649,43 @@ public class LauncherAppsService extends SystemService { } } + /** + * Returns the intents for a specific shortcut. + */ + @Nullable + @Override + public PendingIntent getShortcutIntent(@NonNull final String callingPackage, + @NonNull final String packageName, @NonNull final String shortcutId, + @Nullable final Bundle opts, @NonNull final UserHandle user) + throws RemoteException { + Objects.requireNonNull(callingPackage); + Objects.requireNonNull(packageName); + Objects.requireNonNull(shortcutId); + Objects.requireNonNull(user); + + ensureShortcutPermission(callingPackage); + if (!canAccessProfile(user.getIdentifier(), "Cannot get shortcuts")) { + return null; + } + + final Intent[] intents = mShortcutServiceInternal.createShortcutIntents( + getCallingUserId(), callingPackage, packageName, shortcutId, + user.getIdentifier(), injectBinderCallingPid(), injectBinderCallingUid()); + if (intents == null || intents.length == 0) { + return null; + } + final long ident = Binder.clearCallingIdentity(); + try { + return injectCreatePendingIntent(mContext.createPackageContextAsUser(packageName, + 0, user), 0 /* requestCode */, intents, FLAG_MUTABLE, opts, user); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Cannot create pending intent from shortcut " + shortcutId, e); + } finally { + Binder.restoreCallingIdentity(ident); + } + return null; + } + @Override public boolean isPackageEnabled(String callingPackage, String packageName, UserHandle user) throws RemoteException { @@ -756,6 +794,13 @@ public class LauncherAppsService extends SystemService { callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; } + @VisibleForTesting + PendingIntent injectCreatePendingIntent(Context context, int requestCode, + @NonNull Intent[] intents, int flags, Bundle options, UserHandle user) { + return PendingIntent.getActivitiesAsUser(context, requestCode, intents, flags, options, + user); + } + @Override public ParceledListSlice getShortcuts(@NonNull final String callingPackage, @NonNull final ShortcutQueryWrapper query, @NonNull final UserHandle targetUser) { diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 1ab70e524d3cb..e66c0ef86691a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -43,6 +43,7 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.IUidObserver; +import android.app.PendingIntent; import android.app.Person; import android.app.admin.DevicePolicyManager; import android.app.appsearch.AppSearchBatchResult; @@ -60,6 +61,7 @@ import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; @@ -196,6 +198,13 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return this; } + @Override + public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) + throws PackageManager.NameNotFoundException { + // ignore. + return this; + } + @Override public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, String broadcastPermission, Handler scheduler) { @@ -619,6 +628,12 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { boolean injectHasInteractAcrossUsersFullPermission(int callingPid, int callingUid) { return false; } + + @Override + PendingIntent injectCreatePendingIntent(Context context, int requestCode, + @NonNull Intent[] intents, int flags, Bundle options, UserHandle user) { + return new PendingIntent(mock(IIntentSender.class)); + } } protected class LauncherAppsTestable extends LauncherApps { diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java index b17085ee0317a..fd3e7a8e110d4 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java @@ -15,8 +15,13 @@ */ package com.android.server.pm; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list; + +import android.app.PendingIntent; import android.app.appsearch.PackageIdentifier; import android.content.pm.AppSearchShortcutInfo; +import android.os.RemoteException; +import android.os.UserHandle; import java.util.Random; @@ -41,4 +46,18 @@ public class ShortcutManagerTest12 extends BaseShortcutManagerTest { new PackageIdentifier(CALLING_PACKAGE_2, cert))); }); } + + public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException { + setDefaultLauncher(USER_0, LAUNCHER_1); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> + assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1")))) + ); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + final PendingIntent intent = mLauncherApps.getShortcutIntent( + CALLING_PACKAGE_1, "s1", null, UserHandle.SYSTEM); + assertNotNull(intent); + }); + } }