ShortcutManager: finishing touches
- Change back the throttling quota to 10 calls / day
- Foreground apps are no longer throttled, and when an app comes to
foreground the call counter will be reset.
- When the system locale changes, reset throttling for all packages
for all users.
See LocalService.onSystemLocaleChangedNoLock() for how it's performed.
Because the reset must happen before any other apps have a chance to
publish shortcuts, the logic is not straightforward.
- Added an internal API to reset the throttling upon inline-reply
from a notification.
- Stop supporting icons from "content:" URIs
- Improved javadoc on several APIs.
Also internal refactor needed to this:
- ShortcutUser.getAllPackages()/getAllLaunchers() are no longer
accessible to outer code to prevent accidentally adding/removing the
content. Outer code should use forAllPackages() / forAllLaunchers().
Bug 27923857
Change-Id: I002511193d1d33718163bb1dabe77610bde58198
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 9c90346..2ba24f6 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -49,6 +49,8 @@
void resetThrottling(); // system only API for developer opsions
+ void onApplicationActive(String packageName, int userId); // system only API for sysUI
+
byte[] getBackupPayload(int user);
void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 9b1d0f7..bd8cae2 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
@@ -35,8 +34,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Set;
// TODO Enhance javadoc
@@ -307,14 +304,6 @@
case Icon.TYPE_RESOURCE:
case Icon.TYPE_BITMAP:
break; // OK
- case Icon.TYPE_URI:
- if (ContentResolver.SCHEME_CONTENT.equals(icon.getUri().getScheme())) {
- break;
- }
- // Note "file:" is not supported, because depending on the path, system server
- // cannot access it. // TODO Revisit "file:" icon support
-
- // fall through
default:
throw getInvalidIconException();
}
@@ -374,6 +363,12 @@
* Optionally sets the target activity. If it's not set, and if the caller application
* has multiple launcher icons, this shortcut will be shown on all those icons.
* If it's set, this shortcut will be only shown on this activity.
+ *
+ * <p>The package name of the target activity must match the package name of the shortcut
+ * publisher.
+ *
+ * <p>This has nothing to do with the activity that this shortcut will launch. This is
+ * a hint to the launcher app about which launcher icon to associate this shortcut with.
*/
@NonNull
public Builder setActivityComponent(@NonNull ComponentName activityComponent) {
@@ -385,11 +380,8 @@
* Optionally sets an icon.
*
* <ul>
- * <li>Tints are not supported.
- * <li>Bitmaps, resources and "content:" URIs are supported.
- * <li>"content:" URI will be fetched when a shortcut is registered to
- * {@link ShortcutManager}. Changing the content from the same URI later will
- * not be reflected to launcher icons.
+ * <li>Tints set by {@link Icon#setTint} or {@link Icon#setTintList} are not supported.
+ * <li>Bitmaps and resources are supported, but "content:" URIs are not supported.
* </ul>
*
* <p>For performance reasons, icons will <b>NOT</b> be available on instances
@@ -498,6 +490,11 @@
/**
* Return the target activity, which may be null, in which case the shortcut is not associated
* with a specific activity.
+ *
+ * <p>This has nothing to do with the activity that this shortcut will launch. This is
+ * a hint to the launcher app that on which launcher icon this shortcut should be shown.
+ *
+ * @see Builder#setActivityComponent
*/
@Nullable
public ComponentName getActivityComponent() {
@@ -550,6 +547,10 @@
*
* <p>All shortcuts must have an intent, but this method will return null when
* {@link #hasKeyFieldsOnly()} is true.
+ *
+ * <p>Launcher apps <b>cannot</b> see the intent. If a {@link ShortcutInfo} is obtained via
+ * {@link LauncherApps}, then this method will always return null. Launcher apps can only
+ * start a shortcut intent with {@link LauncherApps#startShortcut}.
*/
@Nullable
public Intent getIntent() {
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 75803d3..ab0367d 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -62,13 +62,17 @@
* <h3>Rate limiting</h3>
*
* Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)},
- * and {@link #updateShortcuts(List)} will be
+ * and {@link #updateShortcuts(List)} from <b>background applications</b> will be
* rate-limited. An application can call these methods at most
* {@link #getRemainingCallCount()} times until the rate-limiting counter is reset,
- * which happens every hour.
+ * which happens at a certain time every day.
*
* <p>An application can use {@link #getRateLimitResetTime()} to get the next reset time.
*
+ * <p>Foreground applications (i.e. ones with a foreground activity or a foreground services)
+ * will not be throttled. Also, when an application comes to foreground,
+ * {@link #getRemainingCallCount()} will be reset to the initial value.
+ *
* <p>For testing purposes, use "Developer Options" (found in the Settings menu) to reset the
* internal rate-limiting counter. Automated tests can use the following ADB shell command to
* achieve the same effect:</p>
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index dc3d317..3f8bad1 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
import java.util.List;
@@ -68,4 +67,10 @@
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage);
+
+ /**
+ * Called by AM when the system locale changes *within the AM lock*. ABSOLUTELY do not take
+ * any locks in this method.
+ */
+ public abstract void onSystemLocaleChangedNoLock();
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 22cf279..040da4d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1989,6 +1989,11 @@
<permission android:name="android.permission.UPDATE_CONFIG"
android:protectionLevel="signature|privileged" />
+ <!-- Allows the system to reset throttling in shortcut manager.
+ @hide -->
+ <permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING"
+ android:protectionLevel="signature" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 21a9f2c..233a3f8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -141,6 +141,7 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -18191,6 +18192,12 @@
null, AppOpsManager.OP_NONE, null, false, false,
MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
+ // Tell the shortcut manager that the system locale changed. It needs to know
+ // it before any other apps receive ACTION_LOCALE_CHANGED, which is why
+ // we "push" from here, rather than having the service listen to the broadcast.
+ LocalServices.getService(ShortcutServiceInternal.class)
+ .onSystemLocaleChangedNoLock();
+
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
if (!mProcessesReady) {
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index c6d66fe..76d47a8 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -57,15 +57,18 @@
*/
final private ArrayMap<PackageWithUser, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
- private ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
+ private ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
+ @UserIdInt int ownerUserId, @NonNull String packageName,
@UserIdInt int launcherUserId, ShortcutPackageInfo spi) {
- super(launcherUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
+ super(shortcutUser, launcherUserId, packageName,
+ spi != null ? spi : ShortcutPackageInfo.newEmpty());
mOwnerUserId = ownerUserId;
}
- public ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
+ public ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
+ @UserIdInt int ownerUserId, @NonNull String packageName,
@UserIdInt int launcherUserId) {
- this(ownerUserId, packageName, launcherUserId, null);
+ this(shortcutUser, ownerUserId, packageName, launcherUserId, null);
}
@Override
@@ -179,8 +182,8 @@
/**
* Load.
*/
- public static ShortcutLauncher loadFromXml(XmlPullParser parser, int ownerUserId,
- boolean fromBackup) throws IOException, XmlPullParserException {
+ public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser,
+ int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException {
final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
ATTR_PACKAGE_NAME);
@@ -189,8 +192,8 @@
fromBackup ? ownerUserId
: ShortcutService.parseIntAttribute(parser, ATTR_LAUNCHER_USER_ID, ownerUserId);
- final ShortcutLauncher ret = new ShortcutLauncher(launcherUserId, launcherPackageName,
- launcherUserId);
+ final ShortcutLauncher ret = new ShortcutLauncher(shortcutUser, launcherUserId,
+ launcherPackageName, launcherUserId);
ArraySet<String> ids = null;
final int outerDepth = parser.getDepth();
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index d7f8cc6..151f61e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -38,7 +38,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
@@ -95,12 +94,21 @@
*/
private long mLastResetTime;
- private ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) {
- super(packageUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
+ private final int mPackageUid;
+
+ private long mLastKnownForegroundElapsedTime;
+
+ private ShortcutPackage(ShortcutService s, ShortcutUser shortcutUser,
+ int packageUserId, String packageName, ShortcutPackageInfo spi) {
+ super(shortcutUser, packageUserId, packageName,
+ spi != null ? spi : ShortcutPackageInfo.newEmpty());
+
+ mPackageUid = s.injectGetPackageUid(packageName, packageUserId);
}
- public ShortcutPackage(int packageUserId, String packageName) {
- this(packageUserId, packageName, null);
+ public ShortcutPackage(ShortcutService s, ShortcutUser shortcutUser,
+ int packageUserId, String packageName) {
+ this(s, shortcutUser, packageUserId, packageName, null);
}
@Override
@@ -109,6 +117,10 @@
return getPackageUserId();
}
+ public int getPackageUid() {
+ return mPackageUid;
+ }
+
/**
* Called when a shortcut is about to be published. At this point we know the publisher package
* exists (as opposed to Launcher trying to fetch shortcuts from a non-existent package), so
@@ -274,18 +286,12 @@
}
// Then, for the pinned set for each launcher, set the pin flag one by one.
- final ArrayMap<ShortcutUser.PackageWithUser, ShortcutLauncher> launchers =
- s.getUserShortcutsLocked(getPackageUserId()).getAllLaunchers();
-
- for (int l = launchers.size() - 1; l >= 0; l--) {
- // Note even if a launcher that hasn't been installed can still pin shortcuts.
-
- final ShortcutLauncher launcherShortcuts = launchers.valueAt(l);
+ s.getUserShortcutsLocked(getPackageUserId()).forAllLaunchers(launcherShortcuts -> {
final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(
getPackageName(), getPackageUserId());
if (pinned == null || pinned.size() == 0) {
- continue;
+ return;
}
for (int i = pinned.size() - 1; i >= 0; i--) {
final String id = pinned.valueAt(i);
@@ -299,7 +305,7 @@
}
si.addFlags(ShortcutInfo.FLAG_PINNED);
}
- }
+ });
// Lastly, remove the ones that are no longer pinned nor dynamic.
removeOrphans(s);
@@ -307,8 +313,28 @@
/**
* Number of calls that the caller has made, since the last reset.
+ *
+ * <p>This takes care of the resetting the counter for foreground apps as well as after
+ * locale changes.
*/
public int getApiCallCount(@NonNull ShortcutService s) {
+ mShortcutUser.resetThrottlingIfNeeded(s);
+
+ // Reset the counter if:
+ // - the package is in foreground now.
+ // - the package is *not* in foreground now, but was in foreground at some point
+ // since the previous time it had been.
+ if (s.isUidForegroundLocked(mPackageUid)
+ || mLastKnownForegroundElapsedTime
+ < s.getUidLastForegroundElapsedTimeLocked(mPackageUid)) {
+ mLastKnownForegroundElapsedTime = s.injectElapsedRealtime();
+ resetRateLimiting(s);
+ }
+
+ // Note resetThrottlingIfNeeded() and resetRateLimiting() will set 0 to mApiCallCount,
+ // but we just can't return 0 at this point, because we may have to update
+ // mLastResetTime.
+
final long last = s.getLastResetTimeLocked();
final long now = s.injectCurrentTimeMillis();
@@ -335,16 +361,30 @@
/**
* If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
* and return true. Otherwise just return false.
+ *
+ * <p>This takes care of the resetting the counter for foreground apps as well as after
+ * locale changes, which is done internally by {@link #getApiCallCount}.
*/
public boolean tryApiCall(@NonNull ShortcutService s) {
if (getApiCallCount(s) >= s.mMaxUpdatesPerInterval) {
return false;
}
mApiCallCount++;
+ s.scheduleSaveUser(getOwnerUserId());
return true;
}
- public void resetRateLimitingForCommandLine() {
+ public void resetRateLimiting(@NonNull ShortcutService s) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "resetRateLimiting: " + getPackageName());
+ }
+ if (mApiCallCount > 0) {
+ mApiCallCount = 0;
+ s.scheduleSaveUser(getOwnerUserId());
+ }
+ }
+
+ public void resetRateLimitingForCommandLineNoSaving() {
mApiCallCount = 0;
mLastResetTime = 0;
}
@@ -451,6 +491,8 @@
pw.print(prefix);
pw.print("Package: ");
pw.print(getPackageName());
+ pw.print(" UID: ");
+ pw.print(mPackageUid);
pw.println();
pw.print(prefix);
@@ -459,6 +501,13 @@
pw.print(getApiCallCount(s));
pw.println();
+ // getApiCallCount() may have updated mLastKnownForegroundElapsedTime.
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("Last known FG: ");
+ pw.print(mLastKnownForegroundElapsedTime);
+ pw.println();
+
// This should be after getApiCallCount(), which may update it.
pw.print(prefix);
pw.print(" ");
@@ -571,14 +620,15 @@
out.endTag(null, TAG_SHORTCUT);
}
- public static ShortcutPackage loadFromXml(ShortcutService s, XmlPullParser parser,
- int ownerUserId, boolean fromBackup)
+ public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortcutUser,
+ XmlPullParser parser, boolean fromBackup)
throws IOException, XmlPullParserException {
final String packageName = ShortcutService.parseStringAttribute(parser,
ATTR_NAME);
- final ShortcutPackage ret = new ShortcutPackage(ownerUserId, packageName);
+ final ShortcutPackage ret = new ShortcutPackage(s, shortcutUser,
+ shortcutUser.getUserId(), packageName);
ret.mDynamicShortcutCount =
ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
@@ -602,7 +652,8 @@
ret.getPackageInfo().loadFromXml(parser, fromBackup);
continue;
case TAG_SHORTCUT:
- final ShortcutInfo si = parseShortcut(parser, packageName, ownerUserId);
+ final ShortcutInfo si = parseShortcut(parser, packageName,
+ shortcutUser.getUserId());
// Don't use addShortcut(), we don't need to save the icon.
ret.mShortcuts.put(si.getId(), si);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index f31dd17..6fbdb82 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -34,8 +34,12 @@
private final ShortcutPackageInfo mPackageInfo;
- protected ShortcutPackageItem(int packageUserId, @NonNull String packageName,
+ protected final ShortcutUser mShortcutUser;
+
+ protected ShortcutPackageItem(@NonNull ShortcutUser shortcutUser,
+ int packageUserId, @NonNull String packageName,
@NonNull ShortcutPackageInfo packageInfo) {
+ mShortcutUser = shortcutUser;
mPackageUserId = packageUserId;
mPackageName = Preconditions.checkStringNotEmpty(packageName);
mPackageInfo = Preconditions.checkNotNull(packageInfo);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c0874ef..0f17804 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -19,9 +19,10 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.app.AppGlobals;
+import android.app.IUidObserver;
import android.content.ComponentName;
-import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -39,11 +40,9 @@
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.graphics.drawable.Icon;
-import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
@@ -56,16 +55,18 @@
import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ShellCommand;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.text.format.Time;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.TypedValue;
import android.util.Xml;
@@ -73,6 +74,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
@@ -102,6 +104,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -124,12 +127,13 @@
static final boolean DEBUG = false; // STOPSHIP if true
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
+ static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
@VisibleForTesting
- static final long DEFAULT_RESET_INTERVAL_SEC = 60 * 60; // 1 hour
+ static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
@VisibleForTesting
- static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 2;
+ static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10;
@VisibleForTesting
static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
@@ -162,6 +166,7 @@
private static final String TAG_ROOT = "root";
private static final String TAG_LAST_RESET_TIME = "last_reset_time";
+ private static final String TAG_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale_seq_no";
private static final String ATTR_VALUE = "value";
@@ -256,8 +261,23 @@
private final UserManager mUserManager;
@GuardedBy("mLock")
+ final SparseIntArray mUidState = new SparseIntArray();
+
+ @GuardedBy("mLock")
+ final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray();
+
+ @GuardedBy("mLock")
private List<Integer> mDirtyUserIds = new ArrayList<>();
+ /**
+ * A counter that increments every time the system locale changes. We keep track of it to reset
+ * throttling counters on the first call from each package after the last locale change.
+ *
+ * We need this mechanism because we can't do much in the locale change callback, which is
+ * {@link ShortcutServiceInternal#onSystemLocaleChangedNoLock()}.
+ */
+ private final AtomicLong mLocaleChangeSequenceNumber = new AtomicLong();
+
private static final int PACKAGE_MATCH_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -283,6 +303,9 @@
@GuardedBy("mStatLock")
private final long[] mDurationStats = new long[Stats.COUNT];
+ private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+
public ShortcutService(Context context) {
this(context, BackgroundThread.get().getLooper());
}
@@ -297,6 +320,9 @@
mUserManager = context.getSystemService(UserManager.class);
mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
+
+ injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
+ | ActivityManager.UID_OBSERVER_GONE);
}
void logDurationStat(int statId, long start) {
@@ -306,6 +332,59 @@
}
}
+ public long getLocaleChangeSequenceNumber() {
+ return mLocaleChangeSequenceNumber.get();
+ }
+
+ final private IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+ handleOnUidStateChanged(uid, procState);
+ }
+
+ @Override public void onUidGone(int uid) throws RemoteException {
+ handleOnUidStateChanged(uid, ActivityManager.MAX_PROCESS_STATE);
+ }
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ }
+ };
+
+ void handleOnUidStateChanged(int uid, int procState) {
+ if (DEBUG_PROCSTATE) {
+ Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
+ }
+ synchronized (mLock) {
+ mUidState.put(uid, procState);
+
+ // We need to keep track of last time an app comes to foreground.
+ // See ShortcutPackage.getApiCallCount() for how it's used.
+ // It doesn't have to be persisted, but it needs to be the elapsed time.
+ if (isProcessStateForeground(procState)) {
+ mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
+ }
+ }
+ }
+
+ private boolean isProcessStateForeground(int processState) {
+ return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
+ }
+
+ boolean isUidForegroundLocked(int uid) {
+ if (uid == Process.SYSTEM_UID) {
+ // IUidObserver doesn't report the state of SYSTEM, but it always has bound services,
+ // so it's foreground anyway.
+ return true;
+ }
+ return isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE));
+ }
+
+ long getUidLastForegroundElapsedTimeLocked(int uid) {
+ return mUidLastForegroundElapsedTime.get(uid);
+ }
+
/**
* System service lifecycle.
*/
@@ -596,6 +675,8 @@
// Body.
writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
+ writeTagValue(out, TAG_LOCALE_CHANGE_SEQUENCE_NUMBER,
+ mLocaleChangeSequenceNumber.get());
// Epilogue.
out.endTag(null, TAG_ROOT);
@@ -640,6 +721,9 @@
case TAG_LAST_RESET_TIME:
mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
break;
+ case TAG_LOCALE_CHANGE_SEQUENCE_NUMBER:
+ mLocaleChangeSequenceNumber.set(parseLongAttribute(parser, ATTR_VALUE));
+ break;
default:
Slog.e(TAG, "Invalid tag: " + tag);
break;
@@ -993,20 +1077,6 @@
bitmap = icon.getBitmap(); // Don't recycle in this case.
break;
}
- case Icon.TYPE_URI: {
- final Uri uri = ContentProvider.maybeAddUserId(icon.getUri(), userId);
-
- try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
-
- bitmapToRecycle = BitmapFactory.decodeStream(is);
- bitmap = bitmapToRecycle;
-
- } catch (IOException e) {
- Slog.e(TAG, "Unable to load icon from " + uri);
- return;
- }
- break;
- }
default:
// This shouldn't happen because we've already validated the icon, but
// just in case.
@@ -1122,6 +1192,24 @@
Preconditions.checkState(isCallerSystem(), "Caller must be system");
}
+ private void enforceResetThrottlingPermission() {
+ if (isCallerSystem()) {
+ return;
+ }
+ injectEnforceCallingPermission(
+ android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null);
+ }
+
+ /**
+ * Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse
+ * mockito. So instead we extracted it here and override it in the tests.
+ */
+ @VisibleForTesting
+ void injectEnforceCallingPermission(
+ @NonNull String permission, @Nullable String message) {
+ mContext.enforceCallingPermission(permission, message);
+ }
+
private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1481,6 +1569,23 @@
Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
}
+ void resetPackageThrottling(String packageName, int userId) {
+ synchronized (mLock) {
+ getPackageShortcutsLocked(packageName, userId)
+ .resetRateLimitingForCommandLineNoSaving();
+ saveUserLocked(userId);
+ }
+ }
+
+ @Override
+ public void onApplicationActive(String packageName, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
+ }
+ enforceResetThrottlingPermission();
+ resetPackageThrottling(packageName, userId);
+ }
+
// We override this method in unit tests to do a simpler check.
boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
return hasShortcutHostPermissionInner(callingPackage, userId);
@@ -1593,15 +1698,11 @@
user.removeLauncher(packageUserId, packageName);
// Then remove pinned shortcuts from all launchers.
- final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = user.getAllLaunchers();
- for (int i = launchers.size() - 1; i >= 0; i--) {
- launchers.valueAt(i).cleanUpPackage(packageName, packageUserId);
- }
- // Now there may be orphan shortcuts because we removed pinned shortucts at the previous
+ user.forAllLaunchers(l -> l.cleanUpPackage(packageName, packageUserId));
+
+ // Now there may be orphan shortcuts because we removed pinned shortcuts at the previous
// step. Remove them too.
- for (int i = user.getAllPackages().size() - 1; i >= 0; i--) {
- user.getAllPackages().valueAt(i).refreshPinnedFlags(this);
- }
+ user.forAllPackages(p -> p.refreshPinnedFlags(this));
scheduleSaveUser(owningUserId);
@@ -1644,13 +1745,12 @@
callingPackage, packageName, shortcutIds, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
} else {
- final ArrayMap<String, ShortcutPackage> packages =
- getUserShortcutsLocked(userId).getAllPackages();
- for (int i = packages.size() - 1; i >= 0; i--) {
+ final List<String> shortcutIdsF = shortcutIds;
+ getUserShortcutsLocked(userId).forAllPackages(p -> {
getShortcutsInnerLocked(launcherUserId,
- callingPackage, packages.keyAt(i), shortcutIds, changedSince,
+ callingPackage, p.getPackageName(), shortcutIdsF, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
- }
+ });
}
}
return ret;
@@ -1819,6 +1919,29 @@
@NonNull String callingPackage) {
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
+
+ /**
+ * Called by AM when the system locale changes *within the AM lock. ABSOLUTELY do not take
+ * any locks in this method.
+ */
+ @Override
+ public void onSystemLocaleChangedNoLock() {
+ // DO NOT HOLD ANY LOCKS HERE.
+
+ // We want to reset throttling for all packages for all users. But we can't just do so
+ // here because:
+ // - We can't load/save users that are locked.
+ // - Even for loaded users, resetting the counters would require us to hold mLock.
+ //
+ // So we use a "pull" model instead. In here, we just increment the "locale change
+ // sequence number". Each ShortcutUser has the "last known locale change sequence".
+ //
+ // This allows ShortcutUser's to detect the system locale change, so they can reset
+ // counters.
+
+ mLocaleChangeSequenceNumber.incrementAndGet();
+ postToHandler(() -> scheduleSaveBaseState());
+ }
}
/**
@@ -2087,11 +2210,11 @@
+ android.Manifest.permission.DUMP);
return;
}
- dumpInner(pw);
+ dumpInner(pw, args);
}
@VisibleForTesting
- void dumpInner(PrintWriter pw) {
+ void dumpInner(PrintWriter pw, String[] args) {
synchronized (mLock) {
final long now = injectCurrentTimeMillis();
pw.print("Now: [");
@@ -2115,6 +2238,9 @@
pw.print(next);
pw.print("] ");
pw.print(formatTime(next));
+
+ pw.print(" Locale change seq#: ");
+ pw.print(mLocaleChangeSequenceNumber.get());
pw.println();
pw.print(" Config:");
@@ -2149,6 +2275,24 @@
pw.println();
mUsers.valueAt(i).dump(this, pw, " ");
}
+
+ pw.println();
+ pw.println(" UID state:");
+
+ for (int i = 0; i < mUidState.size(); i++) {
+ final int uid = mUidState.keyAt(i);
+ final int state = mUidState.valueAt(i);
+ pw.print(" UID=");
+ pw.print(uid);
+ pw.print(" state=");
+ pw.print(state);
+ if (isProcessStateForeground(state)) {
+ pw.print(" [FG]");
+ }
+ pw.print(" last FG=");
+ pw.print(mUidLastForegroundElapsedTime.get(uid));
+ pw.println();
+ }
}
}
@@ -2316,10 +2460,7 @@
Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName);
- synchronized (mLock) {
- getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine();
- saveUserLocked(mUserId);
- }
+ resetPackageThrottling(packageName, mUserId);
}
private void handleOverrideConfig() throws CommandException {
@@ -2404,6 +2545,11 @@
return System.currentTimeMillis();
}
+ @VisibleForTesting
+ long injectElapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
// Injection point.
@VisibleForTesting
int injectBinderCallingUid() {
@@ -2451,6 +2597,14 @@
}
@VisibleForTesting
+ void injectRegisterUidObserver(IUidObserver observer, int which) {
+ try {
+ ActivityManagerNative.getDefault().registerUidObserver(observer, which);
+ } catch (RemoteException shouldntHappen) {
+ }
+ }
+
+ @VisibleForTesting
PackageManagerInternal injectPackageManagerInternal() {
return mPackageManagerInternal;
}
@@ -2500,7 +2654,7 @@
final ShortcutUser user = mUsers.get(userId);
if (user == null) return null;
- final ShortcutPackage pkg = user.getAllPackages().get(packageName);
+ final ShortcutPackage pkg = user.getAllPackagesForTest().get(packageName);
if (pkg == null) return null;
return pkg.findShortcutById(shortcutId);
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 3d2e2ec..7d19a78 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -21,7 +21,9 @@
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import libcore.util.Objects;
@@ -45,6 +47,7 @@
private static final String TAG_LAUNCHER = "launcher";
private static final String ATTR_VALUE = "value";
+ private static final String ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale-seq-no";
static final class PackageWithUser {
final int userId;
@@ -89,10 +92,15 @@
private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();
+ private final SparseArray<ShortcutPackage> mPackagesFromUid = new SparseArray<>();
+
private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>();
+ /** Default launcher that can access the launcher apps APIs. */
private ComponentName mLauncherComponent;
+ private long mKnownLocaleChangeSequenceNumber;
+
public ShortcutUser(int userId) {
mUserId = userId;
}
@@ -101,7 +109,10 @@
return mUserId;
}
- public ArrayMap<String, ShortcutPackage> getAllPackages() {
+ // We don't expose this directly to non-test code because only ShortcutUser should add to/
+ // remove from it.
+ @VisibleForTesting
+ ArrayMap<String, ShortcutPackage> getAllPackagesForTest() {
return mPackages;
}
@@ -113,7 +124,10 @@
return removed;
}
- public ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchers() {
+ // We don't expose this directly to non-test code because only ShortcutUser should add to/
+ // remove from it.
+ @VisibleForTesting
+ ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchersForTest() {
return mLaunchers;
}
@@ -130,7 +144,7 @@
public ShortcutPackage getPackageShortcuts(ShortcutService s, @NonNull String packageName) {
ShortcutPackage ret = mPackages.get(packageName);
if (ret == null) {
- ret = new ShortcutPackage(mUserId, packageName);
+ ret = new ShortcutPackage(s, this, mUserId, packageName);
mPackages.put(packageName, ret);
} else {
ret.attemptToRestoreIfNeededAndSave(s);
@@ -143,7 +157,7 @@
final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName);
ShortcutLauncher ret = mLaunchers.get(key);
if (ret == null) {
- ret = new ShortcutLauncher(mUserId, packageName, launcherUserId);
+ ret = new ShortcutLauncher(this, mUserId, packageName, launcherUserId);
mLaunchers.put(key, ret);
} else {
ret.attemptToRestoreIfNeededAndSave(s);
@@ -151,21 +165,25 @@
return ret;
}
- public void forAllPackageItems(Consumer<ShortcutPackageItem> callback) {
- {
- final int size = mLaunchers.size();
- for (int i = 0; i < size; i++) {
- callback.accept(mLaunchers.valueAt(i));
- }
+ public void forAllPackages(Consumer<? super ShortcutPackage> callback) {
+ final int size = mPackages.size();
+ for (int i = 0; i < size; i++) {
+ callback.accept(mPackages.valueAt(i));
}
- {
- final int size = mPackages.size();
- for (int i = 0; i < size; i++) {
- callback.accept(mPackages.valueAt(i));
- }
+ }
+
+ public void forAllLaunchers(Consumer<? super ShortcutLauncher> callback) {
+ final int size = mLaunchers.size();
+ for (int i = 0; i < size; i++) {
+ callback.accept(mLaunchers.valueAt(i));
}
}
+ public void forAllPackageItems(Consumer<? super ShortcutPackageItem> callback) {
+ forAllLaunchers(callback);
+ forAllPackages(callback);
+ }
+
public void forPackageItem(@NonNull String packageName, @UserIdInt int packageUserId,
Consumer<ShortcutPackageItem> callback) {
forAllPackageItems(spi -> {
@@ -177,6 +195,24 @@
}
/**
+ * Reset all throttling counters for all packages, if there has been a system locale change.
+ */
+ public void resetThrottlingIfNeeded(ShortcutService s) {
+ final long currentNo = s.getLocaleChangeSequenceNumber();
+ if (mKnownLocaleChangeSequenceNumber < currentNo) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "LocaleChange detected for user " + mUserId);
+ }
+
+ mKnownLocaleChangeSequenceNumber = currentNo;
+
+ forAllPackages(p -> p.resetRateLimiting(s));
+
+ s.scheduleSaveUser(mUserId);
+ }
+ }
+
+ /**
* Called when a package is updated.
*/
public void handlePackageUpdated(ShortcutService s, @NonNull String packageName,
@@ -198,6 +234,9 @@
throws IOException, XmlPullParserException {
out.startTag(null, TAG_ROOT);
+ ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER,
+ mKnownLocaleChangeSequenceNumber);
+
ShortcutService.writeTagValue(out, TAG_LAUNCHER,
mLauncherComponent);
@@ -235,6 +274,9 @@
boolean fromBackup) throws IOException, XmlPullParserException {
final ShortcutUser ret = new ShortcutUser(userId);
+ ret.mKnownLocaleChangeSequenceNumber = ShortcutService.parseLongAttribute(parser,
+ ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER);
+
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -254,7 +296,7 @@
}
case ShortcutPackage.TAG_ROOT: {
final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(
- s, parser, userId, fromBackup);
+ s, ret, parser, fromBackup);
// Don't use addShortcut(), we don't need to save the icon.
ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
@@ -262,7 +304,8 @@
}
case ShortcutLauncher.TAG_ROOT: {
- ret.addLauncher(ShortcutLauncher.loadFromXml(parser, userId, fromBackup));
+ ret.addLauncher(
+ ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup));
continue;
}
}
@@ -294,6 +337,8 @@
pw.print(prefix);
pw.print("User: ");
pw.print(mUserId);
+ pw.print(" Known locale seq#: ");
+ pw.print(mKnownLocaleChangeSequenceNumber);
pw.println();
prefix += prefix + " ";
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 13518b5..ced7cf0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -56,11 +56,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -94,6 +97,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.test.InstrumentationTestCase;
+import android.test.MoreAsserts;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -225,6 +229,7 @@
/** ShortcutService with injection override methods. */
private final class ShortcutServiceTestable extends ShortcutService {
final ServiceContext mContext;
+ IUidObserver mUidObserver;
public ShortcutServiceTestable(ServiceContext context, Looper looper) {
super(context, looper);
@@ -265,6 +270,13 @@
}
@Override
+ long injectElapsedRealtime() {
+ // TODO This should be kept separately from mInjectedCurrentTimeLillis, since
+ // this should increase even if we rewind mInjectedCurrentTimeLillis in some tests.
+ return mInjectedCurrentTimeLillis - START_TIME;
+ }
+
+ @Override
int injectBinderCallingUid() {
return mInjectedCallingUid;
}
@@ -295,6 +307,11 @@
}
@Override
+ void injectRegisterUidObserver(IUidObserver observer, int which) {
+ mUidObserver = observer;
+ }
+
+ @Override
PackageManagerInternal injectPackageManagerInternal() {
return mMockPackageManagerInternal;
}
@@ -324,6 +341,13 @@
}
@Override
+ void injectEnforceCallingPermission(String permission, String message) {
+ if (!mCallerPermissions.contains(permission)) {
+ throw new SecurityException("Missing permission: " + permission);
+ }
+ }
+
+ @Override
void wtf(String message, Exception e) {
// During tests, WTF is fatal.
fail(message + " exception: " + e);
@@ -493,6 +517,8 @@
private static final ShortcutQuery QUERY_ALL = new ShortcutQuery();
+ private final ArrayList<String> mCallerPermissions = new ArrayList<>();
+
static {
QUERY_ALL.setQueryFlags(
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
@@ -561,6 +587,11 @@
initService();
setCaller(CALLING_PACKAGE_1);
+
+ // In order to complicate the situation, we set mLocaleChangeSequenceNumber to 1 by
+ // calling this. Running test with mLocaleChangeSequenceNumber == 0 might make us miss
+ // some edge cases.
+ mInternal.onSystemLocaleChangedNoLock();
}
private static UserInfo withProfileGroupId(UserInfo in, int groupId) {
@@ -761,7 +792,7 @@
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintWriter pw = new PrintWriter(out);
- mService.dumpInner(pw);
+ mService.dumpInner(pw, null);
pw.close();
Log.e(TAG, "Dumping ShortcutService: " + message);
@@ -3546,17 +3577,17 @@
// Check the registered packages.
dumpsysOnLogcat();
assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- hashSet(user0.getAllPackages().keySet()));
+ hashSet(user0.getAllPackagesForTest().keySet()));
assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- hashSet(user10.getAllPackages().keySet()));
+ hashSet(user10.getAllPackagesForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- hashSet(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchersForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_10, LAUNCHER_1),
PackageWithUser.of(USER_10, LAUNCHER_2)),
- hashSet(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchersForTest().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_1", "s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3578,17 +3609,17 @@
// No changes.
assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- hashSet(user0.getAllPackages().keySet()));
+ hashSet(user0.getAllPackagesForTest().keySet()));
assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- hashSet(user10.getAllPackages().keySet()));
+ hashSet(user10.getAllPackagesForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- hashSet(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchersForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_10, LAUNCHER_1),
PackageWithUser.of(USER_10, LAUNCHER_2)),
- hashSet(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchersForTest().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_1", "s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3609,17 +3640,17 @@
mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0);
assertEquals(set(CALLING_PACKAGE_2),
- hashSet(user0.getAllPackages().keySet()));
+ hashSet(user0.getAllPackagesForTest().keySet()));
assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- hashSet(user10.getAllPackages().keySet()));
+ hashSet(user10.getAllPackagesForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- hashSet(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchersForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_10, LAUNCHER_1),
PackageWithUser.of(USER_10, LAUNCHER_2)),
- hashSet(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchersForTest().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3640,16 +3671,16 @@
mService.cleanUpPackageLocked(LAUNCHER_1, USER_10, USER_10);
assertEquals(set(CALLING_PACKAGE_2),
- hashSet(user0.getAllPackages().keySet()));
+ hashSet(user0.getAllPackagesForTest().keySet()));
assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- hashSet(user10.getAllPackages().keySet()));
+ hashSet(user10.getAllPackagesForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- hashSet(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchersForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_10, LAUNCHER_2)),
- hashSet(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchersForTest().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3668,16 +3699,16 @@
mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10, USER_10);
assertEquals(set(CALLING_PACKAGE_2),
- hashSet(user0.getAllPackages().keySet()));
+ hashSet(user0.getAllPackagesForTest().keySet()));
assertEquals(set(CALLING_PACKAGE_1),
- hashSet(user10.getAllPackages().keySet()));
+ hashSet(user10.getAllPackagesForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- hashSet(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchersForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_10, LAUNCHER_2)),
- hashSet(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchersForTest().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3696,16 +3727,16 @@
mService.cleanUpPackageLocked(LAUNCHER_2, USER_10, USER_10);
assertEquals(set(CALLING_PACKAGE_2),
- hashSet(user0.getAllPackages().keySet()));
+ hashSet(user0.getAllPackagesForTest().keySet()));
assertEquals(set(CALLING_PACKAGE_1),
- hashSet(user10.getAllPackages().keySet()));
+ hashSet(user10.getAllPackagesForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- hashSet(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchersForTest().keySet()));
assertEquals(
set(),
- hashSet(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchersForTest().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3724,15 +3755,15 @@
mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10, USER_10);
assertEquals(set(CALLING_PACKAGE_2),
- hashSet(user0.getAllPackages().keySet()));
+ hashSet(user0.getAllPackagesForTest().keySet()));
assertEquals(set(),
- hashSet(user10.getAllPackages().keySet()));
+ hashSet(user10.getAllPackagesForTest().keySet()));
assertEquals(
set(PackageWithUser.of(USER_0, LAUNCHER_1),
PackageWithUser.of(USER_0, LAUNCHER_2)),
- hashSet(user0.getAllLaunchers().keySet()));
+ hashSet(user0.getAllLaunchersForTest().keySet()));
assertEquals(set(),
- hashSet(user10.getAllLaunchers().keySet()));
+ hashSet(user10.getAllLaunchersForTest().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -4654,19 +4685,19 @@
private void checkBackupAndRestore_success() {
// Make sure non-system user is not restored.
final ShortcutUser userP0 = mService.getUserShortcutsLocked(USER_P0);
- assertEquals(0, userP0.getAllPackages().size());
- assertEquals(0, userP0.getAllLaunchers().size());
+ assertEquals(0, userP0.getAllPackagesForTest().size());
+ assertEquals(0, userP0.getAllLaunchersForTest().size());
// Make sure only "allowBackup" apps are restored, and are shadow.
final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
- assertExistsAndShadow(user0.getAllPackages().get(CALLING_PACKAGE_1));
- assertExistsAndShadow(user0.getAllPackages().get(CALLING_PACKAGE_2));
- assertExistsAndShadow(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_1)));
- assertExistsAndShadow(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_2)));
+ assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_1));
+ assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_2));
+ assertExistsAndShadow(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_1)));
+ assertExistsAndShadow(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_2)));
- assertNull(user0.getAllPackages().get(CALLING_PACKAGE_3));
- assertNull(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
- assertNull(user0.getAllLaunchers().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
+ assertNull(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
+ assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
+ assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
installPackage(USER_0, CALLING_PACKAGE_1);
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -5285,6 +5316,361 @@
});
}
+ public void testThrottling_localeChanges() {
+ prepareCrossProfileDataSet();
+
+ dumpsysOnLogcat("Before save & load");
+
+ mService.saveDirtyInfo();
+ initService();
+
+ final long origSequenceNumber = mService.getLocaleChangeSequenceNumber();
+
+ mInternal.onSystemLocaleChangedNoLock();
+
+ assertEquals(origSequenceNumber + 1, mService.getLocaleChangeSequenceNumber());
+
+ // Note at this point only user-0 is loaded, and the counters are reset for this user,
+ // but it will work for other users too, because we persist when
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+
+ mService.saveDirtyInfo();
+ initService();
+
+ // Make sure the counter is persisted.
+ assertEquals(origSequenceNumber + 1, mService.getLocaleChangeSequenceNumber());
+ }
+
+ public void testThrottling_foreground() throws Exception {
+ prepareCrossProfileDataSet();
+
+ dumpsysOnLogcat("Before save & load");
+
+ mService.saveDirtyInfo();
+ initService();
+
+ // We need to update the current time from time to time, since some of the internal checks
+ // rely on the time being correctly incremented.
+ mInjectedCurrentTimeLillis++;
+
+ // First, all packages have less than 3 (== initial value) remaining calls.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mInjectedCurrentTimeLillis++;
+
+ // State changed, but not foreground, so no resetting.
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_1, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mInjectedCurrentTimeLillis++;
+
+ // State changed, package1 foreground, reset.
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_1, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_1, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+
+ mInjectedCurrentTimeLillis++;
+
+ // Different app comes to foreground briefly, and goes back to background.
+ // Now, make sure package 2's counter is reset, even in this case.
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_2, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_2, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mInjectedCurrentTimeLillis++;
+
+ // Do the same thing one more time. This would catch the bug with mixuing up
+ // the current time and the elapsed time.
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ mManager.updateShortcuts(list(makeShortcut("s")));
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_2, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mService.mUidObserver.onUidStateChanged(
+ CALLING_UID_2, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mInjectedCurrentTimeLillis++;
+
+ // Package 1 on user-10 comes to foreground.
+ // Now, also try calling some APIs and make sure foreground apps don't get throttled.
+ mService.mUidObserver.onUidStateChanged(
+ UserHandle.getUid(USER_10, CALLING_UID_1),
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(0, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+ mManager.setDynamicShortcuts(list(makeShortcut("s")));
+
+ assertEquals(3, mManager.getRemainingCallCount()); // Still 3!
+ });
+ }
+
+
+ public void testThrottling_resetByInternalCall() throws Exception {
+ prepareCrossProfileDataSet();
+
+ dumpsysOnLogcat("Before save & load");
+
+ mService.saveDirtyInfo();
+ initService();
+
+ // First, all packages have less than 3 (== initial value) remaining calls.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ // Simulate a call from sys UI.
+ mCallerPermissions.add(permission.RESET_SHORTCUT_MANAGER_THROTTLING);
+ mService.onApplicationActive(CALLING_PACKAGE_1, USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mService.onApplicationActive(CALLING_PACKAGE_3, USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+
+ mService.onApplicationActive(CALLING_PACKAGE_1, USER_10);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ MoreAsserts.assertNotEqual(3, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEquals(3, mManager.getRemainingCallCount());
+ });
+ }
+
+ public void testOnApplicationActive_permission() {
+ assertExpectException(SecurityException.class, "Missing permission", () ->
+ mService.onApplicationActive(CALLING_PACKAGE_1, USER_0));
+
+ // Has permission, now it should pass.
+ mCallerPermissions.add(permission.RESET_SHORTCUT_MANAGER_THROTTLING);
+ mService.onApplicationActive(CALLING_PACKAGE_1, USER_0);
+ }
+
// ShortcutInfo tests
public void testShortcutInfoMissingMandatoryFields() {
@@ -5324,7 +5710,7 @@
si = new ShortcutInfo.Builder(getTestContext())
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
- .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
.setTitle("title")
.setText("text")
.setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
@@ -5341,7 +5727,7 @@
assertEquals(getTestContext().getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
- assertEquals("content://a.b.c/", si.getIcon().getUriString());
+ assertEquals(123, si.getIcon().getResId());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
@@ -5363,7 +5749,7 @@
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
- .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
.setTitle("title")
.setText("text")
.setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
@@ -5382,7 +5768,7 @@
assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
- assertEquals("content://a.b.c/", si.getIcon().getUriString());
+ assertEquals(123, si.getIcon().getResId());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
@@ -5498,7 +5884,7 @@
ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
- .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+ .setIcon(Icon.createWithResource(mClientContext, 123))
.setTitle("title")
.setText("text")
.setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
@@ -5520,9 +5906,9 @@
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
- .setIcon(Icon.createWithContentUri("content://x.y.z/")).build());
+ .setIcon(Icon.createWithResource(mClientContext, 456)).build());
assertEquals("text", si.getText());
- assertEquals("content://x.y.z/", si.getIcon().getUriString());
+ assertEquals(456, si.getIcon().getResId());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")