diff options
author | 2020-03-18 02:59:03 +0000 | |
---|---|---|
committer | 2020-03-18 02:59:03 +0000 | |
commit | f9275345db9ea33c0816b321c20b5dbc860ad767 (patch) | |
tree | efc467c4bdc4b357cff77b713d2af90eae3dad61 | |
parent | 6d6ddf45024230f6a7ba62aafc2aa59c9211a48c (diff) | |
parent | 98ffa48a7ab9566bee2737437a0681a1c791cfea (diff) |
Merge changes Ibfb3cf6f,I9a4bd220 into rvc-dev
* changes:
Load Uri based shortcut drawable in LauncherApps
Adds icon URI field to shortcutInfo
13 files changed, 562 insertions, 39 deletions
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index 8a89840876d9..27c9cfcdd05b 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -103,4 +103,7 @@ interface ILauncherApps { in UserHandle user); void uncacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); + + String getShortcutIconUri(String callingPackage, String packageName, String shortcutId, + int userId); } diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 22516f069e83..e73fd03e34b6 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -51,6 +51,7 @@ import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -67,8 +68,10 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledLambda; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1198,6 +1201,35 @@ public class LauncherApps { } /** + * @hide internal/unit tests only + */ + @VisibleForTesting + public ParcelFileDescriptor getUriShortcutIconFd(@NonNull ShortcutInfo shortcut) { + return getUriShortcutIconFd(shortcut.getPackage(), shortcut.getId(), shortcut.getUserId()); + } + + private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName, + @NonNull String shortcutId, int userId) { + String uri = null; + try { + uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId, + userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + if (uri == null) { + return null; + } + try { + return mContext.getContentResolver().openFileDescriptor(Uri.parse(uri), "r"); + } catch (FileNotFoundException e) { + Log.e(TAG, "Icon file not found: " + uri); + return null; + } + } + + /** * Returns the icon for this shortcut, without any badging for the profile. * * <p>The calling launcher application must be allowed to access the shortcut information, @@ -1217,26 +1249,10 @@ public class LauncherApps { public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) { if (shortcut.hasIconFile()) { final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut); - if (pfd == null) { - return null; - } - try { - final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor()); - if (bmp != null) { - BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp); - if (shortcut.hasAdaptiveBitmap()) { - return new AdaptiveIconDrawable(null, dr); - } else { - return dr; - } - } - return null; - } finally { - try { - pfd.close(); - } catch (IOException ignore) { - } - } + return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap()); + } else if (shortcut.hasIconUri()) { + final ParcelFileDescriptor pfd = getUriShortcutIconFd(shortcut); + return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap()); } else if (shortcut.hasIconResource()) { return loadDrawableResourceFromPackage(shortcut.getPackage(), shortcut.getIconResourceId(), shortcut.getUserHandle(), density); @@ -1260,6 +1276,29 @@ public class LauncherApps { } } + private Drawable loadDrawableFromFileDescriptor(ParcelFileDescriptor pfd, boolean adaptive) { + if (pfd == null) { + return null; + } + try { + final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor()); + if (bmp != null) { + BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp); + if (adaptive) { + return new AdaptiveIconDrawable(null, dr); + } else { + return dr; + } + } + return null; + } finally { + try { + pfd.close(); + } catch (IOException ignore) { + } + } + } + private Drawable loadDrawableResourceFromPackage(String packageName, int resId, UserHandle user, int density) { try { diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index af875787bba6..8d8776f1bc23 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -123,6 +123,9 @@ public final class ShortcutInfo implements Parcelable { public static final int FLAG_CACHED = 1 << 14; /** @hide */ + public static final int FLAG_HAS_ICON_URI = 1 << 15; + + /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { FLAG_DYNAMIC, FLAG_PINNED, @@ -139,6 +142,7 @@ public final class ShortcutInfo implements Parcelable { FLAG_SHADOW, FLAG_LONG_LIVED, FLAG_CACHED, + FLAG_HAS_ICON_URI, }) @Retention(RetentionPolicy.SOURCE) public @interface ShortcutFlags {} @@ -401,6 +405,9 @@ public final class ShortcutInfo implements Parcelable { private String mIconResName; // Internal use only. + private String mIconUri; + + // Internal use only. @Nullable private String mBitmapPath; @@ -554,6 +561,7 @@ public final class ShortcutInfo implements Parcelable { if ((cloneFlags & CLONE_REMOVE_ICON) == 0) { mIcon = source.mIcon; mBitmapPath = source.mBitmapPath; + mIconUri = source.mIconUri; } mTitle = source.mTitle; @@ -856,6 +864,7 @@ public final class ShortcutInfo implements Parcelable { mIconResId = 0; mIconResName = null; mBitmapPath = null; + mIconUri = null; } if (source.mTitle != null) { mTitle = source.mTitle; @@ -916,6 +925,8 @@ public final class ShortcutInfo implements Parcelable { case Icon.TYPE_RESOURCE: case Icon.TYPE_BITMAP: case Icon.TYPE_ADAPTIVE_BITMAP: + case Icon.TYPE_URI: + case Icon.TYPE_URI_ADAPTIVE_BITMAP: break; // OK default: throw getInvalidIconException(); @@ -1792,6 +1803,15 @@ public final class ShortcutInfo implements Parcelable { return hasFlags(FLAG_HAS_ICON_RES); } + /** + * Return whether a shortcut's icon is provided via a URI. + * + * @hide internal/unit tests only + */ + public boolean hasIconUri() { + return hasFlags(FLAG_HAS_ICON_URI); + } + /** @hide */ public boolean hasStringResources() { return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0); @@ -1911,6 +1931,19 @@ public final class ShortcutInfo implements Parcelable { return mIconResId; } + /** @hide */ + public void setIconUri(String iconUri) { + mIconUri = iconUri; + } + + /** + * Get the Uri for the icon, valid only when {@link #hasIconUri()} } is true. + * @hide internal / tests only. + */ + public String getIconUri() { + return mIconUri; + } + /** * Bitmap path. Note this will be null even if {@link #hasIconFile()} is set when the save * is pending. Use {@link #isIconPendingSave()} to check it. @@ -2062,6 +2095,7 @@ public final class ShortcutInfo implements Parcelable { mPersons = source.readParcelableArray(cl, Person.class); mLocusId = source.readParcelable(cl); + mIconUri = source.readString(); } @Override @@ -2112,6 +2146,7 @@ public final class ShortcutInfo implements Parcelable { dest.writeParcelableArray(mPersons, flags); dest.writeParcelable(mLocusId, flags); + dest.writeString(mIconUri); } public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR = @@ -2203,6 +2238,12 @@ public final class ShortcutInfo implements Parcelable { if (hasIconResource()) { sb.append("Ic-r"); } + if (hasIconUri()) { + sb.append("Ic-u"); + } + if (hasAdaptiveBitmap()) { + sb.append("Ic-a"); + } if (hasKeyFieldsOnly()) { sb.append("Key"); } @@ -2325,6 +2366,9 @@ public final class ShortcutInfo implements Parcelable { sb.append(", bitmapPath="); sb.append(mBitmapPath); + + sb.append(", iconUri="); + sb.append(mIconUri); } if (mLocusId != null) { @@ -2343,8 +2387,8 @@ public final class ShortcutInfo implements Parcelable { CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName, Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras, long lastChangedTimestamp, - int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason, - Person[] persons, LocusId locusId) { + int flags, int iconResId, String iconResName, String bitmapPath, String iconUri, + int disabledReason, Person[] persons, LocusId locusId) { mUserId = userId; mId = id; mPackageName = packageName; @@ -2369,6 +2413,7 @@ public final class ShortcutInfo implements Parcelable { mIconResId = iconResId; mIconResName = iconResName; mBitmapPath = bitmapPath; + mIconUri = iconUri; mDisabledReason = disabledReason; mPersons = persons; mLocusId = locusId; diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index a50ce92a70bf..435c70ae999b 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -103,4 +103,10 @@ public abstract class ShortcutServiceInternal { */ public abstract List<ShortcutManager.ShareShortcutInfo> getShareTargets( @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId); + + /** + * Returns the icon Uri of the shortcut, and grants Uri read permission to the caller. + */ + public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage, + @NonNull String packageName, @NonNull String shortcutId, int userId); } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 8031eaa62084..1b271a739777 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -819,6 +819,18 @@ public class LauncherAppsService extends SystemService { } @Override + public String getShortcutIconUri(String callingPackage, String packageName, + String shortcutId, int userId) { + ensureShortcutPermission(callingPackage); + if (!canAccessProfile(userId, "Cannot access shortcuts")) { + return null; + } + + return mShortcutServiceInternal.getShortcutIconUri(getCallingUserId(), callingPackage, + packageName, shortcutId, userId); + } + + @Override public boolean hasShortcutHostPermission(String callingPackage) { verifyCallingPackage(callingPackage); return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(), diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java index dc534a702946..1c5f0a7fc0f3 100644 --- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java +++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java @@ -28,7 +28,6 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; import com.android.server.pm.ShortcutService.FileOutputStreamWithPath; import libcore.io.IoUtils; @@ -150,9 +149,10 @@ public class ShortcutBitmapSaver { shortcut.setIconResourceId(0); shortcut.setIconResName(null); shortcut.setBitmapPath(null); + shortcut.setIconUri(null); shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_ADAPTIVE_BITMAP | ShortcutInfo.FLAG_HAS_ICON_RES | - ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE); + ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE | ShortcutInfo.FLAG_HAS_ICON_URI); } public void saveBitmapLocked(ShortcutInfo shortcut, diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index c8df5c731845..1fc0a38e1ed3 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -102,6 +102,7 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_ICON_RES_ID = "icon-res"; private static final String ATTR_ICON_RES_NAME = "icon-resname"; private static final String ATTR_BITMAP_PATH = "bitmap-path"; + private static final String ATTR_ICON_URI = "icon-uri"; private static final String ATTR_LOCUS_ID = "locus-id"; private static final String ATTR_PERSON_NAME = "name"; @@ -1628,7 +1629,8 @@ class ShortcutPackage extends ShortcutPackageItem { int flags = si.getFlags() & ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES | ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE - | ShortcutInfo.FLAG_DYNAMIC); + | ShortcutInfo.FLAG_DYNAMIC + | ShortcutInfo.FLAG_HAS_ICON_URI | ShortcutInfo.FLAG_ADAPTIVE_BITMAP); ShortcutService.writeAttr(out, ATTR_FLAGS, flags); // Set the publisher version code at every backup. @@ -1646,6 +1648,7 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.writeAttr(out, ATTR_ICON_RES_ID, si.getIconResourceId()); ShortcutService.writeAttr(out, ATTR_ICON_RES_NAME, si.getIconResName()); ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath()); + ShortcutService.writeAttr(out, ATTR_ICON_URI, si.getIconUri()); } if (shouldBackupDetails) { @@ -1764,6 +1767,7 @@ class ShortcutPackage extends ShortcutPackageItem { int iconResId; String iconResName; String bitmapPath; + String iconUri; final String locusIdString; int backupVersionCode; ArraySet<String> categories = null; @@ -1791,6 +1795,7 @@ class ShortcutPackage extends ShortcutPackageItem { iconResId = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES_ID); iconResName = ShortcutService.parseStringAttribute(parser, ATTR_ICON_RES_NAME); bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH); + iconUri = ShortcutService.parseStringAttribute(parser, ATTR_ICON_URI); locusIdString = ShortcutService.parseStringAttribute(parser, ATTR_LOCUS_ID); final int outerDepth = parser.getDepth(); @@ -1866,8 +1871,8 @@ class ShortcutPackage extends ShortcutPackageItem { categories, intents.toArray(new Intent[intents.size()]), rank, extras, lastChangedTimestamp, flags, - iconResId, iconResName, bitmapPath, disabledReason, - persons.toArray(new Person[persons.size()]), locusId); + iconResId, iconResName, bitmapPath, iconUri, + disabledReason, persons.toArray(new Person[persons.size()]), locusId); } private static Intent parseIntent(XmlPullParser parser) @@ -1991,16 +1996,26 @@ class ShortcutPackage extends ShortcutPackageItem { Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + " still has an icon"); } - if (si.hasAdaptiveBitmap() && !si.hasIconFile()) { + if (si.hasAdaptiveBitmap() && !(si.hasIconFile() || si.hasIconUri())) { failed = true; Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() - + " has adaptive bitmap but was not saved to a file."); + + " has adaptive bitmap but was not saved to a file nor has icon uri."); } if (si.hasIconFile() && si.hasIconResource()) { failed = true; Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + " has both resource and bitmap icons"); } + if (si.hasIconFile() && si.hasIconUri()) { + failed = true; + Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + + " has both url and bitmap icons"); + } + if (si.hasIconUri() && si.hasIconResource()) { + failed = true; + Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + + " has both url and resource icons"); + } if (si.isEnabled() != (si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED)) { failed = true; diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java index f9c0db0e9a1d..d3aace19672a 100644 --- a/services/core/java/com/android/server/pm/ShortcutParser.java +++ b/services/core/java/com/android/server/pm/ShortcutParser.java @@ -449,6 +449,7 @@ public class ShortcutParser { iconResId, null, // icon res name null, // bitmap path + null, // icon Url disabledReason, null /* persons */, null /* locusId */); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 66f3574d83a0..8768ab0a683b 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -24,6 +24,8 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.IUidObserver; +import android.app.IUriGrantsManager; +import android.app.UriGrantsManager; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; @@ -65,6 +67,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.IBinder; import android.os.LocaleList; import android.os.Looper; import android.os.ParcelFileDescriptor; @@ -106,6 +109,7 @@ import com.android.internal.util.StatLogger; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.ShortcutUser.PackageWithUser; +import com.android.server.uri.UriGrantsManagerInternal; import libcore.io.IoUtils; @@ -327,6 +331,9 @@ public class ShortcutService extends IShortcutService.Stub { private final UserManagerInternal mUserManagerInternal; private final UsageStatsManagerInternal mUsageStatsManagerInternal; private final ActivityManagerInternal mActivityManagerInternal; + private final IUriGrantsManager mUriGrantsManager; + private final UriGrantsManagerInternal mUriGrantsManagerInternal; + private final IBinder mUriPermissionOwner; private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor; private final ShortcutBitmapSaver mShortcutBitmapSaver; @@ -449,6 +456,11 @@ public class ShortcutService extends IShortcutService.Stub { mActivityManagerInternal = Objects.requireNonNull( LocalServices.getService(ActivityManagerInternal.class)); + mUriGrantsManager = UriGrantsManager.getService(); + mUriGrantsManagerInternal = Objects.requireNonNull( + LocalServices.getService(UriGrantsManagerInternal.class)); + mUriPermissionOwner = mUriGrantsManagerInternal.newUriPermissionOwner(TAG); + mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock); mShortcutBitmapSaver = new ShortcutBitmapSaver(this); mShortcutDumpFiles = new ShortcutDumpFiles(this); @@ -1414,7 +1426,7 @@ public class ShortcutService extends IShortcutService.Stub { } void saveIconAndFixUpShortcutLocked(ShortcutInfo shortcut) { - if (shortcut.hasIconFile() || shortcut.hasIconResource()) { + if (shortcut.hasIconFile() || shortcut.hasIconResource() || shortcut.hasIconUri()) { return; } @@ -1438,6 +1450,15 @@ public class ShortcutService extends IShortcutService.Stub { shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES); return; } + case Icon.TYPE_URI: + shortcut.setIconUri(icon.getUriString()); + shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_URI); + return; + case Icon.TYPE_URI_ADAPTIVE_BITMAP: + shortcut.setIconUri(icon.getUriString()); + shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_URI + | ShortcutInfo.FLAG_ADAPTIVE_BITMAP); + return; case Icon.TYPE_BITMAP: bitmap = icon.getBitmap(); // Don't recycle in this case. break; @@ -3129,6 +3150,59 @@ public class ShortcutService extends IShortcutService.Stub { } @Override + public String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage, + @NonNull String packageName, @NonNull String shortcutId, int userId) { + Objects.requireNonNull(launcherPackage, "launcherPackage"); + Objects.requireNonNull(packageName, "packageName"); + Objects.requireNonNull(shortcutId, "shortcutId"); + + synchronized (mLock) { + throwIfUserLockedL(userId); + throwIfUserLockedL(launcherUserId); + + getLauncherShortcutsLocked(launcherPackage, userId, launcherUserId) + .attemptToRestoreIfNeededAndSave(); + + final ShortcutPackage p = getUserShortcutsLocked(userId) + .getPackageShortcutsIfExists(packageName); + if (p == null) { + return null; + } + + final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId); + if (shortcutInfo == null || !shortcutInfo.hasIconUri()) { + return null; + } + String uri = shortcutInfo.getIconUri(); + if (uri == null) { + Slog.w(TAG, "null uri detected in getShortcutIconUri()"); + return null; + } + + final long token = Binder.clearCallingIdentity(); + try { + int packageUid = mPackageManagerInternal.getPackageUidInternal(packageName, + PackageManager.MATCH_DIRECT_BOOT_AUTO, userId); + // Grant read uri permission to the caller on behalf of the shortcut owner. All + // granted permissions are revoked when the default launcher changes, or when + // device is rebooted. + // b/151572645 is tracking a bug where Uri permissions are persisted across + // reboots, even when Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION is not used. + mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid, + launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, + userId, launcherUserId); + } catch (Exception e) { + Slog.e(TAG, "Failed to grant uri access to " + launcherPackage + " for " + uri, + e); + uri = null; + } finally { + Binder.restoreCallingIdentity(token); + } + return uri; + } + } + + @Override public boolean hasShortcutHostPermission(int launcherUserId, @NonNull String callingPackage, int callingPid, int callingUid) { return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId, @@ -3241,7 +3315,14 @@ public class ShortcutService extends IShortcutService.Stub { user.clearLauncher(); } if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) { - // Nothing farther to do. + final ShortcutUser user = getUserShortcutsLocked(userId); + final ComponentName lastLauncher = user.getLastKnownLauncher(); + final ComponentName currentLauncher = getDefaultLauncher(userId); + if (currentLauncher == null || !currentLauncher.equals(lastLauncher)) { + // Default launcher is removed or changed, revoke all URI permissions. + mUriGrantsManagerInternal.revokeUriPermissionFromOwner(mUriPermissionOwner, + null, ~0, 0); + } return; } 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 6c769485f6ad..6a882985d305 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -94,6 +94,8 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.LauncherAppsService.LauncherAppsImpl; import com.android.server.pm.ShortcutUser.PackageWithUser; +import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.uri.UriPermissionOwner; import com.android.server.wm.ActivityTaskManagerInternal; import org.junit.Assert; @@ -630,6 +632,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected UsageStatsManagerInternal mMockUsageStatsManagerInternal; protected ActivityManagerInternal mMockActivityManagerInternal; protected ActivityTaskManagerInternal mMockActivityTaskManagerInternal; + protected UriGrantsManagerInternal mMockUriGrantsManagerInternal; + + protected UriPermissionOwner mUriPermissionOwner; protected static final String CALLING_PACKAGE_1 = "com.android.test.1"; protected static final int CALLING_UID_1 = 10001; @@ -771,6 +776,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class); mMockActivityManagerInternal = mock(ActivityManagerInternal.class); mMockActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class); + mMockUriGrantsManagerInternal = mock(UriGrantsManagerInternal.class); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal); @@ -782,6 +788,10 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal); LocalServices.removeServiceForTest(UserManagerInternal.class); LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal); + LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); + LocalServices.addService(UriGrantsManagerInternal.class, mMockUriGrantsManagerInternal); + + mUriPermissionOwner = new UriPermissionOwner(mMockUriGrantsManagerInternal, TAG); // Prepare injection values. @@ -2193,6 +2203,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { for (ShortcutInfo s : actualShortcuts) { assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource()); assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile()); + assertFalse("ID " + s.getId() + " shouldn't have icon URI", s.hasIconUri()); } return actualShortcuts; } @@ -2202,6 +2213,17 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { for (ShortcutInfo s : actualShortcuts) { assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource()); assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile()); + assertFalse("ID " + s.getId() + " shouldn't have icon URI", s.hasIconUri()); + } + return actualShortcuts; + } + + public static List<ShortcutInfo> assertAllHaveIconUri( + List<ShortcutInfo> actualShortcuts) { + for (ShortcutInfo s : actualShortcuts) { + assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource()); + assertFalse("ID " + s.getId() + " shouldn't have have icon FD", s.hasIconFile()); + assertTrue("ID " + s.getId() + " not have icon URI", s.hasIconUri()); } return actualShortcuts; } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 56460fb6f0a0..2cbb6d5c5bd6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -114,8 +114,11 @@ import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -133,6 +136,16 @@ import java.util.function.BiConsumer; */ @SmallTest public class ShortcutManagerTest1 extends BaseShortcutManagerTest { + + @Override + protected void tearDown() throws Exception { + deleteUriFile("file32x32.jpg"); + deleteUriFile("file64x64.jpg"); + deleteUriFile("file512x512.jpg"); + + super.tearDown(); + } + /** * Test for the first launch path, no settings file available. */ @@ -724,6 +737,17 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource( getTestContext().getResources(), R.drawable.black_512x512)); + // The corresponding files will be deleted in tearDown() + final Icon uri32x32 = Icon.createWithContentUri( + getFileUriFromResource("file32x32.jpg", R.drawable.black_32x32)); + final Icon uri64x64_maskable = Icon.createWithAdaptiveBitmapContentUri( + getFileUriFromResource("file64x64.jpg", R.drawable.black_64x64)); + final Icon uri512x512 = Icon.createWithContentUri( + getFileUriFromResource("file512x512.jpg", R.drawable.black_512x512)); + + doReturn(mUriPermissionOwner.getExternalToken()) + .when(mMockUriGrantsManagerInternal).newUriPermissionOwner(anyString()); + // Set from package 1 setCaller(CALLING_PACKAGE_1); assertTrue(mManager.setDynamicShortcuts(list( @@ -732,6 +756,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { makeShortcutWithIcon("bmp32x32", bmp32x32), makeShortcutWithIcon("bmp64x64", bmp64x64_maskable), makeShortcutWithIcon("bmp512x512", bmp512x512), + makeShortcutWithIcon("uri32x32", uri32x32), + makeShortcutWithIcon("uri64x64", uri64x64_maskable), + makeShortcutWithIcon("uri512x512", uri512x512), makeShortcut("none") ))); @@ -742,6 +769,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { "bmp32x32", "bmp64x64", "bmp512x512", + "uri32x32", + "uri64x64", + "uri512x512", "none"); // Call from another caller with the same ID, just to make sure storage is per-package. @@ -749,11 +779,15 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertTrue(mManager.setDynamicShortcuts(list( makeShortcutWithIcon("res32x32", res512x512), makeShortcutWithIcon("res64x64", res512x512), + makeShortcutWithIcon("uri32x32", uri512x512), + makeShortcutWithIcon("uri64x64", uri512x512), makeShortcutWithIcon("none", res512x512) ))); assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()), "res32x32", "res64x64", + "uri32x32", + "uri64x64", "none"); // Different profile. Note the names and the contents don't match. @@ -795,6 +829,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0))), "bmp512x512"); + assertShortcutIds(assertAllHaveIconUri( + list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri32x32", USER_0))), + "uri32x32"); + + assertShortcutIds(assertAllHaveIconUri( + list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri64x64", USER_0))), + "uri64x64"); + + assertShortcutIds(assertAllHaveIconUri( + list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri512x512", USER_0))), + "uri512x512"); + assertShortcutIds(assertAllHaveIconResId( list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0))), "res32x32"); @@ -860,15 +906,37 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { bmp = pfdToBitmap( mLauncherApps.getShortcutIconFd(CALLING_PACKAGE_1, "bmp32x32", HANDLE_USER_P0)); assertBitmapSize(128, 128, bmp); +/* + bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd( + getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri32x32", USER_0))); + assertBitmapSize(32, 32, bmp); + + bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd( + getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri64x64", USER_0))); + assertBitmapSize(64, 64, bmp); + + bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd( + getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri512x512", USER_0))); + assertBitmapSize(512, 512, bmp); +*/ - Drawable dr = mLauncherApps.getShortcutIconDrawable( + Drawable dr_bmp = mLauncherApps.getShortcutIconDrawable( makeShortcutWithIcon("bmp64x64", bmp64x64_maskable), 0); - assertTrue(dr instanceof AdaptiveIconDrawable); + assertTrue(dr_bmp instanceof AdaptiveIconDrawable); float viewportPercentage = 1 / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction()); assertEquals((int) (bmp64x64_maskable.getBitmap().getWidth() * viewportPercentage), - dr.getIntrinsicWidth()); + dr_bmp.getIntrinsicWidth()); + assertEquals((int) (bmp64x64_maskable.getBitmap().getHeight() * viewportPercentage), + dr_bmp.getIntrinsicHeight()); +/* + Drawable dr_uri = mLauncherApps.getShortcutIconDrawable( + makeShortcutWithIcon("uri64x64", uri64x64_maskable), 0); + assertTrue(dr_uri instanceof AdaptiveIconDrawable); + assertEquals((int) (bmp64x64_maskable.getBitmap().getWidth() * viewportPercentage), + dr_uri.getIntrinsicWidth()); assertEquals((int) (bmp64x64_maskable.getBitmap().getHeight() * viewportPercentage), - dr.getIntrinsicHeight()); + dr_uri.getIntrinsicHeight()); +*/ } public void testCleanupDanglingBitmaps() throws Exception { @@ -1274,6 +1342,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { makeShortcut("s1") ))); + // Set uri icon + assertTrue(mManager.updateShortcuts(list( + new ShortcutInfo.Builder(mClientContext, "s1") + .setIcon(Icon.createWithContentUri("test_uri")) + .build() + ))); + mService.waitForBitmapSavesForTest(); + assertWith(getCallerShortcuts()) + .forShortcutWithId("s1", si -> { + assertTrue(si.hasIconUri()); + assertEquals("test_uri", si.getIconUri()); + }); // Set resource icon assertTrue(mManager.updateShortcuts(list( new ShortcutInfo.Builder(mClientContext, "s1") @@ -1287,6 +1367,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertEquals(R.drawable.black_32x32, si.getIconResourceId()); }); mService.waitForBitmapSavesForTest(); + + mInjectedCurrentTimeMillis += INTERVAL; // reset throttling + // Set bitmap icon assertTrue(mManager.updateShortcuts(list( new ShortcutInfo.Builder(mClientContext, "s1") @@ -1300,9 +1383,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertTrue(si.hasIconFile()); }); - mInjectedCurrentTimeMillis += INTERVAL; // reset throttling - - // Do it again, with the reverse order (bitmap -> icon) + // Do it again, with the reverse order (bitmap -> resource -> uri) assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s1") ))); @@ -1320,6 +1401,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertTrue(si.hasIconFile()); }); + mInjectedCurrentTimeMillis += INTERVAL; // reset throttling + // Set resource icon assertTrue(mManager.updateShortcuts(list( new ShortcutInfo.Builder(mClientContext, "s1") @@ -1332,6 +1415,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertTrue(si.hasIconResource()); assertEquals(R.drawable.black_32x32, si.getIconResourceId()); }); + // Set uri icon + assertTrue(mManager.updateShortcuts(list( + new ShortcutInfo.Builder(mClientContext, "s1") + .setIcon(Icon.createWithContentUri("test_uri")) + .build() + ))); + mService.waitForBitmapSavesForTest(); + assertWith(getCallerShortcuts()) + .forShortcutWithId("s1", si -> { + assertTrue(si.hasIconUri()); + assertEquals("test_uri", si.getIconUri()); + }); }); } @@ -8499,4 +8594,26 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } } } + + private Uri getFileUriFromResource(String fileName, int resId) throws IOException { + File file = new File(getTestContext().getFilesDir(), fileName); + // Make sure we are not leaving phantom files behind. + assertFalse(file.exists()); + try (InputStream source = getTestContext().getResources().openRawResource(resId); + OutputStream target = new FileOutputStream(file)) { + byte[] buffer = new byte[1024]; + for (int len = source.read(buffer); len >= 0; len = source.read(buffer)) { + target.write(buffer, 0, len); + } + } + assertTrue(file.exists()); + return Uri.fromFile(file); + } + + private void deleteUriFile(String fileName) { + File file = new File(getTestContext().getFilesDir(), fileName); + if (file.exists()) { + file.delete(); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 7b101c728d0f..ca7704950b51 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -257,6 +257,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); si.setIconResourceId(456); + si.setIconUri("test_uri"); si = parceled(si); @@ -279,6 +280,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); + assertEquals("test_uri", si.getIconUri()); assertEquals(0, si.getTitleResId()); assertEquals(null, si.getTitleResName()); @@ -310,6 +312,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); si.setIconResourceId(456); + si.setIconUri("test_uri"); lookupAndFillInResourceNames(si); @@ -335,6 +338,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); + assertEquals("test_uri", si.getIconUri()); } public void testShortcutInfoClone() { @@ -359,6 +363,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); sorig.setIconResourceId(456); + sorig.setIconUri("test_uri"); lookupAndFillInResourceNames(sorig); @@ -386,6 +391,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); + assertEquals("test_uri", si.getIconUri()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); @@ -407,6 +413,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); @@ -428,6 +435,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); @@ -450,6 +458,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); @@ -474,6 +483,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); @@ -499,6 +509,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); sorig.setIconResourceId(456); + sorig.setIconUri("test_uri"); lookupAndFillInResourceNames(sorig); @@ -526,6 +537,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals("abc", si.getBitmapPath()); assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); + assertEquals("test_uri", si.getIconUri()); si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); @@ -547,6 +559,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); @@ -570,6 +583,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); @@ -593,6 +607,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); assertEquals(456, si.getIconResourceId()); assertEquals(null, si.getIconResName()); @@ -657,6 +672,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); sorig.setIconResourceId(456); + sorig.setIconUri("test_uri"); lookupAndFillInResourceNames(sorig); @@ -677,6 +693,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(0, si.getIconResourceId()); assertEquals(null, si.getIconResName()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") @@ -787,6 +804,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); sorig.setIconResourceId(456); + sorig.setIconUri("test_uri"); ShortcutInfo si; @@ -804,6 +822,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(0, si.getIconResourceId()); assertEquals(null, si.getIconResName()); assertEquals(null, si.getBitmapPath()); + assertNull(si.getIconUri()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") @@ -977,6 +996,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags()); assertNotNull(si.getBitmapPath()); // Something should be set. assertEquals(0, si.getIconResourceId()); + assertNull(si.getIconUri()); assertTrue(si.getLastChangedTimestamp() < now); // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts @@ -1053,6 +1073,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { si.getFlags()); assertNotNull(si.getBitmapPath()); // Something should be set. assertEquals(0, si.getIconResourceId()); + assertNull(si.getIconUri()); assertTrue(si.getLastChangedTimestamp() < now); dumpUserFile(USER_10); @@ -1125,6 +1146,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_RES | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags()); assertNull(si.getBitmapPath()); + assertNull(si.getIconUri()); assertEquals(R.drawable.black_32x32, si.getIconResourceId()); assertTrue(si.getLastChangedTimestamp() < now); @@ -1134,6 +1156,94 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(1, si.getRank()); } + public void testShortcutInfoSaveAndLoad_uri() throws InterruptedException { + mRunningUsers.put(USER_10, true); + + setCaller(CALLING_PACKAGE_1, USER_10); + + final Icon uriIcon = Icon.createWithContentUri("test_uri"); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setIcon(uriIcon) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setRank(123) + .setExtras(pb) + .build(); + sorig.setTimestamp(mInjectedCurrentTimeMillis); + + final Icon uriMaskableIcon = Icon.createWithAdaptiveBitmapContentUri("uri_maskable"); + + ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext) + .setId("id2") + .setTitle("x") + .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setRank(456) + .setIcon(uriMaskableIcon) + .build(); + sorig2.setTimestamp(mInjectedCurrentTimeMillis); + + mManager.addDynamicShortcuts(list(sorig, sorig2)); + + mInjectedCurrentTimeMillis += 1; + final long now = mInjectedCurrentTimeMillis; + mInjectedCurrentTimeMillis += 1; + + // Save and load. + mService.saveDirtyInfo(); + initService(); + mService.handleUnlockUser(USER_10); + + ShortcutInfo si; + si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10); + + assertEquals(USER_10, si.getUserId()); + assertEquals(HANDLE_USER_10, si.getUserHandle()); + assertEquals(CALLING_PACKAGE_1, si.getPackage()); + assertEquals("id", si.getId()); + assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName()); + assertEquals(null, si.getIcon()); + assertEquals(10, si.getTitleResId()); + assertEquals("r10", si.getTitleResName()); + assertEquals(11, si.getTextResId()); + assertEquals("r11", si.getTextResName()); + assertEquals(12, si.getDisabledMessageResourceId()); + assertEquals("r12", si.getDisabledMessageResName()); + assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(0, si.getRank()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_URI + | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags()); + assertNull(si.getBitmapPath()); + assertNull(si.getIconResName()); + assertEquals(0, si.getIconResourceId()); + assertEquals("test_uri", si.getIconUri()); + assertTrue(si.getLastChangedTimestamp() < now); + + // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts + // to test it. + si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10); + assertEquals(1, si.getRank()); + assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_URI + | ShortcutInfo.FLAG_STRINGS_RESOLVED | ShortcutInfo.FLAG_ADAPTIVE_BITMAP, + si.getFlags()); + assertNull(si.getBitmapPath()); + assertNull(si.getIconResName()); + assertEquals(0, si.getIconResourceId()); + assertEquals("uri_maskable", si.getIconUri()); + } + public void testShortcutInfoSaveAndLoad_forBackup() { setCaller(CALLING_PACKAGE_1, USER_0); @@ -1196,6 +1306,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { | ShortcutInfo.FLAG_SHADOW , si.getFlags()); assertNull(si.getBitmapPath()); // No icon. assertEquals(0, si.getIconResourceId()); + assertNull(si.getIconUri()); // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0); @@ -1265,6 +1376,77 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertNull(si.getBitmapPath()); // No icon. assertEquals(0, si.getIconResourceId()); assertEquals(null, si.getIconResName()); + assertNull(si.getIconUri()); + + // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank. + si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0); + assertEquals(0, si.getRank()); + } + + public void testShortcutInfoSaveAndLoad_forBackup_uri() { + setCaller(CALLING_PACKAGE_1, USER_0); + + final Icon uriIcon = Icon.createWithContentUri("test_uri"); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setIcon(uriIcon) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setRank(123) + .setExtras(pb) + .build(); + + ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext) + .setId("id2") + .setTitle("x") + .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setRank(456) + .build(); + + mManager.addDynamicShortcuts(list(sorig, sorig2)); + + // Dynamic shortcuts won't be backed up, so we need to pin it. + setCaller(LAUNCHER_1, USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id", "id2"), HANDLE_USER_0); + + // Do backup & restore. + backupAndRestore(); + + mService.handleUnlockUser(USER_0); // Load user-0. + + ShortcutInfo si; + si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0); + + assertEquals(CALLING_PACKAGE_1, si.getPackage()); + assertEquals("id", si.getId()); + assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName()); + assertEquals(null, si.getIcon()); + assertEquals(10, si.getTitleResId()); + assertEquals("r10", si.getTitleResName()); + assertEquals(11, si.getTextResId()); + assertEquals("r11", si.getTextResName()); + assertEquals(12, si.getDisabledMessageResourceId()); + assertEquals("r12", si.getDisabledMessageResName()); + assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(0, si.getRank()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED + | ShortcutInfo.FLAG_SHADOW , si.getFlags()); + assertNull(si.getBitmapPath()); // No icon. + assertEquals(0, si.getIconResourceId()); + assertEquals(null, si.getIconResName()); + assertNull(si.getIconUri()); // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 8e7804786b38..010f8ac334a7 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -390,7 +390,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { "title", 0, "titleResName", "text", 0, "textResName", "disabledMessage", 0, "disabledMessageResName", null, null, 0, null, 0, 0, - 0, "iconResName", "bitmapPath", 0, + 0, "iconResName", "bitmapPath", null, 0, null, null); return si; } |