Merge "ShortcutManaegr: load config from settings, also..." into nyc-dev
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()} */
|
||||
|
||||
Reference in New Issue
Block a user