Loading core/java/android/content/pm/ShortcutInfo.java +4 −0 Original line number Diff line number Diff line Loading @@ -128,6 +128,10 @@ public final class ShortcutInfo implements Parcelable { public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT | CLONE_REMOVE_RES_NAMES; /** @hide */ public static final int CLONE_REMOVE_FOR_LAUNCHER_APPROVAL = CLONE_REMOVE_INTENT | CLONE_REMOVE_RES_NAMES; /** @hide */ @IntDef(flag = true, value = { Loading services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java +28 −15 Original line number Diff line number Diff line Loading @@ -48,7 +48,15 @@ class ShortcutRequestPinProcessor { */ private static class PinShortcutRequestInner extends IPinItemRequest.Stub { private final ShortcutRequestPinProcessor mProcessor; public final ShortcutInfo shortcut; /** Original shortcut passed by the app. */ public final ShortcutInfo shortcutOriginal; /** * Cloned shortcut that's passed to the launcher. The notable difference from * {@link #shortcutOriginal} is it must not have the intent. */ public final ShortcutInfo shortcutForLauncher; private final IntentSender mResultIntent; public final String launcherPackage; Loading @@ -59,10 +67,12 @@ class ShortcutRequestPinProcessor { private boolean mAccepted; private PinShortcutRequestInner(ShortcutRequestPinProcessor processor, ShortcutInfo shortcut, IntentSender resultIntent, ShortcutInfo shortcutOriginal, ShortcutInfo shortcutForLauncher, IntentSender resultIntent, String launcherPackage, int launcherUserId, boolean preExisting) { mProcessor = processor; this.shortcut = shortcut; this.shortcutOriginal = shortcutOriginal; this.shortcutForLauncher = shortcutForLauncher; mResultIntent = resultIntent; this.launcherPackage = launcherPackage; this.launcherUserId = launcherUserId; Loading Loading @@ -99,8 +109,8 @@ class ShortcutRequestPinProcessor { mAccepted = true; } if (DEBUG) { Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcut.getId() + " package=" + shortcut.getPackage() Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcutOriginal.getId() + " package=" + shortcutOriginal.getPackage() + " options=" + options); } Loading Loading @@ -163,7 +173,7 @@ class ShortcutRequestPinProcessor { } // This is the shortcut that'll be sent to the launcher. final ShortcutInfo shortcutToSend; final ShortcutInfo shortcutForLauncher; if (existsAlready) { validateExistingShortcut(existing); Loading @@ -179,8 +189,10 @@ class ShortcutRequestPinProcessor { // Pass a clone, not the original. // Note this will remove the intent and icons. shortcutToSend = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); shortcutToSend.clearFlags(ShortcutInfo.FLAG_PINNED); shortcutForLauncher = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); // FLAG_PINNED is still set, if it's pinned by other launchers. shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED); } else { // It doesn't exist, so it must have all mandatory fields. mService.validateShortcutForPinRequest(inShortcut); Loading @@ -191,17 +203,18 @@ class ShortcutRequestPinProcessor { if (DEBUG) { Slog.d(TAG, "resolved shortcut=" + inShortcut.toInsecureString()); } // TODO Remove the intent here -- don't pass shortcut intents to the launcher. shortcutToSend = inShortcut; // We should strip out the intent, but should preserve the icon. shortcutForLauncher = inShortcut.clone( ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER_APPROVAL); } // Create a request object. final PinShortcutRequestInner inner = new PinShortcutRequestInner(this, shortcutToSend, resultIntent, new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher, resultIntent, launcherPackage, launcherUserId, existsAlready); final PinItemRequest outer = new PinItemRequest(PinItemRequest.REQUEST_TYPE_SHORTCUT, shortcutToSend, inner); shortcutForLauncher, inner); return startRequestConfirmActivity(launcherComponent, launcherUserId, outer); } Loading @@ -210,7 +223,7 @@ class ShortcutRequestPinProcessor { // Make sure it's enabled. // (Because we can't always force enable it automatically as it may be a stale // manifest shortcut.) Preconditions.checkState(shortcutInfo.isEnabled(), Preconditions.checkArgument(shortcutInfo.isEnabled(), "Shortcut ID=" + shortcutInfo + " already exists but disabled."); } Loading Loading @@ -270,7 +283,7 @@ class ShortcutRequestPinProcessor { */ public boolean directPinShortcut(PinShortcutRequestInner request) { final ShortcutInfo original = request.shortcut; final ShortcutInfo original = request.shortcutOriginal; final int appUserId = original.getUserId(); final String appPackageName = original.getPackage(); final int launcherUserId = request.launcherUserId; Loading @@ -292,7 +305,7 @@ class ShortcutRequestPinProcessor { try { if (current == null) { // It doesn't exist, so it must have all necessary fields. mService.validateShortcutForPinRequest(request.shortcut); mService.validateShortcutForPinRequest(original); } else { validateExistingShortcut(current); } Loading services/core/java/com/android/server/pm/ShortcutService.java +3 −2 Original line number Diff line number Diff line Loading @@ -1865,9 +1865,10 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { throwIfUserLockedL(userId); // TODO Make sure the caller is in the foreground. Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()), "Calling application must have a foreground activity or a foreground service"); // TODO Cancel all pending request from the same app. // TODO Cancel all pending requests from the caller. // Send request to the launcher, if supported. ret = mShortcutRequestPinProcessor.requestPinShortcutLocked(shortcut, resultIntent); Loading services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +51 −1 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ import android.os.Handler; import android.os.Looper; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.test.InstrumentationTestCase; Loading Loading @@ -1288,6 +1289,13 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { + "/" + ShortcutService.FILENAME_USER_PACKAGES, message); } /** * Make a shortcut with an ID only. */ protected ShortcutInfo makeShortcutIdOnly(String id) { return new ShortcutInfo.Builder(mClientContext, id).build(); } /** * Make a shortcut with an ID. */ Loading @@ -1297,12 +1305,19 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } @Deprecated // Title was renamed to short label. protected ShortcutInfo makeShortcutWithTitle(String id, String title) { return makeShortcut( id, title, /* activity =*/ null, /* icon =*/ null, makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } protected ShortcutInfo makeShortcutWithShortLabel(String id, String shortLabel) { return makeShortcut( id, shortLabel, /* activity =*/ null, /* icon =*/ null, makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } /** * Make a shortcut with an ID and timestamp. */ Loading Loading @@ -1695,6 +1710,13 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED); } protected List<ShortcutInfo> getShortcutAsLauncher(int targetUserId) { final ShortcutQuery q = new ShortcutQuery(); q.setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_PINNED); return mLauncherApps.getShortcuts(q, UserHandle.of(targetUserId)); } protected ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId, int userId) { final List<ShortcutInfo> infoList = Loading Loading @@ -1968,7 +1990,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { public static List<ShortcutInfo> assertAllHaveIcon( List<ShortcutInfo> actualShortcuts) { for (ShortcutInfo s : actualShortcuts) { assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource()); assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource() || s.getIcon() != null); } return actualShortcuts; } Loading Loading @@ -2030,4 +2053,31 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true, PACKAGE_FALLBACK_LAUNCHER_PRIORITY); } protected void makeCallerForeground() { try { mService.mUidObserver.onUidStateChanged( mInjectedCallingUid, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } protected void makeCallerBackground() { try { mService.mUidObserver.onUidStateChanged( mInjectedCallingUid, ActivityManager.PROCESS_STATE_TOP_SLEEPING); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } protected void publishManifestShortcutsAsCaller(int resId) { addManifestShortcutResource( new ComponentName(getCallingPackage(), ShortcutActivity.class.getName()), resId); updatePackageVersion(getCallingPackage(), 1); mService.mPackageMonitor.onReceive(getTestContext(), genPackageAddIntent(getCallingPackage(), getCallingUserId())); } } services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java +961 −24 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/content/pm/ShortcutInfo.java +4 −0 Original line number Diff line number Diff line Loading @@ -128,6 +128,10 @@ public final class ShortcutInfo implements Parcelable { public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT | CLONE_REMOVE_RES_NAMES; /** @hide */ public static final int CLONE_REMOVE_FOR_LAUNCHER_APPROVAL = CLONE_REMOVE_INTENT | CLONE_REMOVE_RES_NAMES; /** @hide */ @IntDef(flag = true, value = { Loading
services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java +28 −15 Original line number Diff line number Diff line Loading @@ -48,7 +48,15 @@ class ShortcutRequestPinProcessor { */ private static class PinShortcutRequestInner extends IPinItemRequest.Stub { private final ShortcutRequestPinProcessor mProcessor; public final ShortcutInfo shortcut; /** Original shortcut passed by the app. */ public final ShortcutInfo shortcutOriginal; /** * Cloned shortcut that's passed to the launcher. The notable difference from * {@link #shortcutOriginal} is it must not have the intent. */ public final ShortcutInfo shortcutForLauncher; private final IntentSender mResultIntent; public final String launcherPackage; Loading @@ -59,10 +67,12 @@ class ShortcutRequestPinProcessor { private boolean mAccepted; private PinShortcutRequestInner(ShortcutRequestPinProcessor processor, ShortcutInfo shortcut, IntentSender resultIntent, ShortcutInfo shortcutOriginal, ShortcutInfo shortcutForLauncher, IntentSender resultIntent, String launcherPackage, int launcherUserId, boolean preExisting) { mProcessor = processor; this.shortcut = shortcut; this.shortcutOriginal = shortcutOriginal; this.shortcutForLauncher = shortcutForLauncher; mResultIntent = resultIntent; this.launcherPackage = launcherPackage; this.launcherUserId = launcherUserId; Loading Loading @@ -99,8 +109,8 @@ class ShortcutRequestPinProcessor { mAccepted = true; } if (DEBUG) { Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcut.getId() + " package=" + shortcut.getPackage() Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcutOriginal.getId() + " package=" + shortcutOriginal.getPackage() + " options=" + options); } Loading Loading @@ -163,7 +173,7 @@ class ShortcutRequestPinProcessor { } // This is the shortcut that'll be sent to the launcher. final ShortcutInfo shortcutToSend; final ShortcutInfo shortcutForLauncher; if (existsAlready) { validateExistingShortcut(existing); Loading @@ -179,8 +189,10 @@ class ShortcutRequestPinProcessor { // Pass a clone, not the original. // Note this will remove the intent and icons. shortcutToSend = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); shortcutToSend.clearFlags(ShortcutInfo.FLAG_PINNED); shortcutForLauncher = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); // FLAG_PINNED is still set, if it's pinned by other launchers. shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED); } else { // It doesn't exist, so it must have all mandatory fields. mService.validateShortcutForPinRequest(inShortcut); Loading @@ -191,17 +203,18 @@ class ShortcutRequestPinProcessor { if (DEBUG) { Slog.d(TAG, "resolved shortcut=" + inShortcut.toInsecureString()); } // TODO Remove the intent here -- don't pass shortcut intents to the launcher. shortcutToSend = inShortcut; // We should strip out the intent, but should preserve the icon. shortcutForLauncher = inShortcut.clone( ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER_APPROVAL); } // Create a request object. final PinShortcutRequestInner inner = new PinShortcutRequestInner(this, shortcutToSend, resultIntent, new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher, resultIntent, launcherPackage, launcherUserId, existsAlready); final PinItemRequest outer = new PinItemRequest(PinItemRequest.REQUEST_TYPE_SHORTCUT, shortcutToSend, inner); shortcutForLauncher, inner); return startRequestConfirmActivity(launcherComponent, launcherUserId, outer); } Loading @@ -210,7 +223,7 @@ class ShortcutRequestPinProcessor { // Make sure it's enabled. // (Because we can't always force enable it automatically as it may be a stale // manifest shortcut.) Preconditions.checkState(shortcutInfo.isEnabled(), Preconditions.checkArgument(shortcutInfo.isEnabled(), "Shortcut ID=" + shortcutInfo + " already exists but disabled."); } Loading Loading @@ -270,7 +283,7 @@ class ShortcutRequestPinProcessor { */ public boolean directPinShortcut(PinShortcutRequestInner request) { final ShortcutInfo original = request.shortcut; final ShortcutInfo original = request.shortcutOriginal; final int appUserId = original.getUserId(); final String appPackageName = original.getPackage(); final int launcherUserId = request.launcherUserId; Loading @@ -292,7 +305,7 @@ class ShortcutRequestPinProcessor { try { if (current == null) { // It doesn't exist, so it must have all necessary fields. mService.validateShortcutForPinRequest(request.shortcut); mService.validateShortcutForPinRequest(original); } else { validateExistingShortcut(current); } Loading
services/core/java/com/android/server/pm/ShortcutService.java +3 −2 Original line number Diff line number Diff line Loading @@ -1865,9 +1865,10 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { throwIfUserLockedL(userId); // TODO Make sure the caller is in the foreground. Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()), "Calling application must have a foreground activity or a foreground service"); // TODO Cancel all pending request from the same app. // TODO Cancel all pending requests from the caller. // Send request to the launcher, if supported. ret = mShortcutRequestPinProcessor.requestPinShortcutLocked(shortcut, resultIntent); Loading
services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +51 −1 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ import android.os.Handler; import android.os.Looper; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.test.InstrumentationTestCase; Loading Loading @@ -1288,6 +1289,13 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { + "/" + ShortcutService.FILENAME_USER_PACKAGES, message); } /** * Make a shortcut with an ID only. */ protected ShortcutInfo makeShortcutIdOnly(String id) { return new ShortcutInfo.Builder(mClientContext, id).build(); } /** * Make a shortcut with an ID. */ Loading @@ -1297,12 +1305,19 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } @Deprecated // Title was renamed to short label. protected ShortcutInfo makeShortcutWithTitle(String id, String title) { return makeShortcut( id, title, /* activity =*/ null, /* icon =*/ null, makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } protected ShortcutInfo makeShortcutWithShortLabel(String id, String shortLabel) { return makeShortcut( id, shortLabel, /* activity =*/ null, /* icon =*/ null, makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } /** * Make a shortcut with an ID and timestamp. */ Loading Loading @@ -1695,6 +1710,13 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED); } protected List<ShortcutInfo> getShortcutAsLauncher(int targetUserId) { final ShortcutQuery q = new ShortcutQuery(); q.setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_PINNED); return mLauncherApps.getShortcuts(q, UserHandle.of(targetUserId)); } protected ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId, int userId) { final List<ShortcutInfo> infoList = Loading Loading @@ -1968,7 +1990,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { public static List<ShortcutInfo> assertAllHaveIcon( List<ShortcutInfo> actualShortcuts) { for (ShortcutInfo s : actualShortcuts) { assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource()); assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource() || s.getIcon() != null); } return actualShortcuts; } Loading Loading @@ -2030,4 +2053,31 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true, PACKAGE_FALLBACK_LAUNCHER_PRIORITY); } protected void makeCallerForeground() { try { mService.mUidObserver.onUidStateChanged( mInjectedCallingUid, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } protected void makeCallerBackground() { try { mService.mUidObserver.onUidStateChanged( mInjectedCallingUid, ActivityManager.PROCESS_STATE_TOP_SLEEPING); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } protected void publishManifestShortcutsAsCaller(int resId) { addManifestShortcutResource( new ComponentName(getCallingPackage(), ShortcutActivity.class.getName()), resId); updatePackageVersion(getCallingPackage(), 1); mService.mPackageMonitor.onReceive(getTestContext(), genPackageAddIntent(getCallingPackage(), getCallingUserId())); } }
services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java +961 −24 File changed.Preview size limit exceeded, changes collapsed. Show changes