Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 837a5fd7 authored by Makoto Onuki's avatar Makoto Onuki Committed by android-build-merger
Browse files

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

am: 495d124a

* commit '495d124a':
  ShortcutManaegr: load config from settings, also...
parents 911c45b1 495d124a
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -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
+186 −34
Original line number Diff line number Diff line
@@ -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,22 +547,75 @@ public class ShortcutService extends IShortcutService.Stub {
     */
    private void initialize() {
        synchronized (mLock) {
            injectLoadConfigurationLocked();
            loadConfigurationLocked();
            loadBaseStateLocked();
        }
    }

    // Test overrides it to inject different values.
    /**
     * Load the configuration from Settings.
     */
    private void loadConfigurationLocked() {
        updateConfigurationLocked(injectShortcutManagerConstants());
    }

    /**
     * Load the configuration from Settings.
     */
    @VisibleForTesting
    void injectLoadConfigurationLocked() {
        mResetInterval = DEFAULT_RESET_INTERVAL_SEC * 1000L;
        mMaxDailyUpdates = DEFAULT_MAX_DAILY_UPDATES;
        mMaxDynamicShortcuts = DEFAULT_MAX_SHORTCUTS_PER_APP;

        final int iconDimensionDp = (injectIsLowRamDevice()
                ? DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP : DEFAULT_MAX_ICON_DIMENSION_DP);
        mMaxIconDimension =
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, iconDimensionDp,
    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());
    }

@@ -1829,14 +1931,27 @@ public class ShortcutService extends IShortcutService.Stub {
                return handleDefaultCommands(cmd);
            }
            final PrintWriter pw = getOutPrintWriter();
            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
    long getResetIntervalForTest() {
        return mResetInterval;
    }

    @VisibleForTesting
    int getMaxIconDimensionForTest() {
        return mMaxIconDimension;
    }

    @VisibleForTesting
    void setMaxIconDimensionForTest(int dimension) {
        mMaxIconDimension = dimension;
    CompressFormat getIconPersistFormatForTest() {
        return mIconPersistFormat;
    }

    @VisibleForTesting
    public void setResetIntervalForTest(long interval) {
        mResetInterval = interval;
    int getIconPersistQualityForTest() {
        return mIconPersistQuality;
    }
}
+63 −5
Original line number Diff line number Diff line
@@ -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()} */