Merge "ShortcutManaegr: load config from settings, also..." into nyc-dev

This commit is contained in:
Makoto Onuki
2016-03-10 00:18:34 +00:00
committed by Android (Google) Code Review
3 changed files with 275 additions and 40 deletions

View File

@@ -7785,6 +7785,31 @@ public final class Settings {
*/
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

View File

@@ -56,6 +56,7 @@ 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.TypedValue;
@@ -92,8 +93,6 @@ import java.util.function.Predicate;
/**
* TODO:
*
* - Implement launchShortcut
*
* - Detect when already registered instances are passed to APIs again, which might break
* internal bitmap handling.
*
@@ -103,8 +102,6 @@ import java.util.function.Predicate;
*
* - 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 @@ public class ShortcutService extends IShortcutService.Stub {
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 @@ public class ShortcutService extends IShortcutService.Stub {
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 @@ public class ShortcutService extends IShortcutService.Stub {
*/
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 @@ public class ShortcutService extends IShortcutService.Stub {
*/
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 @@ public class ShortcutService extends IShortcutService.Stub {
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 @@ public class ShortcutService extends IShortcutService.Stub {
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 @@ public class ShortcutService extends IShortcutService.Stub {
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 @@ public class ShortcutService extends IShortcutService.Stub {
return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
}
@VisibleForTesting
boolean injectIsLowRamDevice() {
return ActivityManager.isLowRamDeviceStatic();
}
@@ -1917,22 +2059,32 @@ public class ShortcutService extends IShortcutService.Stub {
}
@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;
}
}

View File

@@ -27,6 +27,7 @@ import android.content.pm.ShortcutManager;
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.frameworks.servicestests.R;
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 @@ public class ShortcutManagerTest extends AndroidTestCase {
}
@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 @@ public class ShortcutManagerTest extends AndroidTestCase {
void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
// Can't check
}
@Override
boolean injectIsLowRamDevice() {
return mInjectdIsLowRamDevice;
}
}
/** ShortcutManager with injection override methods. */
@@ -186,6 +202,8 @@ public class ShortcutManagerTest extends AndroidTestCase {
private long mInjectedCurrentTimeLillis;
private boolean mInjectdIsLowRamDevice;
private int mInjectedCallingUid;
private String mInjectedClientPackage;
@@ -216,6 +234,8 @@ public class ShortcutManagerTest extends AndroidTestCase {
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 @@ public class ShortcutManagerTest extends AndroidTestCase {
// 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()} */