ShortcutService.java revision 3f4b1ca97ad7c31bdbe2ba29264841fb58683e81
16f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki/*
26f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Copyright (C) 2016 The Android Open Source Project
36f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
46f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
56f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * you may not use this file except in compliance with the License.
66f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * You may obtain a copy of the License at
76f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
86f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
96f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Unless required by applicable law or agreed to in writing, software
116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * See the License for the specific language governing permissions and
146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * limitations under the License.
156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */
166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukipackage com.android.server.pm;
176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.NonNull;
196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.Nullable;
206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.UserIdInt;
215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.app.ActivityManager;
226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.ComponentName;
235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.content.ContentProvider;
246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.Context;
256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.Intent;
266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.IShortcutService;
276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.LauncherApps;
286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.LauncherApps.ShortcutQuery;
296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.PackageManager;
306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.PackageManager.NameNotFoundException;
316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ParceledListSlice;
326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutInfo;
336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutServiceInternal;
346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Bitmap;
365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Bitmap.CompressFormat;
375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.BitmapFactory;
385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Canvas;
395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.RectF;
406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.graphics.drawable.Icon;
415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.net.Uri;
426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Binder;
436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Environment;
446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Handler;
455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.os.ParcelFileDescriptor;
466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.PersistableBundle;
476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Process;
486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.RemoteException;
496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.ResultReceiver;
505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.os.SELinux;
516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.ShellCommand;
526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.UserHandle;
536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.text.TextUtils;
545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.text.format.Formatter;
556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.text.format.Time;
566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.ArrayMap;
576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.ArraySet;
586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.AtomicFile;
594362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onukiimport android.util.KeyValueListParser;
606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.Slog;
616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.SparseArray;
625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.util.TypedValue;
636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.Xml;
646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.annotations.GuardedBy;
666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.annotations.VisibleForTesting;
676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.os.BackgroundThread;
686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.util.FastXmlSerializer;
696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.util.Preconditions;
706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.server.LocalServices;
716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.server.SystemService;
726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport libcore.io.IoUtils;
746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlPullParser;
766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlPullParserException;
776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlSerializer;
786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.File;
806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileDescriptor;
816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileInputStream;
826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileNotFoundException;
836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileOutputStream;
846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.IOException;
855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport java.io.InputStream;
866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.PrintWriter;
876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.net.URISyntaxException;
886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.nio.charset.StandardCharsets;
896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.ArrayList;
906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.List;
913f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onukiimport java.util.Map;
926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.function.Predicate;
936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki/**
956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * TODO:
966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Detect when already registered instances are passed to APIs again, which might break
985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   internal bitmap handling.
996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
1006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
1015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   -> Need to scan all packages when a user starts too.
1025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   -> Clear data -> remove all dynamic?  but not the pinned?
1036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
1046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Pinned per each launcher package (multiple launchers)
1056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
1065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Make save async (should we?)
1075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *
1085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Scan and remove orphan bitmaps (just in case).
1095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *
1105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Backup & restore
1116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */
1126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukipublic class ShortcutService extends IShortcutService.Stub {
1135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static final String TAG = "ShortcutService";
1146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final boolean DEBUG = false; // STOPSHIP if true
11641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final boolean DEBUG_LOAD = false; // STOPSHIP if true
1176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1184362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1194362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
1204362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1214362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1224362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_DAILY_UPDATES = 10;
1234362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1244362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
1264362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
1294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1304362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1314362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
1324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
1354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1364362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
1386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
1406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String FILENAME_BASE_STATE = "shortcut_service.xml";
1436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String DIRECTORY_PER_USER = "shortcut_service";
1466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
1496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static final String DIRECTORY_BITMAPS = "bitmaps";
1516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_ROOT = "root";
1533f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    static final String TAG_USER = "user";
15441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_PACKAGE = "package";
15541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_LAST_RESET_TIME = "last_reset_time";
15641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_INTENT_EXTRAS = "intent-extras";
15741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_EXTRAS = "extras";
15841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_SHORTCUT = "shortcut";
15941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
16041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_VALUE = "value";
16141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_NAME = "name";
16241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
16341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_CALL_COUNT = "call-count";
16441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_LAST_RESET = "last-reset";
16541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ID = "id";
16641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ACTIVITY = "activity";
16741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_TITLE = "title";
16841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_INTENT = "intent";
16941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_WEIGHT = "weight";
17041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_TIMESTAMP = "timestamp";
17141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_FLAGS = "flags";
17241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ICON_RES = "icon-res";
17341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_BITMAP_PATH = "bitmap-path";
1746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1754362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1764362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    interface ConfigConstants {
1774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the throttling reset interval, in seconds. (long)
1794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
1814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max number of modifying API calls per app for every interval. (int)
1844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_DAILY_UPDATES = "max_daily_updates";
1864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1884362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max icon dimensions in DP, for non-low-memory devices.
1894362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1904362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
1914362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max icon dimensions in DP, for low-memory devices.
1944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
1964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1974362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max dynamic shortcuts per app. (int)
1994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
2004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_SHORTCUTS = "max_shortcuts";
2014362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
2024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
20341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki         * Key name for icon compression quality, 0-100.
2044362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
2054362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_ICON_QUALITY = "icon_quality";
2064362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
2074362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
2084362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
2094362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
2104362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_ICON_FORMAT = "icon_format";
2114362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
2124362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
21341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final Context mContext;
2146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Object mLock = new Object();
2166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Handler mHandler;
2186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
2216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long mRawLastResetTime;
2246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2263f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki     * User ID -> UserShortcuts
2276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2293f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private final SparseArray<UserShortcuts> mUsers = new SparseArray<>();
2306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Max number of dynamic shortcuts that each application can have at a time.
2336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private int mMaxDynamicShortcuts;
2356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Max number of updating API calls that each application can make a day.
2386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
23941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    int mMaxDailyUpdates;
2406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Actual throttling-reset interval.  By default it's a day.
2436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long mResetInterval;
2456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    /**
2475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * Icon max width/height in pixels.
2485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     */
2495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private int mMaxIconDimension;
2505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
2514362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private CompressFormat mIconPersistFormat;
2524362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private int mIconPersistQuality;
2535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
2546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ShortcutService(Context context) {
2556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mContext = Preconditions.checkNotNull(context);
2566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
2576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mHandler = new Handler(BackgroundThread.get().getLooper());
2586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
2596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * System service lifecycle.
2626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public static final class Lifecycle extends SystemService {
2646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ShortcutService mService;
2656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Lifecycle(Context context) {
2676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            super(context);
2686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mService = new ShortcutService(context);
2696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onStart() {
2736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            publishBinderService(Context.SHORTCUT_SERVICE, mService);
2746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onBootPhase(int phase) {
2786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mService.onBootPhase(phase);
2796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onCleanupUser(int userHandle) {
2836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mService.mLock) {
2846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mService.onCleanupUserInner(userHandle);
2856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
289f3a572b5c0cab23a435bd90414d25de84e00398eMakoto Onuki        public void onUnlockUser(int userId) {
2906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mService.mLock) {
2916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mService.onStartUserLocked(userId);
2926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
2956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
2976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onBootPhase(int phase) {
2986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
2996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.d(TAG, "onBootPhase: " + phase);
3006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        switch (phase) {
3026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            case SystemService.PHASE_LOCK_SETTINGS_READY:
3036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                initialize();
3046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                break;
3056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onStartUserLocked(int userId) {
3106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Preload
3116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getUserShortcutsLocked(userId);
3126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onCleanupUserInner(int userId) {
3166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Unload
3173f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        mUsers.delete(userId);
3186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the base state file name */
3216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private AtomicFile getBaseStateFile() {
3226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
3236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
3246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new AtomicFile(path);
3256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
3286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Init the instance. (load the state file, etc)
3296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
3306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void initialize() {
3316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
3324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            loadConfigurationLocked();
3336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            loadBaseStateLocked();
3346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    /**
3384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     * Load the configuration from Settings.
3394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     */
3404362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private void loadConfigurationLocked() {
3414362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        updateConfigurationLocked(injectShortcutManagerConstants());
3424362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
3434362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    /**
3454362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     * Load the configuration from Settings.
3464362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     */
3474362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
3484362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    boolean updateConfigurationLocked(String config) {
3494362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        boolean result = true;
3504362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3514362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        final KeyValueListParser parser = new KeyValueListParser(',');
3524362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        try {
3534362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            parser.setString(config);
3544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        } catch (IllegalArgumentException e) {
3554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            // Failed to parse the settings string, log this and move on
3564362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            // with defaults.
3574362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            Slog.e(TAG, "Bad shortcut manager settings", e);
3584362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            result = false;
3594362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
3604362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mResetInterval = parser.getLong(
3624362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
3634362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                * 1000L;
3644362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3654362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxDailyUpdates = (int) parser.getLong(
3664362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES);
3674362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3684362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxDynamicShortcuts = (int) parser.getLong(
3694362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP);
3704362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3714362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        final int iconDimensionDp = injectIsLowRamDevice()
3724362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ? (int) parser.getLong(
3734362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
3744362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
3754362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                : (int) parser.getLong(
3764362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
3774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    DEFAULT_MAX_ICON_DIMENSION_DP);
3784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxIconDimension = injectDipToPixel(iconDimensionDp);
3804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mIconPersistFormat = CompressFormat.valueOf(
3824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
3834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mIconPersistQuality = (int) parser.getLong(
3854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_ICON_QUALITY,
3864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                DEFAULT_ICON_PERSIST_QUALITY);
3874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3884362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return result;
3894362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
3904362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
3924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    String injectShortcutManagerConstants() {
3934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return android.provider.Settings.Global.getString(
3944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                mContext.getContentResolver(),
3954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
3964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
3975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
3984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
3994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int injectDipToPixel(int dip) {
4004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
4014362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                mContext.getResources().getDisplayMetrics());
4026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // === Persisting ===
4056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
40741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static String parseStringAttribute(XmlPullParser parser, String attribute) {
4086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return parser.getAttributeValue(null, attribute);
4096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
41141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static int parseIntAttribute(XmlPullParser parser, String attribute) {
41241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return (int) parseLongAttribute(parser, attribute);
41341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
41441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
41541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static long parseLongAttribute(XmlPullParser parser, String attribute) {
4166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
4196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
4216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return Long.parseLong(value);
4226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NumberFormatException e) {
4236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Error parsing long " + value);
4246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
4256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
42941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
4306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return ComponentName.unflattenFromString(value);
4356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
43841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
4396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
4446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return Intent.parseUri(value, /* flags =*/ 0);
4456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (URISyntaxException e) {
4466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Error parsing intent", e);
4476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
45141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
4526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) return;
4536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, tag);
4556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.attribute(null, ATTR_VALUE, value);
4566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, tag);
4576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
45941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
4606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeTagValue(out, tag, Long.toString(value));
4616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
46341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
4646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws IOException, XmlPullParserException {
4656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (bundle == null) return;
4666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, tag);
4686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        bundle.saveToXml(out);
4696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, tag);
4706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
47241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, String value) throws IOException {
4736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) return;
4746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.attribute(null, name, value);
4766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
47841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
4796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, String.valueOf(value));
4806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
48241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
4836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (comp == null) return;
4846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, comp.flattenToString());
4856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
48741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
4886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (intent == null) return;
4896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, intent.toUri(/* flags =*/ 0));
4916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
4946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void saveBaseStateLocked() {
4956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = getBaseStateFile();
4966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
4976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Saving to " + file.getBaseFile());
4986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        FileOutputStream outs = null;
5016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
5026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            outs = file.startWrite();
5036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Write to XML
5056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlSerializer out = new FastXmlSerializer();
5066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.setOutput(outs, StandardCharsets.UTF_8.name());
5076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startDocument(null, true);
5086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startTag(null, TAG_ROOT);
5096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Body.
5116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
5126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Epilogue.
5146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endTag(null, TAG_ROOT);
5156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endDocument();
5166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Close.
5186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.finishWrite(outs);
5196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException e) {
5206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
5216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.failWrite(outs);
5226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void loadBaseStateLocked() {
5266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mRawLastResetTime = 0;
5276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = getBaseStateFile();
5296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Loading from " + file.getBaseFile());
5316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try (FileInputStream in = file.openRead()) {
5336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
5346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            parser.setInput(in, StandardCharsets.UTF_8.name());
5356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int type;
5376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
5386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (type != XmlPullParser.START_TAG) {
5396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
5406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int depth = parser.getDepth();
5426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // Check the root tag
5436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String tag = parser.getName();
5446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (depth == 1) {
5456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    if (!TAG_ROOT.equals(tag)) {
5466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        Slog.e(TAG, "Invalid root tag: " + tag);
5476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return;
5486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
5496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
5506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // Assume depth == 2
5526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (tag) {
5536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case TAG_LAST_RESET_TIME:
5546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
5556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
5566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    default:
5576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        Slog.e(TAG, "Invalid tag: " + tag);
5586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
5596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
5616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (FileNotFoundException e) {
5626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Use the default
5636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
5646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
5656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = 0;
5676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Adjust the last reset time.
5696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getLastResetTimeLocked();
5706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void saveUserLocked(@UserIdInt int userId) {
5736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
5746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Saving to " + path);
5766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
5786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = new AtomicFile(path);
5796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        FileOutputStream outs = null;
5806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
5816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            outs = file.startWrite();
5826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Write to XML
5846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlSerializer out = new FastXmlSerializer();
5856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.setOutput(outs, StandardCharsets.UTF_8.name());
5866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startDocument(null, true);
5876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5883f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            getUserShortcutsLocked(userId).saveToXml(out);
5896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endDocument();
5916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Close.
5936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.finishWrite(outs);
5946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
5956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
5966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.failWrite(outs);
5976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
60041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static IOException throwForInvalidTag(int depth, String tag) throws IOException {
6016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
6026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
6053f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private UserShortcuts loadUserLocked(@UserIdInt int userId) {
6066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
6076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
6086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Loading from " + path);
6096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = new AtomicFile(path);
6116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final FileInputStream in;
6136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
6146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            in = file.openRead();
6156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (FileNotFoundException e) {
6166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (DEBUG) {
6176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                Slog.i(TAG, "Not found " + path);
6186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
6196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
6206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6213f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        UserShortcuts ret = null;
6226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
6236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
6246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            parser.setInput(in, StandardCharsets.UTF_8.name());
6256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int type;
6276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
6286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (type != XmlPullParser.START_TAG) {
6296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
6306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int depth = parser.getDepth();
6326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String tag = parser.getName();
6346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (DEBUG_LOAD) {
6356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    Slog.d(TAG, String.format("depth=%d type=%d name=%s",
6366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            depth, type, tag));
6376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6383f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                if ((depth == 1) && TAG_USER.equals(tag)) {
6393f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    ret = UserShortcuts.loadFromXml(parser, userId);
6403f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    continue;
6416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                throwForInvalidTag(depth, tag);
6436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
6446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
6456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
6466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
6476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
6486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } finally {
6496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            IoUtils.closeQuietly(in);
6506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // TODO Actually make it async.
6546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void scheduleSaveBaseState() {
6556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
6566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            saveBaseStateLocked();
6576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // TODO Actually make it async.
6616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void scheduleSaveUser(@UserIdInt int userId) {
6626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
6636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            saveUserLocked(userId);
6646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the last reset time. */
6686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long getLastResetTimeLocked() {
6696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        updateTimes();
6706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mRawLastResetTime;
6716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the next reset time. */
6746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long getNextResetTimeLocked() {
6756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        updateTimes();
6766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mRawLastResetTime + mResetInterval;
6776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
6806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Update the last reset time.
6816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
6826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void updateTimes() {
6836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long now = injectCurrentTimeMillis();
6856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long prevLastResetTime = mRawLastResetTime;
6876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mRawLastResetTime == 0) { // first launch.
6896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
6906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
6916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else if (now < mRawLastResetTime) {
6926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Clock rewound.
6936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
6946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
6956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else {
6966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Do it properly.
6976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((mRawLastResetTime + mResetInterval) <= now) {
6986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mRawLastResetTime += mResetInterval;
6996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (prevLastResetTime != mRawLastResetTime) {
7026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            scheduleSaveBaseState();
7036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user state. */
7076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
7086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
7093f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) {
7103f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        UserShortcuts userPackages = mUsers.get(userId);
7116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (userPackages == null) {
7126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackages = loadUserLocked(userId);
7136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (userPackages == null) {
7143f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                userPackages = new UserShortcuts(userId);
7156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7163f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            mUsers.put(userId, userPackages);
7176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return userPackages;
7196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user per-package state. */
7226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
7236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
7246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private PackageShortcuts getPackageShortcutsLocked(
7256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @NonNull String packageName, @UserIdInt int userId) {
7263f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final UserShortcuts userPackages = getUserShortcutsLocked(userId);
7273f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        PackageShortcuts shortcuts = userPackages.getPackages().get(packageName);
7286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcuts == null) {
7295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcuts = new PackageShortcuts(userId, packageName);
7303f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            userPackages.getPackages().put(packageName, shortcuts);
7316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return shortcuts;
7336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Caller validation ===
7366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) {
7385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.getBitmapPath() != null) {
7395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (DEBUG) {
7405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                Slog.d(TAG, "Removing " + shortcut.getBitmapPath());
7415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
7425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            new File(shortcut.getBitmapPath()).delete();
7435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setBitmapPath(null);
7455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setIconResourceId(0);
7465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES);
7475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
7515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static class FileOutputStreamWithPath extends FileOutputStream {
7525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        private final File mFile;
7535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public FileOutputStreamWithPath(File file) throws FileNotFoundException {
7555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            super(file);
7565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            mFile = file;
7575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public File getFile() {
7605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mFile;
7615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    /**
7655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * Build the cached bitmap filename for a shortcut icon.
7665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     *
7675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * The filename will be based on the ID, except certain characters will be escaped.
7685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     */
7695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
7705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
7715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            throws IOException {
7725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final File packagePath = new File(getUserBitmapFilePath(userId),
7735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                shortcut.getPackageName());
7745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!packagePath.isDirectory()) {
7755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            packagePath.mkdirs();
7765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!packagePath.isDirectory()) {
7775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                throw new IOException("Unable to create directory " + packagePath);
7785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
7795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            SELinux.restorecon(packagePath);
7805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final String baseName = String.valueOf(injectCurrentTimeMillis());
7835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (int suffix = 0;; suffix++) {
7845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
7855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final File file = new File(packagePath, filename);
7865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!file.exists()) {
7875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (DEBUG) {
7885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
7895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
7905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return new FileOutputStreamWithPath(file);
7915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
7925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) {
7965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
7975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
7985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final long token = Binder.clearCallingIdentity();
8015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        try {
8025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            // Clear icon info on the shortcut.
8035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setIconResourceId(0);
8045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setBitmapPath(null);
8055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final Icon icon = shortcut.getIcon();
8075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (icon == null) {
8085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return; // has no icon
8095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Bitmap bitmap = null;
8125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            try {
8135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                switch (icon.getType()) {
8145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_RESOURCE: {
8155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        injectValidateIconResPackage(shortcut, icon);
8165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.setIconResourceId(icon.getResId());
8185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
8195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        return;
8205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_BITMAP: {
8225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        bitmap = icon.getBitmap();
8235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        break;
8245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_URI: {
8265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        final Uri uri = ContentProvider.maybeAddUserId(icon.getUri(), userId);
8275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
8295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            bitmap = BitmapFactory.decodeStream(is);
8315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        } catch (IOException e) {
8335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            Slog.e(TAG, "Unable to load icon from " + uri);
8345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            return;
8355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        }
8365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        break;
8375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    default:
8395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        // This shouldn't happen because we've already validated the icon, but
8405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        // just in case.
8415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        throw ShortcutInfo.getInvalidIconException();
8425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (bitmap == null) {
8445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.e(TAG, "Null bitmap detected");
8455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return;
8465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                // Shrink and write to the file.
8485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                File path = null;
8495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                try {
8505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut);
8515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    try {
8525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        path = out.getFile();
8535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shrinkBitmap(bitmap, mMaxIconDimension)
8555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                                .compress(mIconPersistFormat, mIconPersistQuality, out);
8565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.setBitmapPath(out.getFile().getAbsolutePath());
8585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
8595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    } finally {
8605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        IoUtils.closeQuietly(out);
8615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                } catch (IOException|RuntimeException e) {
8635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    // STOPSHIP Change wtf to e
8645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
8655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (path != null && path.exists()) {
8665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        path.delete();
8675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } finally {
8705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (bitmap != null) {
8715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    bitmap.recycle();
8725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                // Once saved, we won't use the original icon information, so null it out.
8745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                shortcut.clearIcon();
8755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        } finally {
8775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Binder.restoreCallingIdentity(token);
8785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
8805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // Unfortunately we can't do this check in unit tests because we fake creator package names,
8825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // so override in unit tests.
8835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // TODO CTS this case.
8845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
8855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!shortcut.getPackageName().equals(icon.getResPackage())) {
8865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            throw new IllegalArgumentException(
8875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    "Icon resource must reside in shortcut owner package");
8885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
8905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
8925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
8935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        // Original width/height.
8945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int ow = in.getWidth();
8955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int oh = in.getHeight();
8965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if ((ow <= maxSize) && (oh <= maxSize)) {
8975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (DEBUG) {
8985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
8995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
9005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return in;
9015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int longerDimension = Math.max(ow, oh);
9035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        // New width and height.
9055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int nw = ow * maxSize / longerDimension;
9065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int nh = oh * maxSize / longerDimension;
9075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (DEBUG) {
9085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
9095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    ow, oh, nw, nh));
9105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
9135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final Canvas c = new Canvas(scaledBitmap);
9145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final RectF dst = new RectF(0, 0, nw, nh);
9165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
9185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        in.recycle();
9205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return scaledBitmap;
9225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
9235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // === Caller validation ===
9255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerSystem() {
9276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
9296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerShell() {
9326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
9346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceSystemOrShell() {
9376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerSystem() || isCallerShell(),
9386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                "Caller must be system or shell");
9396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceShell() {
9426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerShell(), "Caller must be shell");
9436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
9466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(packageName, "packageName");
9476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (isCallerSystem()) {
9496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // no check
9506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Otherwise, make sure the arguments are valid.
9556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (UserHandle.getUserId(callingUid) != userId) {
9566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new SecurityException("Invalid user-ID");
9576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
9596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // Caller is valid.
9606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new SecurityException("Caller UID= doesn't own " + packageName);
9626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Test overrides it.
9655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
9666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
9676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Is MATCH_UNINSTALLED_PACKAGES correct to get SD card app info?
9696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mContext.getPackageManager().getPackageUidAsUser(packageName,
9716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
9725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            | PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
9736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NameNotFoundException e) {
9746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return -1;
9756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
9796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
9806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
9816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void enforceMaxDynamicShortcuts(int numShortcuts) {
9826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (numShortcuts > mMaxDynamicShortcuts) {
9836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
9846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
9886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Sends a notification to LauncherApps
9896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Write to file
9906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
9916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) {
9926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        notifyListeners(packageName, userId);
9936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveUser(userId);
9946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
9976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutChangeListener> copy;
9986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> shortcuts = new ArrayList<>();
9996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
10006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy = new ArrayList<>(mListeners);
10016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId)
10036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    .findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
10046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        for (int i = copy.size() - 1; i >= 0; i--) {
10066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy.get(i).onShortcutChanged(packageName, shortcuts, userId);
10076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
10116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Clean up / validate an incoming shortcut.
10126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure all mandatory fields are set.
10136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure the intent's extras are persistable, and them to set
10146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     *  {@link ShortcutInfo#mIntentPersistableExtras}.  Also clear its extras.
10156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Clear flags.
10165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     *
10175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * TODO Detailed unit tests
10186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
10195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
10206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkNotNull(shortcut, "Null shortcut detected");
10216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcut.getActivityComponent() != null) {
10226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkState(
10236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    shortcut.getPackageName().equals(
10246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            shortcut.getActivityComponent().getPackageName()),
10256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    "Activity package name mismatch");
10266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!forUpdate) {
10295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.enforceMandatoryFields();
10305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.getIcon() != null) {
10325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            ShortcutInfo.validateIcon(shortcut.getIcon());
10335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validateForXml(shortcut.getId());
10365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validateForXml(shortcut.getTitle());
10375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validatePersistableBundleForXml(shortcut.getIntentPersistableExtras());
10385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validatePersistableBundleForXml(shortcut.getExtras());
10396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        shortcut.setFlags(0);
10415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
10435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those
10445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // characters.
10455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
10465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static void validatePersistableBundleForXml(PersistableBundle b) {
10475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (b == null || b.size() == 0) {
10485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
10496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (String key : b.keySet()) {
10515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            validateForXml(key);
10525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final Object value = b.get(key);
10535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (value == null) {
10545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                continue;
10555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof String) {
10565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                validateForXml((String) value);
10575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof String[]) {
10585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                for (String v : (String[]) value) {
10595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    validateForXml(v);
10605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
10615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof PersistableBundle) {
10625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                validatePersistableBundleForXml((PersistableBundle) value);
10635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
10645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static void validateForXml(String s) {
10685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (TextUtils.isEmpty(s)) {
10695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
10705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (int i = s.length() - 1; i >= 0; i--) {
10725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!isAllowedInXml(s.charAt(i))) {
10735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                throw new IllegalArgumentException("Unsupported character detected in: " + s);
10745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
10755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static boolean isAllowedInXml(char c) {
10795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
10806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === APIs ===
10836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
10856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
10866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
10876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
10886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
10906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int size = newShortcuts.size();
10916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
10936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
10946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
10966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
10976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
10986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
10996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            enforceMaxDynamicShortcuts(size);
11006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcuts.
11026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
11035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
11046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // First, remove all un-pinned; dynamic shortcuts
11075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            ps.deleteAllDynamicShortcuts(this);
11086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Then, add/update all.  We need to make sure to take over "pinned" flag.
11106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
11116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo newShortcut = newShortcuts.get(i);
11126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
11136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                ps.updateShortcutWithCapping(this, newShortcut);
11146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
11226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
11265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int size = newShortcuts.size();
11276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            // Throttling.
11325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!ps.tryApiCall(this)) {
11335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return false;
11346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            for (int i = 0; i < size; i++) {
11375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo source = newShortcuts.get(i);
11385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
11395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo target = ps.findShortcutById(source.getId());
11415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (target != null) {
11425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    final boolean replacingIcon = (source.getIcon() != null);
11435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (replacingIcon) {
11445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        removeIcon(userId, target);
11455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
11465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    target.copyNonNullFieldsFrom(source);
11485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (replacingIcon) {
11505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        saveIconAndFixUpShortcut(userId, target);
11515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
11525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
11535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
11546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
11626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
11696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
11706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
11716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcut.
11745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
11756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Add it.
11776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
11786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            ps.updateShortcutWithCapping(this, newShortcut);
11796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteDynamicShortcut(String packageName, String shortcutId,
11876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
11906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this, shortcutId);
11936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
11996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(this);
12036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
12056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
12096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
12136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
12146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isDynamic);
12156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
12206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
12246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
12256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isPinned);
12266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
12306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
12316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutInfo> ret = new ArrayList<>();
12336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
12356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new ParceledListSlice<>(ret);
12376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
12416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws RemoteException {
12426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mMaxDynamicShortcuts;
12456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
12496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return mMaxDailyUpdates
12536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
12546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
12596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getNextResetTimeLocked();
12636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @Override
12675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
12685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        synchronized (mLock) {
12695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mMaxIconDimension;
12705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
12715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
12725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
12736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
12746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Reset all throttling, for developer options and command line.  Only system/shell can call it.
12756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
12766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void resetThrottling() {
12786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceSystemOrShell();
12796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        resetThrottlingInner();
12816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
12846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void resetThrottlingInner() {
12856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = injectCurrentTimeMillis();
12876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveBaseState();
12895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        Slog.i(TAG, "ShortcutManager: throttling counter reset");
12906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
12936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Entry point from {@link LauncherApps}.
12946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
12956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class LocalService extends ShortcutServiceInternal {
12966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
12976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcuts(
12986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage, long changedSince,
12996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable String packageName, @Nullable ComponentName componentName,
13006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int queryFlags, int userId) {
13016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>();
13026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final int cloneFlag =
13036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
13046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
13056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
13066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (packageName != null) {
13096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags,
13106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            userId, ret, cloneFlag);
13116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                } else {
13126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    final ArrayMap<String, PackageShortcuts> packages =
13133f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                            getUserShortcutsLocked(userId).getPackages();
13145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    for (int i = packages.size() - 1; i >= 0; i--) {
13156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        getShortcutsInnerLocked(
13166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                packages.keyAt(i),
13176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                changedSince, componentName, queryFlags, userId, ret, cloneFlag);
13186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
13196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
13206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
13226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince,
13256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable ComponentName componentName, int queryFlags,
13266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
13276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId).findAll(ret,
13286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    (ShortcutInfo si) -> {
13296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (si.getLastChangedTimestamp() < changedSince) {
13306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
13316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
13326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (componentName != null
13336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && !componentName.equals(si.getActivityComponent())) {
13346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
13356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
13366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchDynamic =
13376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
13386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && si.isDynamic();
13396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchPinned =
13406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
13416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                        && si.isPinned();
13426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return matchDynamic || matchPinned;
13436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }, cloneFlag);
13446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcutInfo(
13486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage,
13496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String packageName, @Nullable List<String> ids, int userId) {
13506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
13516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
13526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
13546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArraySet<String> idSet = new ArraySet<>(ids);
13556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).findAll(ret,
13576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        (ShortcutInfo si) -> idSet.contains(si.getId()),
13586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
13596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
13616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
13656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull List<String> shortcutIds, int userId) {
13666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
13676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
13686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkNotNull(shortcutIds, "shortcutIds");
13696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                getPackageShortcutsLocked(packageName, userId).replacePinned(
13725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        ShortcutService.this, callingPackage, shortcutIds);
13736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackageChanged(packageName, userId);
13756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Intent createShortcutIntent(@NonNull String callingPackage,
137943204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki                @NonNull String packageName, @NonNull String shortcutId, int userId) {
13806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
138143204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
138243204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki            Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
13836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo fullShortcut =
138643204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki                        getPackageShortcutsLocked(packageName, userId)
13875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        .findShortcutById(shortcutId);
13885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return fullShortcut == null ? null : fullShortcut.getIntent();
13896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void addListener(@NonNull ShortcutChangeListener listener) {
13946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mListeners.add(Preconditions.checkNotNull(listener));
13966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
13995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        @Override
14005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public int getShortcutIconResId(@NonNull String callingPackage,
14015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                @NonNull ShortcutInfo shortcut, int userId) {
14025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Preconditions.checkNotNull(shortcut, "shortcut");
14035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            synchronized (mLock) {
14055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
14065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
14075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return (shortcutInfo != null && shortcutInfo.hasIconResource())
14085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        ? shortcutInfo.getIconResourceId() : 0;
14095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
14105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
14115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        @Override
14135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
141434d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                @NonNull ShortcutInfo shortcutIn, int userId) {
141534d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki            Preconditions.checkNotNull(shortcutIn, "shortcut");
14165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            synchronized (mLock) {
14185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
141934d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
14205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
14215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return null;
14225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
14235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                try {
142434d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                    if (shortcutInfo.getBitmapPath() == null) {
142534d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
142634d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        return null;
142734d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                    }
14285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return ParcelFileDescriptor.open(
14295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            new File(shortcutInfo.getBitmapPath()),
14305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            ParcelFileDescriptor.MODE_READ_ONLY);
14315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                } catch (FileNotFoundException e) {
14325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath());
14335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return null;
14345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
14355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
14365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
14376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Dump ===
14406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
14426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
14436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
14446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                != PackageManager.PERMISSION_GRANTED) {
14456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Permission Denial: can't dump UserManager from from pid="
14466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + Binder.getCallingPid()
14476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + ", uid=" + Binder.getCallingUid()
14486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + " without permission "
14496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + android.Manifest.permission.DUMP);
14506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
14516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        dumpInner(pw);
14536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
14566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void dumpInner(PrintWriter pw) {
14576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
14586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long now = injectCurrentTimeMillis();
14596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("Now: [");
14606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(now);
14616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
14626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(now));
14635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Raw last reset: [");
14656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(mRawLastResetTime);
14666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
14676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(mRawLastResetTime));
14686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long last = getLastResetTimeLocked();
14706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Last reset: [");
14716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(last);
14726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
14736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(last));
14746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final long next = getNextResetTimeLocked();
14766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Next reset: [");
14776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(next);
14786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
14796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(next));
14806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
14816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Max icon dim: ");
14835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mMaxIconDimension);
14845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Icon format: ");
14855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mIconPersistFormat);
14865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Icon quality: ");
14875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mIconPersistQuality);
14885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.println();
14895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14913f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            for (int i = 0; i < mUsers.size(); i++) {
14923f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                pw.println();
14933f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                mUsers.valueAt(i).dump(this, pw, "  ");
14946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
149841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static String formatTime(long time) {
14996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Time tobj = new Time();
15006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        tobj.set(time);
15016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return tobj.format("%Y-%m-%d %H:%M:%S");
15026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Shell support ===
15056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
15076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
15086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String[] args, ResultReceiver resultReceiver) throws RemoteException {
15096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceShell();
15116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
15136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
15166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Handle "adb shell cmd".
15176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
15186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class MyShellCommand extends ShellCommand {
15196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
15206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public int onCommand(String cmd) {
15216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (cmd == null) {
15226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return handleDefaultCommands(cmd);
15236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
15254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            int ret = 1;
15264362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            switch (cmd) {
15276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                case "reset-package-throttling":
15284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ret = handleResetPackageThrottling();
15294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    break;
15306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                case "reset-throttling":
15314362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ret = handleResetThrottling();
15324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    break;
15334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                case "override-config":
15344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ret = handleOverrideConfig();
15354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    break;
15364362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                case "reset-config":
15374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ret = handleResetConfig();
15384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    break;
15396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                default:
15406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    return handleDefaultCommands(cmd);
15416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15424362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            if (ret == 0) {
15434362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                pw.println("Success");
15444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
15454362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            return ret;
15466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
15496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onHelp() {
15506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
15516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Usage: cmd shortcut COMMAND [options ...]");
15526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
15536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
15546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for a package");
15556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
15566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-throttling");
15576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for all packages and users");
15586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
15594362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("cmd shortcut override-config CONFIG");
15604362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("    Override the configuration for testing (will last until reboot)");
15614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println();
15624362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("cmd shortcut reset-config");
15634362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("    Reset the configuration set with \"update-config\"");
15644362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println();
15656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int handleResetThrottling() {
15686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            resetThrottling();
15696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
15706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int handleResetPackageThrottling() {
15736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
15746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int userId = UserHandle.USER_SYSTEM;
15766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String opt;
15776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((opt = getNextOption()) != null) {
15786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (opt) {
15796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case "--user":
15806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        userId = UserHandle.parseUserArg(getNextArgRequired());
15816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
15826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    default:
15836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        pw.println("Error: Unknown option: " + opt);
15846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return 1;
15856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
15866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final String packageName = getNextArgRequired();
15886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
15906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).resetRateLimitingForCommandLine();
15916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                saveUserLocked(userId);
15926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
15956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
15974362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        private int handleOverrideConfig() {
15984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
15994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            final String config = getNextArgRequired();
16004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
16014362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            synchronized (mLock) {
16024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                if (!updateConfigurationLocked(config)) {
16034362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    pw.println("override-config failed.  See logcat for details.");
16044362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    return 1;
16054362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                }
16064362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
16074362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            return 0;
16084362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
16094362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
16104362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        private int handleResetConfig() {
16114362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            synchronized (mLock) {
16124362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                loadConfigurationLocked();
16134362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
16144362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            return 0;
16154362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
16166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Unit test support ===
16196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
16216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long injectCurrentTimeMillis() {
16226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return System.currentTimeMillis();
16236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
16266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    int injectBinderCallingUid() {
16276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return getCallingUid();
16286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectSystemDataPath() {
16316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return Environment.getDataSystemDirectory();
16326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectUserDataPath(@UserIdInt int userId) {
16355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
16365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
16375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
16384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
16395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    boolean injectIsLowRamDevice() {
16405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return ActivityManager.isLowRamDeviceStatic();
16415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
16425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
16435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    File getUserBitmapFilePath(@UserIdInt int userId) {
16445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
16456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
16483f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    SparseArray<UserShortcuts> getShortcutsForTest() {
16493f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return mUsers;
16506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
16534362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxDynamicShortcutsForTest() {
16544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxDynamicShortcuts;
16554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
16564362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
16574362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
16584362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxDailyUpdatesForTest() {
16594362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxDailyUpdates;
16604362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
16614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
16624362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
16634362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    long getResetIntervalForTest() {
16644362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mResetInterval;
16656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
16684362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxIconDimensionForTest() {
16694362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxIconDimension;
16706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
16734362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    CompressFormat getIconPersistFormatForTest() {
16744362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mIconPersistFormat;
16755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
16765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
16775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
16784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getIconPersistQualityForTest() {
16794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mIconPersistQuality;
16806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
168141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
168241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @VisibleForTesting
168341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
168441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        synchronized (mLock) {
168541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return getPackageShortcutsLocked(packageName, userId).findShortcutById(shortcutId);
168641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
168741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
168841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki}
168941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
16903f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onukiclass UserShortcuts {
16913f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private static final String TAG = ShortcutService.TAG;
16923f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
16933f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    @UserIdInt
16943f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    final int mUserId;
16953f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
16963f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>();
16973f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
16983f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public UserShortcuts(int userId) {
16993f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        mUserId = userId;
17003f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
17013f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17023f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public ArrayMap<String, PackageShortcuts> getPackages() {
17033f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return mPackages;
17043f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
17053f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17063f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
17073f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        out.startTag(null, ShortcutService.TAG_USER);
17083f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17093f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        for (int i = 0; i < mPackages.size(); i++) {
17103f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            final String packageName = mPackages.keyAt(i);
17113f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            final PackageShortcuts packageShortcuts = mPackages.valueAt(i);
17123f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17133f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            packageShortcuts.saveToXml(out);
17143f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
17153f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17163f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        out.endTag(null, ShortcutService.TAG_USER);
17173f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
17183f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17193f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public static UserShortcuts loadFromXml(XmlPullParser parser, int userId)
17203f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            throws IOException, XmlPullParserException {
17213f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final UserShortcuts ret = new UserShortcuts(userId);
17223f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17233f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final int outerDepth = parser.getDepth();
17243f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        int type;
17253f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
17263f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
17273f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            if (type != XmlPullParser.START_TAG) {
17283f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                continue;
17293f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            }
17303f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            final int depth = parser.getDepth();
17313f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            final String tag = parser.getName();
17323f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            switch (tag) {
17333f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                case ShortcutService.TAG_PACKAGE:
17343f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
17353f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17363f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    // Don't use addShortcut(), we don't need to save the icon.
17373f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    ret.getPackages().put(shortcuts.mPackageName, shortcuts);
17383f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    continue;
17393f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            }
17403f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
17413f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
17423f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return ret;
17433f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
17443f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17453f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
17463f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.print("  ");
17473f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.print("User: ");
17483f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.print(mUserId);
17493f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.println();
17503f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
17513f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        for (int i = 0; i < mPackages.size(); i++) {
17523f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            mPackages.valueAt(i).dump(s, pw, prefix + "  ");
17533f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
17543f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
17553f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki}
17563f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
175741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki/**
175841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * All the information relevant to shortcuts from a single package (per-user).
175941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */
176041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onukiclass PackageShortcuts {
176141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static final String TAG = ShortcutService.TAG;
176241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
176341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @UserIdInt
176441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final int mUserId;
176541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
176641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @NonNull
176741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final String mPackageName;
176841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
176941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
177041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * All the shortcuts from the package, keyed on IDs.
177141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
177241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
177341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
177441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
177541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * # of dynamic shortcuts.
177641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
177741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private int mDynamicShortcutCount = 0;
177841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
177941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
178041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * # of times the package has called rate-limited APIs.
178141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
178241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private int mApiCallCount;
178341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
178441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
178541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * When {@link #mApiCallCount} was reset last time.
178641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
178741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private long mLastResetTime;
178841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
178941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    PackageShortcuts(int userId, String packageName) {
179041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mUserId = userId;
179141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mPackageName = packageName;
179241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
179341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
179441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
179541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @Nullable
179641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public ShortcutInfo findShortcutById(String id) {
179741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return mShortcuts.get(id);
179841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
179941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
180041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
180141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @NonNull String id) {
180241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo shortcut = mShortcuts.remove(id);
180341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (shortcut != null) {
180441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            s.removeIcon(mUserId, shortcut);
180541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
180641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
180741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return shortcut;
180841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
180941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
181041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
181141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        deleteShortcut(s, newShortcut.getId());
181241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        s.saveIconAndFixUpShortcut(mUserId, newShortcut);
181341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mShortcuts.put(newShortcut.getId(), newShortcut);
181441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
181541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
181641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
181741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Add a shortcut, or update one with the same ID, with taking over existing flags.
181841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     *
181941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * It checks the max number of dynamic shortcuts.
182041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
182141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
182241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void updateShortcutWithCapping(@NonNull ShortcutService s,
182341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @NonNull ShortcutInfo newShortcut) {
182441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
182541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
182641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int oldFlags = 0;
182741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int newDynamicCount = mDynamicShortcutCount;
182841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
182941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut != null) {
183041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            oldFlags = oldShortcut.getFlags();
183141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (oldShortcut.isDynamic()) {
183241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                newDynamicCount--;
183341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
183441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
183541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (newShortcut.isDynamic()) {
183641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            newDynamicCount++;
183741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
183841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Make sure there's still room.
183941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        s.enforceMaxDynamicShortcuts(newDynamicCount);
184041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
184141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Okay, make it dynamic and add.
184241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        newShortcut.addFlags(oldFlags);
184341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
184441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        addShortcut(s, newShortcut);
184541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mDynamicShortcutCount = newDynamicCount;
184641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
184741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
184841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
184941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Remove all shortcuts that aren't pinned nor dynamic.
185041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
185141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private void removeOrphans(@NonNull ShortcutService s) {
185241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ArrayList<String> removeList = null; // Lazily initialize.
185341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
185441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
185541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = mShortcuts.valueAt(i);
185641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
185741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (si.isPinned() || si.isDynamic()) continue;
185841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
185941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (removeList == null) {
186041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                removeList = new ArrayList<>();
186141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
186241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            removeList.add(si.getId());
186341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
186441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (removeList != null) {
186541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            for (int i = removeList.size() - 1 ; i >= 0; i--) {
186641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                deleteShortcut(s, removeList.get(i));
186741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
186841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
186941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
187041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
187141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
187241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
187341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
187441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
187541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
187641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        removeOrphans(s);
187741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mDynamicShortcutCount = 0;
187841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
187941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
188041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
188141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
188241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
188341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
188441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut == null) {
188541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return;
188641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
188741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut.isDynamic()) {
188841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mDynamicShortcutCount--;
188941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
189041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut.isPinned()) {
189141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
189241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        } else {
189341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            deleteShortcut(s, shortcutId);
189441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
189541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
189641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
189741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
189841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void replacePinned(@NonNull ShortcutService s, String launcherPackage,
189941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            List<String> shortcutIds) {
190041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
190141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // TODO Should be per launcherPackage.
190241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
190341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // First, un-pin all shortcuts
190441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
190541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
190641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
190741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
190841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Then pin ALL
190941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = shortcutIds.size() - 1; i >= 0; i--) {
191041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i));
191141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (shortcut != null) {
191241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
191341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
191441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
191541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
191641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        removeOrphans(s);
191741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
191841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
191941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
192041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Number of calls that the caller has made, since the last reset.
192141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
192241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
192341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public int getApiCallCount(@NonNull ShortcutService s) {
192441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final long last = s.getLastResetTimeLocked();
192541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
192641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final long now = s.injectCurrentTimeMillis();
192741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (mLastResetTime > now) {
192841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            // Clock rewound. // TODO Test it
192941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mLastResetTime = now;
193041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
193141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
193241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // If not reset yet, then reset.
193341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (mLastResetTime < last) {
193441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mApiCallCount = 0;
193541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mLastResetTime = last;
193641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
193741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return mApiCallCount;
193841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
193941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
194041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
194141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
194241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * and return true.  Otherwise just return false.
194341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
194441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
194541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public boolean tryApiCall(@NonNull ShortcutService s) {
194641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
194741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return false;
194841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
194941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mApiCallCount++;
195041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return true;
195141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
195241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
195341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
195441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void resetRateLimitingForCommandLine() {
195541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mApiCallCount = 0;
195641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mLastResetTime = 0;
195741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
195841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
195941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
196041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Find all shortcuts that match {@code query}.
196141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
196241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
196341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void findAll(@NonNull List<ShortcutInfo> result,
196441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
196541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = 0; i < mShortcuts.size(); i++) {
196641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = mShortcuts.valueAt(i);
196741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (query == null || query.test(si)) {
196841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                result.add(si.clone(cloneFlag));
196941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
197041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
197141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
197241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
197341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
197441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
197541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Package: ");
197641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(mPackageName);
197741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
197841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
197941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
198041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
198141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Calls: ");
198241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(getApiCallCount(s));
198341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
198441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
198541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // This should be after getApiCallCount(), which may update it.
198641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
198741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
198841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Last reset: [");
198941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(mLastResetTime);
199041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("] ");
199141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(s.formatTime(mLastResetTime));
199241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
199341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
199441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println("      Shortcuts:");
199541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        long totalBitmapSize = 0;
199641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
199741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int size = shortcuts.size();
199841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = 0; i < size; i++) {
199941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = shortcuts.valueAt(i);
200041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            pw.print("        ");
200141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            pw.println(si.toInsecureString());
200234d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki            if (si.getBitmapPath() != null) {
200341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                final long len = new File(si.getBitmapPath()).length();
200441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.print("          ");
200541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.print("bitmap size=");
200641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.println(len);
200741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
200841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                totalBitmapSize += len;
200941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
201041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
201141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
201241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
201341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Total bitmap size: ");
201441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(totalBitmapSize);
201541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(" (");
201641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
201741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println(")");
201841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
201941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
202041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
202141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.startTag(null, ShortcutService.TAG_PACKAGE);
202241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
202341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_NAME, mPackageName);
202441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
202541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_CALL_COUNT, mApiCallCount);
202641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_LAST_RESET, mLastResetTime);
202741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
202841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int size = mShortcuts.size();
202941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int j = 0; j < size; j++) {
203041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            saveShortcut(out, mShortcuts.valueAt(j));
203141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
203241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
203341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.endTag(null, ShortcutService.TAG_PACKAGE);
203441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
203541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
203641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
203741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
203841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.startTag(null, ShortcutService.TAG_SHORTCUT);
203941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ID, si.getId());
204041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // writeAttr(out, "package", si.getPackageName()); // not needed
204141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ACTIVITY, si.getActivityComponent());
204241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // writeAttr(out, "icon", si.getIcon());  // We don't save it.
204341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_TITLE, si.getTitle());
204441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_INTENT, si.getIntentNoExtras());
204541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_WEIGHT, si.getWeight());
204641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_TIMESTAMP,
204741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                si.getLastChangedTimestamp());
204841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_FLAGS, si.getFlags());
204941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ICON_RES, si.getIconResourceId());
205041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_BITMAP_PATH, si.getBitmapPath());
205141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
205241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeTagExtra(out, ShortcutService.TAG_INTENT_EXTRAS,
205341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                si.getIntentPersistableExtras());
205441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeTagExtra(out, ShortcutService.TAG_EXTRAS, si.getExtras());
205541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
205641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.endTag(null, ShortcutService.TAG_SHORTCUT);
205741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
205841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
205941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId)
206041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
206141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
206241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final String packageName = ShortcutService.parseStringAttribute(parser,
206341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_NAME);
206441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
206541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final PackageShortcuts ret = new PackageShortcuts(userId, packageName);
206641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
206741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mDynamicShortcutCount =
206841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_DYNAMIC_COUNT);
206941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mApiCallCount =
207041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_CALL_COUNT);
207141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mLastResetTime =
207241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_LAST_RESET);
207341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
207441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int outerDepth = parser.getDepth();
207541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int type;
207641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
207741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
207841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (type != XmlPullParser.START_TAG) {
207941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                continue;
208041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
208141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final int depth = parser.getDepth();
208241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final String tag = parser.getName();
208341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            switch (tag) {
208441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_SHORTCUT:
208541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    final ShortcutInfo si = parseShortcut(parser, packageName);
208641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
208741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    // Don't use addShortcut(), we don't need to save the icon.
208841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    ret.mShortcuts.put(si.getId(), si);
208941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
209041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
209141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
209241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
209341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return ret;
209441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
209541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
209641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
209741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
209841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String id;
209941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ComponentName activityComponent;
210041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Icon icon;
210141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String title;
210241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        Intent intent;
210341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        PersistableBundle intentPersistableExtras = null;
210441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int weight;
210541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        PersistableBundle extras = null;
210641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        long lastChangedTimestamp;
210741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int flags;
210841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int iconRes;
210941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String bitmapPath;
211041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
211141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        id = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_ID);
211241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        activityComponent = ShortcutService.parseComponentNameAttribute(parser,
211341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_ACTIVITY);
211441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        title = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_TITLE);
211541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        intent = ShortcutService.parseIntentAttribute(parser, ShortcutService.ATTR_INTENT);
211641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        weight = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_WEIGHT);
211741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
211841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_TIMESTAMP);
211941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        flags = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_FLAGS);
212041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        iconRes = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_ICON_RES);
212141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        bitmapPath = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_BITMAP_PATH);
212241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
212341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int outerDepth = parser.getDepth();
212441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int type;
212541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
212641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
212741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (type != XmlPullParser.START_TAG) {
212841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                continue;
212941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
213041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final int depth = parser.getDepth();
213141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final String tag = parser.getName();
213241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (ShortcutService.DEBUG_LOAD) {
213341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
213441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                        depth, type, tag));
213541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
213641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            switch (tag) {
213741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_INTENT_EXTRAS:
213841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
213941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
214041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_EXTRAS:
214141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    extras = PersistableBundle.restoreFromXml(parser);
214241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
214341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
214441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
214541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
214641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return new ShortcutInfo(
214741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                id, packageName, activityComponent, /* icon =*/ null, title, intent,
214841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
214941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                iconRes, bitmapPath);
215041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
21516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki}
2152