ShortcutManaegr: load config from settings, also...
support "cmd override-config" and "cmd reset-config" to help CTS.
Bug 27548047
Change-Id: Id6c9e6f41a2238856dd3470d88d88d0e7e686f26
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d43ff4e..d4ff766 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7786,6 +7786,31 @@
public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants";
/**
+ * ShortcutManager specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "reset_interval_sec=86400,max_daily_updates=5"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * reset_interval_sec (long)
+ * max_daily_updates (int)
+ * max_icon_dimension_dp (int, DP)
+ * max_icon_dimension_dp_lowram (int, DP)
+ * max_shortcuts (int)
+ * icon_quality (int, 0-100)
+ * icon_format (String)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.pm.ShortcutService.ConfigConstants
+ */
+ public static final String SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants";
+
+ /**
* Get the key that retrieves a bluetooth headset's priority.
* @hide
*/
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 8982632..9d8def6 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -56,6 +56,7 @@
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.TypedValue;
@@ -92,8 +93,6 @@
/**
* TODO:
*
- * - Implement launchShortcut
- *
* - Detect when already registered instances are passed to APIs again, which might break
* internal bitmap handling.
*
@@ -103,8 +102,6 @@
*
* - Pinned per each launcher package (multiple launchers)
*
- * - Load config from settings
- *
* - Make save async (should we?)
*
* - Scan and remove orphan bitmaps (just in case).
@@ -117,11 +114,26 @@
private static final boolean DEBUG = true; // STOPSHIP if true
private static final boolean DEBUG_LOAD = true; // STOPSHIP if true
- private static final int DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
- private static final int DEFAULT_MAX_DAILY_UPDATES = 10;
- private static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
- private static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
- private static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
+ @VisibleForTesting
+ static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_DAILY_UPDATES = 10;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
+
+ @VisibleForTesting
+ static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
+
+ @VisibleForTesting
+ static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
@@ -158,6 +170,44 @@
private static final String ATTR_ICON_RES = "icon-res";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
+ @VisibleForTesting
+ interface ConfigConstants {
+ /**
+ * Key name for the throttling reset interval, in seconds. (long)
+ */
+ String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
+
+ /**
+ * Key name for the max number of modifying API calls per app for every interval. (int)
+ */
+ String KEY_MAX_DAILY_UPDATES = "max_daily_updates";
+
+ /**
+ * Key name for the max icon dimensions in DP, for non-low-memory devices.
+ */
+ String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
+
+ /**
+ * Key name for the max icon dimensions in DP, for low-memory devices.
+ */
+ String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
+
+ /**
+ * Key name for the max dynamic shortcuts per app. (int)
+ */
+ String KEY_MAX_SHORTCUTS = "max_shortcuts";
+
+ /**
+ * Key name for icom compression quality, 0-100.
+ */
+ String KEY_ICON_QUALITY = "icon_quality";
+
+ /**
+ * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
+ */
+ String KEY_ICON_FORMAT = "icon_format";
+ }
+
private final Context mContext;
private final Object mLock = new Object();
@@ -416,9 +466,8 @@
*/
private int mMaxIconDimension;
- private CompressFormat mIconPersistFormat = CompressFormat.PNG;
-
- private int mIconPersistQuality = 100;
+ private CompressFormat mIconPersistFormat;
+ private int mIconPersistQuality;
public ShortcutService(Context context) {
mContext = Preconditions.checkNotNull(context);
@@ -498,23 +547,76 @@
*/
private void initialize() {
synchronized (mLock) {
- injectLoadConfigurationLocked();
+ loadConfigurationLocked();
loadBaseStateLocked();
}
}
- // Test overrides it to inject different values.
- @VisibleForTesting
- void injectLoadConfigurationLocked() {
- mResetInterval = DEFAULT_RESET_INTERVAL_SEC * 1000L;
- mMaxDailyUpdates = DEFAULT_MAX_DAILY_UPDATES;
- mMaxDynamicShortcuts = DEFAULT_MAX_SHORTCUTS_PER_APP;
+ /**
+ * Load the configuration from Settings.
+ */
+ private void loadConfigurationLocked() {
+ updateConfigurationLocked(injectShortcutManagerConstants());
+ }
- final int iconDimensionDp = (injectIsLowRamDevice()
- ? DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP : DEFAULT_MAX_ICON_DIMENSION_DP);
- mMaxIconDimension =
- (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, iconDimensionDp,
- mContext.getResources().getDisplayMetrics());
+ /**
+ * Load the configuration from Settings.
+ */
+ @VisibleForTesting
+ boolean updateConfigurationLocked(String config) {
+ boolean result = true;
+
+ final KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(config);
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on
+ // with defaults.
+ Slog.e(TAG, "Bad shortcut manager settings", e);
+ result = false;
+ }
+
+ mResetInterval = parser.getLong(
+ ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
+ * 1000L;
+
+ mMaxDailyUpdates = (int) parser.getLong(
+ ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES);
+
+ mMaxDynamicShortcuts = (int) parser.getLong(
+ ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP);
+
+ final int iconDimensionDp = injectIsLowRamDevice()
+ ? (int) parser.getLong(
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
+ DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
+ : (int) parser.getLong(
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
+ DEFAULT_MAX_ICON_DIMENSION_DP);
+
+ mMaxIconDimension = injectDipToPixel(iconDimensionDp);
+
+ mIconPersistFormat = CompressFormat.valueOf(
+ parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
+
+ mIconPersistQuality = (int) parser.getLong(
+ ConfigConstants.KEY_ICON_QUALITY,
+ DEFAULT_ICON_PERSIST_QUALITY);
+
+ return result;
+ }
+
+ @VisibleForTesting
+ String injectShortcutManagerConstants() {
+ return android.provider.Settings.Global.getString(
+ mContext.getContentResolver(),
+ android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
+ }
+
+ @VisibleForTesting
+ int injectDipToPixel(int dip) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
+ mContext.getResources().getDisplayMetrics());
}
// === Persisting ===
@@ -1829,14 +1931,27 @@
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
- switch(cmd) {
+ int ret = 1;
+ switch (cmd) {
case "reset-package-throttling":
- return handleResetPackageThrottling();
+ ret = handleResetPackageThrottling();
+ break;
case "reset-throttling":
- return handleResetThrottling();
+ ret = handleResetThrottling();
+ break;
+ case "override-config":
+ ret = handleOverrideConfig();
+ break;
+ case "reset-config":
+ ret = handleResetConfig();
+ break;
default:
return handleDefaultCommands(cmd);
}
+ if (ret == 0) {
+ pw.println("Success");
+ }
+ return ret;
}
@Override
@@ -1850,6 +1965,12 @@
pw.println("cmd shortcut reset-throttling");
pw.println(" Reset throttling for all packages and users");
pw.println();
+ pw.println("cmd shortcut override-config CONFIG");
+ pw.println(" Override the configuration for testing (will last until reboot)");
+ pw.println();
+ pw.println("cmd shortcut reset-config");
+ pw.println(" Reset the configuration set with \"update-config\"");
+ pw.println();
}
private int handleResetThrottling() {
@@ -1881,6 +2002,26 @@
return 0;
}
+
+ private int handleOverrideConfig() {
+ final PrintWriter pw = getOutPrintWriter();
+ final String config = getNextArgRequired();
+
+ synchronized (mLock) {
+ if (!updateConfigurationLocked(config)) {
+ pw.println("override-config failed. See logcat for details.");
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ private int handleResetConfig() {
+ synchronized (mLock) {
+ loadConfigurationLocked();
+ }
+ return 0;
+ }
}
// === Unit test support ===
@@ -1903,6 +2044,7 @@
return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
}
+ @VisibleForTesting
boolean injectIsLowRamDevice() {
return ActivityManager.isLowRamDeviceStatic();
}
@@ -1917,22 +2059,32 @@
}
@VisibleForTesting
- void setMaxDynamicShortcutsForTest(int max) {
- mMaxDynamicShortcuts = max;
+ int getMaxDynamicShortcutsForTest() {
+ return mMaxDynamicShortcuts;
}
@VisibleForTesting
- void setMaxDailyUpdatesForTest(int max) {
- mMaxDailyUpdates = max;
+ int getMaxDailyUpdatesForTest() {
+ return mMaxDailyUpdates;
}
@VisibleForTesting
- void setMaxIconDimensionForTest(int dimension) {
- mMaxIconDimension = dimension;
+ long getResetIntervalForTest() {
+ return mResetInterval;
}
@VisibleForTesting
- public void setResetIntervalForTest(long interval) {
- mResetInterval = interval;
+ int getMaxIconDimensionForTest() {
+ return mMaxIconDimension;
+ }
+
+ @VisibleForTesting
+ CompressFormat getIconPersistFormatForTest() {
+ return mIconPersistFormat;
+ }
+
+ @VisibleForTesting
+ int getIconPersistQualityForTest() {
+ return mIconPersistQuality;
}
}
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 21daa1b..036cc66 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -27,6 +27,7 @@
import android.content.pm.ShortcutServiceInternal;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -42,6 +43,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.ShortcutService.ConfigConstants;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
import libcore.io.IoUtils;
@@ -114,11 +116,20 @@
}
@Override
- void injectLoadConfigurationLocked() {
- setResetIntervalForTest(INTERVAL);
- setMaxDynamicShortcutsForTest(MAX_SHORTCUTS);
- setMaxDailyUpdatesForTest(MAX_DAILY_UPDATES);
- setMaxIconDimensionForTest(MAX_ICON_DIMENSION);
+ String injectShortcutManagerConstants() {
+ return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=" + MAX_SHORTCUTS + ","
+ + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=" + MAX_DAILY_UPDATES + ","
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=" + MAX_ICON_DIMENSION + ","
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "="
+ + MAX_ICON_DIMENSION_LOWRAM + ","
+ + ConfigConstants.KEY_ICON_FORMAT + "=PNG,"
+ + ConfigConstants.KEY_ICON_QUALITY + "=100";
+ }
+
+ @Override
+ int injectDipToPixel(int dip) {
+ return dip;
}
@Override
@@ -151,6 +162,11 @@
void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
// Can't check
}
+
+ @Override
+ boolean injectIsLowRamDevice() {
+ return mInjectdIsLowRamDevice;
+ }
}
/** ShortcutManager with injection override methods. */
@@ -186,6 +202,8 @@
private long mInjectedCurrentTimeLillis;
+ private boolean mInjectdIsLowRamDevice;
+
private int mInjectedCallingUid;
private String mInjectedClientPackage;
@@ -216,6 +234,8 @@
private static final int MAX_ICON_DIMENSION = 128;
+ private static final int MAX_ICON_DIMENSION_LOWRAM = 32;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -676,6 +696,44 @@
// TODO Add various broken cases.
}
+ public void testLoadConfig() {
+ mService.updateConfigurationLocked(
+ ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
+ + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=5,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ + ConfigConstants.KEY_ICON_FORMAT + "=WEBP,"
+ + ConfigConstants.KEY_ICON_QUALITY + "=75");
+ assertEquals(123000, mService.getResetIntervalForTest());
+ assertEquals(4, mService.getMaxDynamicShortcutsForTest());
+ assertEquals(5, mService.getMaxDailyUpdatesForTest());
+ assertEquals(100, mService.getMaxIconDimensionForTest());
+ assertEquals(CompressFormat.WEBP, mService.getIconPersistFormatForTest());
+ assertEquals(75, mService.getIconPersistQualityForTest());
+
+ mInjectdIsLowRamDevice = true;
+ mService.updateConfigurationLocked(
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ + ConfigConstants.KEY_ICON_FORMAT + "=JPEG");
+ assertEquals(ShortcutService.DEFAULT_RESET_INTERVAL_SEC * 1000,
+ mService.getResetIntervalForTest());
+
+ assertEquals(ShortcutService.DEFAULT_MAX_SHORTCUTS_PER_APP,
+ mService.getMaxDynamicShortcutsForTest());
+
+ assertEquals(ShortcutService.DEFAULT_MAX_DAILY_UPDATES,
+ mService.getMaxDailyUpdatesForTest());
+
+ assertEquals(50, mService.getMaxIconDimensionForTest());
+
+ assertEquals(CompressFormat.JPEG, mService.getIconPersistFormatForTest());
+
+ assertEquals(ShortcutService.DEFAULT_ICON_PERSIST_QUALITY,
+ mService.getIconPersistQualityForTest());
+ }
+
// === Test for app side APIs ===
/** Test for {@link android.content.pm.ShortcutManager#getMaxDynamicShortcutCount()} */