ShortcutService.java revision 34d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79
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;
916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.function.Predicate;
926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki/**
946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * TODO:
956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Detect when already registered instances are passed to APIs again, which might break
975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   internal bitmap handling.
986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
1005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   -> Need to scan all packages when a user starts too.
1015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   -> Clear data -> remove all dynamic?  but not the pinned?
1026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
1036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Pinned per each launcher package (multiple launchers)
1046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
1055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Make save async (should we?)
1065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *
1075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Scan and remove orphan bitmaps (just in case).
1085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *
1095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Backup & restore
1106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */
1116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukipublic class ShortcutService extends IShortcutService.Stub {
1125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static final String TAG = "ShortcutService";
1136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final boolean DEBUG = false; // STOPSHIP if true
11541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final boolean DEBUG_LOAD = false; // STOPSHIP if true
1166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1174362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1184362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
1194362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1204362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1214362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_DAILY_UPDATES = 10;
1224362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1234362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1244362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
1254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1264362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
1284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1304362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
1314362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
1344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1364362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
1376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
1396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String FILENAME_BASE_STATE = "shortcut_service.xml";
1426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String DIRECTORY_PER_USER = "shortcut_service";
1456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
1486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static final String DIRECTORY_BITMAPS = "bitmaps";
1506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_ROOT = "root";
15241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_PACKAGE = "package";
15341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_LAST_RESET_TIME = "last_reset_time";
15441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_INTENT_EXTRAS = "intent-extras";
15541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_EXTRAS = "extras";
15641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_SHORTCUT = "shortcut";
15741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
15841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_VALUE = "value";
15941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_NAME = "name";
16041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
16141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_CALL_COUNT = "call-count";
16241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_LAST_RESET = "last-reset";
16341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ID = "id";
16441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ACTIVITY = "activity";
16541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_TITLE = "title";
16641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_INTENT = "intent";
16741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_WEIGHT = "weight";
16841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_TIMESTAMP = "timestamp";
16941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_FLAGS = "flags";
17041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ICON_RES = "icon-res";
17141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_BITMAP_PATH = "bitmap-path";
1726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1734362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1744362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    interface ConfigConstants {
1754362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1764362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the throttling reset interval, in seconds. (long)
1774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
1794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max number of modifying API calls per app for every interval. (int)
1824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_DAILY_UPDATES = "max_daily_updates";
1844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max icon dimensions in DP, for non-low-memory devices.
1874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1884362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
1894362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1904362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1914362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max icon dimensions in DP, for low-memory devices.
1924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
1944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max dynamic shortcuts per app. (int)
1974362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_SHORTCUTS = "max_shortcuts";
1994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
2004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
20141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki         * Key name for icon compression quality, 0-100.
2024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
2034362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_ICON_QUALITY = "icon_quality";
2044362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
2054362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
2064362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
2074362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
2084362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_ICON_FORMAT = "icon_format";
2094362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
2104362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
21141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final Context mContext;
2126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Object mLock = new Object();
2146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Handler mHandler;
2166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
2196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long mRawLastResetTime;
2226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * User ID -> package name -> list of ShortcutInfos.
2256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final SparseArray<ArrayMap<String, PackageShortcuts>> mShortcuts =
2286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            new SparseArray<>();
2296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Max number of dynamic shortcuts that each application can have at a time.
2326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private int mMaxDynamicShortcuts;
2346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Max number of updating API calls that each application can make a day.
2376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
23841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    int mMaxDailyUpdates;
2396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Actual throttling-reset interval.  By default it's a day.
2426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long mResetInterval;
2446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    /**
2465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * Icon max width/height in pixels.
2475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     */
2485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private int mMaxIconDimension;
2495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
2504362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private CompressFormat mIconPersistFormat;
2514362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private int mIconPersistQuality;
2525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
2536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ShortcutService(Context context) {
2546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mContext = Preconditions.checkNotNull(context);
2556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
2566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mHandler = new Handler(BackgroundThread.get().getLooper());
2576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
2586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * System service lifecycle.
2616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public static final class Lifecycle extends SystemService {
2636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ShortcutService mService;
2646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Lifecycle(Context context) {
2666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            super(context);
2676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mService = new ShortcutService(context);
2686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onStart() {
2726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            publishBinderService(Context.SHORTCUT_SERVICE, mService);
2736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onBootPhase(int phase) {
2776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mService.onBootPhase(phase);
2786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onCleanupUser(int userHandle) {
2826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mService.mLock) {
2836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mService.onCleanupUserInner(userHandle);
2846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
288f3a572b5c0cab23a435bd90414d25de84e00398eMakoto Onuki        public void onUnlockUser(int userId) {
2896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mService.mLock) {
2906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mService.onStartUserLocked(userId);
2916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
2946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
2966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onBootPhase(int phase) {
2976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
2986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.d(TAG, "onBootPhase: " + phase);
2996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        switch (phase) {
3016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            case SystemService.PHASE_LOCK_SETTINGS_READY:
3026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                initialize();
3036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                break;
3046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onStartUserLocked(int userId) {
3096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Preload
3106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getUserShortcutsLocked(userId);
3116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onCleanupUserInner(int userId) {
3156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Unload
3166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mShortcuts.delete(userId);
3176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the base state file name */
3206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private AtomicFile getBaseStateFile() {
3216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
3226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
3236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new AtomicFile(path);
3246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
3276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Init the instance. (load the state file, etc)
3286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
3296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void initialize() {
3306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
3314362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            loadConfigurationLocked();
3326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            loadBaseStateLocked();
3336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3364362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    /**
3374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     * Load the configuration from Settings.
3384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     */
3394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private void loadConfigurationLocked() {
3404362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        updateConfigurationLocked(injectShortcutManagerConstants());
3414362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
3424362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3434362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    /**
3444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     * Load the configuration from Settings.
3454362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     */
3464362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
3474362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    boolean updateConfigurationLocked(String config) {
3484362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        boolean result = true;
3494362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3504362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        final KeyValueListParser parser = new KeyValueListParser(',');
3514362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        try {
3524362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            parser.setString(config);
3534362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        } catch (IllegalArgumentException e) {
3544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            // Failed to parse the settings string, log this and move on
3554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            // with defaults.
3564362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            Slog.e(TAG, "Bad shortcut manager settings", e);
3574362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            result = false;
3584362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
3594362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3604362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mResetInterval = parser.getLong(
3614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
3624362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                * 1000L;
3634362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3644362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxDailyUpdates = (int) parser.getLong(
3654362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES);
3664362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3674362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxDynamicShortcuts = (int) parser.getLong(
3684362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP);
3694362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3704362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        final int iconDimensionDp = injectIsLowRamDevice()
3714362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ? (int) parser.getLong(
3724362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
3734362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
3744362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                : (int) parser.getLong(
3754362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
3764362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    DEFAULT_MAX_ICON_DIMENSION_DP);
3774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxIconDimension = injectDipToPixel(iconDimensionDp);
3794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mIconPersistFormat = CompressFormat.valueOf(
3814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
3824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mIconPersistQuality = (int) parser.getLong(
3844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_ICON_QUALITY,
3854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                DEFAULT_ICON_PERSIST_QUALITY);
3864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return result;
3884362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
3894362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
3914362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    String injectShortcutManagerConstants() {
3924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return android.provider.Settings.Global.getString(
3934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                mContext.getContentResolver(),
3944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
3954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
3965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
3974362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
3984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int injectDipToPixel(int dip) {
3994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
4004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                mContext.getResources().getDisplayMetrics());
4016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // === Persisting ===
4046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
40641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static String parseStringAttribute(XmlPullParser parser, String attribute) {
4076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return parser.getAttributeValue(null, attribute);
4086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
41041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static int parseIntAttribute(XmlPullParser parser, String attribute) {
41141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return (int) parseLongAttribute(parser, attribute);
41241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
41341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
41441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static long parseLongAttribute(XmlPullParser parser, String attribute) {
4156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
4186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
4206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return Long.parseLong(value);
4216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NumberFormatException e) {
4226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Error parsing long " + value);
4236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
4246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
42841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
4296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return ComponentName.unflattenFromString(value);
4346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
43741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
4386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
4436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return Intent.parseUri(value, /* flags =*/ 0);
4446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (URISyntaxException e) {
4456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Error parsing intent", e);
4466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
45041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
4516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) return;
4526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, tag);
4546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.attribute(null, ATTR_VALUE, value);
4556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, tag);
4566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
45841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
4596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeTagValue(out, tag, Long.toString(value));
4606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
46241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
4636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws IOException, XmlPullParserException {
4646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (bundle == null) return;
4656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, tag);
4676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        bundle.saveToXml(out);
4686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, tag);
4696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
47141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, String value) throws IOException {
4726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) return;
4736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.attribute(null, name, value);
4756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
47741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
4786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, String.valueOf(value));
4796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
48141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
4826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (comp == null) return;
4836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, comp.flattenToString());
4846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
48641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
4876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (intent == null) return;
4886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, intent.toUri(/* flags =*/ 0));
4906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
4936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void saveBaseStateLocked() {
4946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = getBaseStateFile();
4956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
4966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Saving to " + file.getBaseFile());
4976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        FileOutputStream outs = null;
5006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
5016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            outs = file.startWrite();
5026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Write to XML
5046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlSerializer out = new FastXmlSerializer();
5056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.setOutput(outs, StandardCharsets.UTF_8.name());
5066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startDocument(null, true);
5076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startTag(null, TAG_ROOT);
5086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Body.
5106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
5116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Epilogue.
5136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endTag(null, TAG_ROOT);
5146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endDocument();
5156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Close.
5176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.finishWrite(outs);
5186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException e) {
5196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
5206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.failWrite(outs);
5216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void loadBaseStateLocked() {
5256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mRawLastResetTime = 0;
5266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = getBaseStateFile();
5286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Loading from " + file.getBaseFile());
5306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try (FileInputStream in = file.openRead()) {
5326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
5336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            parser.setInput(in, StandardCharsets.UTF_8.name());
5346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int type;
5366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
5376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (type != XmlPullParser.START_TAG) {
5386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
5396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int depth = parser.getDepth();
5416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // Check the root tag
5426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String tag = parser.getName();
5436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (depth == 1) {
5446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    if (!TAG_ROOT.equals(tag)) {
5456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        Slog.e(TAG, "Invalid root tag: " + tag);
5466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return;
5476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
5486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
5496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // Assume depth == 2
5516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (tag) {
5526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case TAG_LAST_RESET_TIME:
5536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
5546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
5556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    default:
5566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        Slog.e(TAG, "Invalid tag: " + tag);
5576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
5586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
5606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (FileNotFoundException e) {
5616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Use the default
5626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
5636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
5646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = 0;
5666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Adjust the last reset time.
5686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getLastResetTimeLocked();
5696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void saveUserLocked(@UserIdInt int userId) {
5726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
5736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Saving to " + path);
5756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
5776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = new AtomicFile(path);
5786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        FileOutputStream outs = null;
5796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
5806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            outs = file.startWrite();
5816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Write to XML
5836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlSerializer out = new FastXmlSerializer();
5846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.setOutput(outs, StandardCharsets.UTF_8.name());
5856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startDocument(null, true);
5866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startTag(null, TAG_ROOT);
5876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayMap<String, PackageShortcuts> packages = getUserShortcutsLocked(userId);
5896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Body.
5916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < packages.size(); i++) {
5926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String packageName = packages.keyAt(i);
5935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final PackageShortcuts packageShortcuts = packages.valueAt(i);
5946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
59541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                packageShortcuts.saveToXml(out);
5966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
5976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Epilogue.
5996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endTag(null, TAG_ROOT);
6006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endDocument();
6016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Close.
6036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.finishWrite(outs);
6046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
6056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
6066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.failWrite(outs);
6076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
61041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static IOException throwForInvalidTag(int depth, String tag) throws IOException {
6116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
6126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
6156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ArrayMap<String, PackageShortcuts> loadUserLocked(@UserIdInt int userId) {
6166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
6176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
6186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Loading from " + path);
6196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = new AtomicFile(path);
6216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final FileInputStream in;
6236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
6246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            in = file.openRead();
6256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (FileNotFoundException e) {
6266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (DEBUG) {
6276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                Slog.i(TAG, "Not found " + path);
6286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
6296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
6306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
63141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ArrayMap<String, PackageShortcuts> ret = new ArrayMap<>();
6326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
6336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
6346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            parser.setInput(in, StandardCharsets.UTF_8.name());
6356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            PackageShortcuts shortcuts = null;
6376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int type;
6396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
6406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (type != XmlPullParser.START_TAG) {
6416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
6426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int depth = parser.getDepth();
6446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String tag = parser.getName();
6466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (DEBUG_LOAD) {
6476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    Slog.d(TAG, String.format("depth=%d type=%d name=%s",
6486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            depth, type, tag));
6496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (depth) {
6516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case 1: {
6526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (TAG_ROOT.equals(tag)) {
6536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            continue;
6546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
6556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
6566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
6576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case 2: {
6586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        switch (tag) {
6595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            case TAG_PACKAGE:
66041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                                shortcuts = PackageShortcuts.loadFromXml(parser, userId);
66141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                                ret.put(shortcuts.mPackageName, shortcuts);
6626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                continue;
6636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
6646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
6656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
6666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                throwForInvalidTag(depth, tag);
6686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
6696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
6706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
6716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
6726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
6736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } finally {
6746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            IoUtils.closeQuietly(in);
6756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // TODO Actually make it async.
6796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void scheduleSaveBaseState() {
6806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
6816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            saveBaseStateLocked();
6826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // TODO Actually make it async.
6866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void scheduleSaveUser(@UserIdInt int userId) {
6876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
6886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            saveUserLocked(userId);
6896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the last reset time. */
6936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long getLastResetTimeLocked() {
6946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        updateTimes();
6956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mRawLastResetTime;
6966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the next reset time. */
6996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long getNextResetTimeLocked() {
7006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        updateTimes();
7016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mRawLastResetTime + mResetInterval;
7026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
7056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Update the last reset time.
7066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
7076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void updateTimes() {
7086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long now = injectCurrentTimeMillis();
7106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long prevLastResetTime = mRawLastResetTime;
7126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mRawLastResetTime == 0) { // first launch.
7146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
7156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
7166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else if (now < mRawLastResetTime) {
7176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Clock rewound.
7186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
7196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
7206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else {
7216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Do it properly.
7226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((mRawLastResetTime + mResetInterval) <= now) {
7236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mRawLastResetTime += mResetInterval;
7246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (prevLastResetTime != mRawLastResetTime) {
7276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            scheduleSaveBaseState();
7286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user state. */
7326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
7336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
7346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ArrayMap<String, PackageShortcuts> getUserShortcutsLocked(@UserIdInt int userId) {
7356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        ArrayMap<String, PackageShortcuts> userPackages = mShortcuts.get(userId);
7366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (userPackages == null) {
7376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackages = loadUserLocked(userId);
7386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (userPackages == null) {
7396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                userPackages = new ArrayMap<>();
7406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mShortcuts.put(userId, userPackages);
7426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return userPackages;
7446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user per-package state. */
7476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
7486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
7496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private PackageShortcuts getPackageShortcutsLocked(
7506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @NonNull String packageName, @UserIdInt int userId) {
7516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayMap<String, PackageShortcuts> userPackages = getUserShortcutsLocked(userId);
7526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        PackageShortcuts shortcuts = userPackages.get(packageName);
7536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcuts == null) {
7545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcuts = new PackageShortcuts(userId, packageName);
7556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackages.put(packageName, shortcuts);
7566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return shortcuts;
7586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Caller validation ===
7616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) {
7635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.getBitmapPath() != null) {
7645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (DEBUG) {
7655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                Slog.d(TAG, "Removing " + shortcut.getBitmapPath());
7665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
7675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            new File(shortcut.getBitmapPath()).delete();
7685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setBitmapPath(null);
7705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setIconResourceId(0);
7715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES);
7725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
7765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static class FileOutputStreamWithPath extends FileOutputStream {
7775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        private final File mFile;
7785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public FileOutputStreamWithPath(File file) throws FileNotFoundException {
7805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            super(file);
7815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            mFile = file;
7825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public File getFile() {
7855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mFile;
7865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    /**
7905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * Build the cached bitmap filename for a shortcut icon.
7915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     *
7925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * The filename will be based on the ID, except certain characters will be escaped.
7935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     */
7945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
7955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
7965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            throws IOException {
7975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final File packagePath = new File(getUserBitmapFilePath(userId),
7985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                shortcut.getPackageName());
7995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!packagePath.isDirectory()) {
8005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            packagePath.mkdirs();
8015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!packagePath.isDirectory()) {
8025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                throw new IOException("Unable to create directory " + packagePath);
8035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            SELinux.restorecon(packagePath);
8055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final String baseName = String.valueOf(injectCurrentTimeMillis());
8085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (int suffix = 0;; suffix++) {
8095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
8105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final File file = new File(packagePath, filename);
8115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!file.exists()) {
8125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (DEBUG) {
8135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
8145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return new FileOutputStreamWithPath(file);
8165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
8195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) {
8215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
8225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
8235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final long token = Binder.clearCallingIdentity();
8265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        try {
8275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            // Clear icon info on the shortcut.
8285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setIconResourceId(0);
8295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setBitmapPath(null);
8305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final Icon icon = shortcut.getIcon();
8325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (icon == null) {
8335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return; // has no icon
8345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Bitmap bitmap = null;
8375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            try {
8385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                switch (icon.getType()) {
8395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_RESOURCE: {
8405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        injectValidateIconResPackage(shortcut, icon);
8415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.setIconResourceId(icon.getResId());
8435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
8445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        return;
8455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_BITMAP: {
8475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        bitmap = icon.getBitmap();
8485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        break;
8495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_URI: {
8515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        final Uri uri = ContentProvider.maybeAddUserId(icon.getUri(), userId);
8525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
8545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            bitmap = BitmapFactory.decodeStream(is);
8565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        } catch (IOException e) {
8585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            Slog.e(TAG, "Unable to load icon from " + uri);
8595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            return;
8605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        }
8615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        break;
8625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    default:
8645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        // This shouldn't happen because we've already validated the icon, but
8655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        // just in case.
8665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        throw ShortcutInfo.getInvalidIconException();
8675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (bitmap == null) {
8695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.e(TAG, "Null bitmap detected");
8705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return;
8715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                // Shrink and write to the file.
8735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                File path = null;
8745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                try {
8755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut);
8765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    try {
8775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        path = out.getFile();
8785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shrinkBitmap(bitmap, mMaxIconDimension)
8805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                                .compress(mIconPersistFormat, mIconPersistQuality, out);
8815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.setBitmapPath(out.getFile().getAbsolutePath());
8835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
8845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    } finally {
8855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        IoUtils.closeQuietly(out);
8865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                } catch (IOException|RuntimeException e) {
8885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    // STOPSHIP Change wtf to e
8895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
8905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (path != null && path.exists()) {
8915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        path.delete();
8925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } finally {
8955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (bitmap != null) {
8965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    bitmap.recycle();
8975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                // Once saved, we won't use the original icon information, so null it out.
8995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                shortcut.clearIcon();
9005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
9015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        } finally {
9025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Binder.restoreCallingIdentity(token);
9035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
9055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // Unfortunately we can't do this check in unit tests because we fake creator package names,
9075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // so override in unit tests.
9085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // TODO CTS this case.
9095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
9105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!shortcut.getPackageName().equals(icon.getResPackage())) {
9115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            throw new IllegalArgumentException(
9125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    "Icon resource must reside in shortcut owner package");
9135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
9155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
9175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
9185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        // Original width/height.
9195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int ow = in.getWidth();
9205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int oh = in.getHeight();
9215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if ((ow <= maxSize) && (oh <= maxSize)) {
9225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (DEBUG) {
9235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
9245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
9255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return in;
9265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int longerDimension = Math.max(ow, oh);
9285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        // New width and height.
9305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int nw = ow * maxSize / longerDimension;
9315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int nh = oh * maxSize / longerDimension;
9325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (DEBUG) {
9335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
9345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    ow, oh, nw, nh));
9355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
9385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final Canvas c = new Canvas(scaledBitmap);
9395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final RectF dst = new RectF(0, 0, nw, nh);
9415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
9435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        in.recycle();
9455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return scaledBitmap;
9475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
9485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // === Caller validation ===
9505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerSystem() {
9526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
9546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerShell() {
9576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
9596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceSystemOrShell() {
9626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerSystem() || isCallerShell(),
9636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                "Caller must be system or shell");
9646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceShell() {
9676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerShell(), "Caller must be shell");
9686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
9716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(packageName, "packageName");
9726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (isCallerSystem()) {
9746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // no check
9756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Otherwise, make sure the arguments are valid.
9806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (UserHandle.getUserId(callingUid) != userId) {
9816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new SecurityException("Invalid user-ID");
9826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
9846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // Caller is valid.
9856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new SecurityException("Caller UID= doesn't own " + packageName);
9876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Test overrides it.
9905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
9916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
9926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Is MATCH_UNINSTALLED_PACKAGES correct to get SD card app info?
9946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mContext.getPackageManager().getPackageUidAsUser(packageName,
9966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
9975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            | PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
9986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NameNotFoundException e) {
9996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return -1;
10006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
10046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
10056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
10066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void enforceMaxDynamicShortcuts(int numShortcuts) {
10076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (numShortcuts > mMaxDynamicShortcuts) {
10086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
10096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
10136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Sends a notification to LauncherApps
10146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Write to file
10156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
10166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) {
10176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        notifyListeners(packageName, userId);
10186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveUser(userId);
10196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
10226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutChangeListener> copy;
10236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> shortcuts = new ArrayList<>();
10246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
10256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy = new ArrayList<>(mListeners);
10266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId)
10286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    .findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
10296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        for (int i = copy.size() - 1; i >= 0; i--) {
10316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy.get(i).onShortcutChanged(packageName, shortcuts, userId);
10326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
10366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Clean up / validate an incoming shortcut.
10376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure all mandatory fields are set.
10386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure the intent's extras are persistable, and them to set
10396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     *  {@link ShortcutInfo#mIntentPersistableExtras}.  Also clear its extras.
10406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Clear flags.
10415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     *
10425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * TODO Detailed unit tests
10436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
10445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
10456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkNotNull(shortcut, "Null shortcut detected");
10466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcut.getActivityComponent() != null) {
10476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkState(
10486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    shortcut.getPackageName().equals(
10496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            shortcut.getActivityComponent().getPackageName()),
10506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    "Activity package name mismatch");
10516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!forUpdate) {
10545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.enforceMandatoryFields();
10555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.getIcon() != null) {
10575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            ShortcutInfo.validateIcon(shortcut.getIcon());
10585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validateForXml(shortcut.getId());
10615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validateForXml(shortcut.getTitle());
10625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validatePersistableBundleForXml(shortcut.getIntentPersistableExtras());
10635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validatePersistableBundleForXml(shortcut.getExtras());
10646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        shortcut.setFlags(0);
10665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
10685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those
10695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // characters.
10705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
10715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static void validatePersistableBundleForXml(PersistableBundle b) {
10725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (b == null || b.size() == 0) {
10735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
10746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (String key : b.keySet()) {
10765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            validateForXml(key);
10775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final Object value = b.get(key);
10785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (value == null) {
10795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                continue;
10805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof String) {
10815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                validateForXml((String) value);
10825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof String[]) {
10835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                for (String v : (String[]) value) {
10845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    validateForXml(v);
10855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
10865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof PersistableBundle) {
10875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                validatePersistableBundleForXml((PersistableBundle) value);
10885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
10895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static void validateForXml(String s) {
10935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (TextUtils.isEmpty(s)) {
10945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
10955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (int i = s.length() - 1; i >= 0; i--) {
10975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!isAllowedInXml(s.charAt(i))) {
10985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                throw new IllegalArgumentException("Unsupported character detected in: " + s);
10995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
11005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
11015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
11026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static boolean isAllowedInXml(char c) {
11045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
11056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === APIs ===
11086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
11116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
11156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int size = newShortcuts.size();
11166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
11216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
11226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
11236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            enforceMaxDynamicShortcuts(size);
11256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcuts.
11276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
11285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
11296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // First, remove all un-pinned; dynamic shortcuts
11325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            ps.deleteAllDynamicShortcuts(this);
11336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Then, add/update all.  We need to make sure to take over "pinned" flag.
11356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
11366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo newShortcut = newShortcuts.get(i);
11376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
11386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                ps.updateShortcutWithCapping(this, newShortcut);
11396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
11476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
11515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int size = newShortcuts.size();
11526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            // Throttling.
11575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!ps.tryApiCall(this)) {
11585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return false;
11596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            for (int i = 0; i < size; i++) {
11625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo source = newShortcuts.get(i);
11635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
11645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo target = ps.findShortcutById(source.getId());
11665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (target != null) {
11675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    final boolean replacingIcon = (source.getIcon() != null);
11685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (replacingIcon) {
11695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        removeIcon(userId, target);
11705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
11715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    target.copyNonNullFieldsFrom(source);
11735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (replacingIcon) {
11755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        saveIconAndFixUpShortcut(userId, target);
11765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
11775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
11785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
11796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
11876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
11946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
11956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
11966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcut.
11995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
12006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Add it.
12026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
12036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            ps.updateShortcutWithCapping(this, newShortcut);
12046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
12066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
12086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteDynamicShortcut(String packageName, String shortcutId,
12126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
12156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this, shortcutId);
12186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
12206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
12246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(this);
12286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
12306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
12346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
12386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
12396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isDynamic);
12406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
12456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
12496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
12506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isPinned);
12516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
12556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
12566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutInfo> ret = new ArrayList<>();
12586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
12606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new ParceledListSlice<>(ret);
12626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
12666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws RemoteException {
12676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mMaxDynamicShortcuts;
12706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
12746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return mMaxDailyUpdates
12786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
12796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
12846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getNextResetTimeLocked();
12886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @Override
12925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
12935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        synchronized (mLock) {
12945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mMaxIconDimension;
12955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
12965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
12975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
12986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
12996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Reset all throttling, for developer options and command line.  Only system/shell can call it.
13006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
13016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
13026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void resetThrottling() {
13036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceSystemOrShell();
13046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        resetThrottlingInner();
13066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
13076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
13096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void resetThrottlingInner() {
13106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
13116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = injectCurrentTimeMillis();
13126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveBaseState();
13145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        Slog.i(TAG, "ShortcutManager: throttling counter reset");
13156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
13166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
13186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Entry point from {@link LauncherApps}.
13196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
13206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class LocalService extends ShortcutServiceInternal {
13216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcuts(
13236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage, long changedSince,
13246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable String packageName, @Nullable ComponentName componentName,
13256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int queryFlags, int userId) {
13266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>();
13276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final int cloneFlag =
13286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
13296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
13306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
13316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (packageName != null) {
13346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags,
13356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            userId, ret, cloneFlag);
13366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                } else {
13376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    final ArrayMap<String, PackageShortcuts> packages =
13386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            getUserShortcutsLocked(userId);
13395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    for (int i = packages.size() - 1; i >= 0; i--) {
13406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        getShortcutsInnerLocked(
13416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                packages.keyAt(i),
13426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                changedSince, componentName, queryFlags, userId, ret, cloneFlag);
13436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
13446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
13456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
13476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince,
13506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable ComponentName componentName, int queryFlags,
13516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
13526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId).findAll(ret,
13536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    (ShortcutInfo si) -> {
13546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (si.getLastChangedTimestamp() < changedSince) {
13556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
13566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
13576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (componentName != null
13586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && !componentName.equals(si.getActivityComponent())) {
13596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
13606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
13616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchDynamic =
13626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
13636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && si.isDynamic();
13646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchPinned =
13656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
13666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                        && si.isPinned();
13676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return matchDynamic || matchPinned;
13686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }, cloneFlag);
13696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcutInfo(
13736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage,
13746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String packageName, @Nullable List<String> ids, int userId) {
13756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
13766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
13776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
13796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArraySet<String> idSet = new ArraySet<>(ids);
13806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).findAll(ret,
13826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        (ShortcutInfo si) -> idSet.contains(si.getId()),
13836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
13846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
13866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
13906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull List<String> shortcutIds, int userId) {
13916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
13926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
13936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkNotNull(shortcutIds, "shortcutIds");
13946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                getPackageShortcutsLocked(packageName, userId).replacePinned(
13975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        ShortcutService.this, callingPackage, shortcutIds);
13986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackageChanged(packageName, userId);
14006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Intent createShortcutIntent(@NonNull String callingPackage,
140443204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki                @NonNull String packageName, @NonNull String shortcutId, int userId) {
14056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
140643204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
140743204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki            Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
14086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo fullShortcut =
141143204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki                        getPackageShortcutsLocked(packageName, userId)
14125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        .findShortcutById(shortcutId);
14135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return fullShortcut == null ? null : fullShortcut.getIntent();
14146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void addListener(@NonNull ShortcutChangeListener listener) {
14196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mListeners.add(Preconditions.checkNotNull(listener));
14216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        @Override
14255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public int getShortcutIconResId(@NonNull String callingPackage,
14265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                @NonNull ShortcutInfo shortcut, int userId) {
14275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Preconditions.checkNotNull(shortcut, "shortcut");
14285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            synchronized (mLock) {
14305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
14315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
14325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return (shortcutInfo != null && shortcutInfo.hasIconResource())
14335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        ? shortcutInfo.getIconResourceId() : 0;
14345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
14355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
14365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        @Override
14385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
143934d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                @NonNull ShortcutInfo shortcutIn, int userId) {
144034d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki            Preconditions.checkNotNull(shortcutIn, "shortcut");
14415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            synchronized (mLock) {
14435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
144434d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
14455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
14465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return null;
14475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
14485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                try {
144934d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                    if (shortcutInfo.getBitmapPath() == null) {
145034d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
145134d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        return null;
145234d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                    }
14535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return ParcelFileDescriptor.open(
14545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            new File(shortcutInfo.getBitmapPath()),
14555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            ParcelFileDescriptor.MODE_READ_ONLY);
14565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                } catch (FileNotFoundException e) {
14575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath());
14585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return null;
14595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
14605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
14615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
14626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Dump ===
14656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
14676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
14686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
14696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                != PackageManager.PERMISSION_GRANTED) {
14706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Permission Denial: can't dump UserManager from from pid="
14716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + Binder.getCallingPid()
14726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + ", uid=" + Binder.getCallingUid()
14736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + " without permission "
14746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + android.Manifest.permission.DUMP);
14756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
14766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        dumpInner(pw);
14786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
14816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void dumpInner(PrintWriter pw) {
14826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
14836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long now = injectCurrentTimeMillis();
14846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("Now: [");
14856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(now);
14866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
14876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(now));
14885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Raw last reset: [");
14906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(mRawLastResetTime);
14916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
14926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(mRawLastResetTime));
14936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long last = getLastResetTimeLocked();
14956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Last reset: [");
14966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(last);
14976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
14986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(last));
14996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final long next = getNextResetTimeLocked();
15016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Next reset: [");
15026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(next);
15036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(next));
15056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
15066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Max icon dim: ");
15085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mMaxIconDimension);
15095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Icon format: ");
15105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mIconPersistFormat);
15115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Icon quality: ");
15125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mIconPersistQuality);
15135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.println();
15145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
15166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < mShortcuts.size(); i++) {
15186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                dumpUserLocked(pw, mShortcuts.keyAt(i));
15196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void dumpUserLocked(PrintWriter pw, int userId) {
15246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print("  User: ");
15256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print(userId);
15266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.println();
15276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayMap<String, PackageShortcuts> packages = mShortcuts.get(userId);
15296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (packages == null) {
15306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
15316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        for (int j = 0; j < packages.size(); j++) {
15336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            dumpPackageLocked(pw, userId, packages.keyAt(j));
15346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.println();
15366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void dumpPackageLocked(PrintWriter pw, int userId, String packageName) {
15395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final PackageShortcuts packageShortcuts = mShortcuts.get(userId).get(packageName);
15405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (packageShortcuts == null) {
15416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
15426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
154441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        packageShortcuts.dump(this, pw, "    ");
15456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
154741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static String formatTime(long time) {
15486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Time tobj = new Time();
15496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        tobj.set(time);
15506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return tobj.format("%Y-%m-%d %H:%M:%S");
15516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Shell support ===
15546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
15566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
15576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String[] args, ResultReceiver resultReceiver) throws RemoteException {
15586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceShell();
15606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
15626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
15656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Handle "adb shell cmd".
15666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
15676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class MyShellCommand extends ShellCommand {
15686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
15696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public int onCommand(String cmd) {
15706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (cmd == null) {
15716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return handleDefaultCommands(cmd);
15726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
15744362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            int ret = 1;
15754362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            switch (cmd) {
15766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                case "reset-package-throttling":
15774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ret = handleResetPackageThrottling();
15784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    break;
15796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                case "reset-throttling":
15804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ret = handleResetThrottling();
15814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    break;
15824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                case "override-config":
15834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ret = handleOverrideConfig();
15844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    break;
15854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                case "reset-config":
15864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ret = handleResetConfig();
15874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    break;
15886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                default:
15896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    return handleDefaultCommands(cmd);
15906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15914362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            if (ret == 0) {
15924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                pw.println("Success");
15934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
15944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            return ret;
15956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
15986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onHelp() {
15996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
16006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Usage: cmd shortcut COMMAND [options ...]");
16016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
16036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for a package");
16046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-throttling");
16066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for all packages and users");
16076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16084362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("cmd shortcut override-config CONFIG");
16094362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("    Override the configuration for testing (will last until reboot)");
16104362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println();
16114362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("cmd shortcut reset-config");
16124362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("    Reset the configuration set with \"update-config\"");
16134362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println();
16146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
16156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int handleResetThrottling() {
16176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            resetThrottling();
16186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
16196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
16206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int handleResetPackageThrottling() {
16226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
16236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int userId = UserHandle.USER_SYSTEM;
16256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String opt;
16266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((opt = getNextOption()) != null) {
16276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (opt) {
16286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case "--user":
16296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        userId = UserHandle.parseUserArg(getNextArgRequired());
16306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
16316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    default:
16326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        pw.println("Error: Unknown option: " + opt);
16336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return 1;
16346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
16356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
16366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final String packageName = getNextArgRequired();
16376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
16396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).resetRateLimitingForCommandLine();
16406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                saveUserLocked(userId);
16416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
16426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
16446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
16454362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
16464362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        private int handleOverrideConfig() {
16474362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
16484362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            final String config = getNextArgRequired();
16494362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
16504362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            synchronized (mLock) {
16514362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                if (!updateConfigurationLocked(config)) {
16524362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    pw.println("override-config failed.  See logcat for details.");
16534362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    return 1;
16544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                }
16554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
16564362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            return 0;
16574362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
16584362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
16594362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        private int handleResetConfig() {
16604362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            synchronized (mLock) {
16614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                loadConfigurationLocked();
16624362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
16634362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            return 0;
16644362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
16656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Unit test support ===
16686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
16706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long injectCurrentTimeMillis() {
16716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return System.currentTimeMillis();
16726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
16756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    int injectBinderCallingUid() {
16766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return getCallingUid();
16776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectSystemDataPath() {
16806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return Environment.getDataSystemDirectory();
16816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectUserDataPath(@UserIdInt int userId) {
16845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
16855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
16865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
16874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
16885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    boolean injectIsLowRamDevice() {
16895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return ActivityManager.isLowRamDeviceStatic();
16905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
16915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
16925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    File getUserBitmapFilePath(@UserIdInt int userId) {
16935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
16946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
16976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    SparseArray<ArrayMap<String, PackageShortcuts>> getShortcutsForTest() {
16986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mShortcuts;
16996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
17024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxDynamicShortcutsForTest() {
17034362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxDynamicShortcuts;
17044362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
17054362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
17064362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
17074362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxDailyUpdatesForTest() {
17084362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxDailyUpdates;
17094362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
17104362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
17114362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
17124362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    long getResetIntervalForTest() {
17134362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mResetInterval;
17146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
17174362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxIconDimensionForTest() {
17184362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxIconDimension;
17196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
17224362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    CompressFormat getIconPersistFormatForTest() {
17234362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mIconPersistFormat;
17245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
17255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
17265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
17274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getIconPersistQualityForTest() {
17284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mIconPersistQuality;
17296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
173041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
173141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @VisibleForTesting
173241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
173341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        synchronized (mLock) {
173441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return getPackageShortcutsLocked(packageName, userId).findShortcutById(shortcutId);
173541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
173641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
173741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki}
173841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
173941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki/**
174041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * All the information relevant to shortcuts from a single package (per-user).
174141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */
174241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onukiclass PackageShortcuts {
174341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static final String TAG = ShortcutService.TAG;
174441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
174541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @UserIdInt
174641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final int mUserId;
174741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
174841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @NonNull
174941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final String mPackageName;
175041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
175141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
175241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * All the shortcuts from the package, keyed on IDs.
175341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
175441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
175541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
175641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
175741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * # of dynamic shortcuts.
175841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
175941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private int mDynamicShortcutCount = 0;
176041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
176141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
176241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * # of times the package has called rate-limited APIs.
176341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
176441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private int mApiCallCount;
176541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
176641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
176741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * When {@link #mApiCallCount} was reset last time.
176841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
176941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private long mLastResetTime;
177041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
177141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    PackageShortcuts(int userId, String packageName) {
177241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mUserId = userId;
177341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mPackageName = packageName;
177441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
177541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
177641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
177741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @Nullable
177841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public ShortcutInfo findShortcutById(String id) {
177941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return mShortcuts.get(id);
178041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
178141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
178241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
178341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @NonNull String id) {
178441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo shortcut = mShortcuts.remove(id);
178541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (shortcut != null) {
178641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            s.removeIcon(mUserId, shortcut);
178741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
178841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
178941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return shortcut;
179041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
179141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
179241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
179341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        deleteShortcut(s, newShortcut.getId());
179441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        s.saveIconAndFixUpShortcut(mUserId, newShortcut);
179541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mShortcuts.put(newShortcut.getId(), newShortcut);
179641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
179741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
179841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
179941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Add a shortcut, or update one with the same ID, with taking over existing flags.
180041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     *
180141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * It checks the max number of dynamic shortcuts.
180241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
180341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
180441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void updateShortcutWithCapping(@NonNull ShortcutService s,
180541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @NonNull ShortcutInfo newShortcut) {
180641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
180741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
180841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int oldFlags = 0;
180941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int newDynamicCount = mDynamicShortcutCount;
181041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
181141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut != null) {
181241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            oldFlags = oldShortcut.getFlags();
181341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (oldShortcut.isDynamic()) {
181441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                newDynamicCount--;
181541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
181641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
181741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (newShortcut.isDynamic()) {
181841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            newDynamicCount++;
181941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
182041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Make sure there's still room.
182141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        s.enforceMaxDynamicShortcuts(newDynamicCount);
182241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
182341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Okay, make it dynamic and add.
182441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        newShortcut.addFlags(oldFlags);
182541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
182641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        addShortcut(s, newShortcut);
182741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mDynamicShortcutCount = newDynamicCount;
182841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
182941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
183041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
183141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Remove all shortcuts that aren't pinned nor dynamic.
183241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
183341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private void removeOrphans(@NonNull ShortcutService s) {
183441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ArrayList<String> removeList = null; // Lazily initialize.
183541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
183641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
183741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = mShortcuts.valueAt(i);
183841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
183941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (si.isPinned() || si.isDynamic()) continue;
184041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
184141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (removeList == null) {
184241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                removeList = new ArrayList<>();
184341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
184441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            removeList.add(si.getId());
184541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
184641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (removeList != null) {
184741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            for (int i = removeList.size() - 1 ; i >= 0; i--) {
184841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                deleteShortcut(s, removeList.get(i));
184941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
185041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
185141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
185241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
185341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
185441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
185541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
185641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
185741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
185841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        removeOrphans(s);
185941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mDynamicShortcutCount = 0;
186041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
186141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
186241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
186341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
186441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
186541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
186641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut == null) {
186741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return;
186841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
186941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut.isDynamic()) {
187041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mDynamicShortcutCount--;
187141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
187241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut.isPinned()) {
187341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
187441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        } else {
187541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            deleteShortcut(s, shortcutId);
187641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
187741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
187841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
187941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
188041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void replacePinned(@NonNull ShortcutService s, String launcherPackage,
188141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            List<String> shortcutIds) {
188241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
188341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // TODO Should be per launcherPackage.
188441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
188541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // First, un-pin all shortcuts
188641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
188741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
188841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
188941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
189041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Then pin ALL
189141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = shortcutIds.size() - 1; i >= 0; i--) {
189241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i));
189341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (shortcut != null) {
189441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
189541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
189641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
189741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
189841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        removeOrphans(s);
189941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
190041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
190141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
190241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Number of calls that the caller has made, since the last reset.
190341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
190441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
190541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public int getApiCallCount(@NonNull ShortcutService s) {
190641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final long last = s.getLastResetTimeLocked();
190741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
190841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final long now = s.injectCurrentTimeMillis();
190941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (mLastResetTime > now) {
191041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            // Clock rewound. // TODO Test it
191141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mLastResetTime = now;
191241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
191341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
191441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // If not reset yet, then reset.
191541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (mLastResetTime < last) {
191641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mApiCallCount = 0;
191741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mLastResetTime = last;
191841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
191941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return mApiCallCount;
192041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
192141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
192241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
192341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
192441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * and return true.  Otherwise just return false.
192541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
192641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
192741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public boolean tryApiCall(@NonNull ShortcutService s) {
192841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
192941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return false;
193041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
193141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mApiCallCount++;
193241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return true;
193341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
193441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
193541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
193641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void resetRateLimitingForCommandLine() {
193741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mApiCallCount = 0;
193841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mLastResetTime = 0;
193941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
194041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
194141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
194241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Find all shortcuts that match {@code query}.
194341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
194441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
194541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void findAll(@NonNull List<ShortcutInfo> result,
194641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
194741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = 0; i < mShortcuts.size(); i++) {
194841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = mShortcuts.valueAt(i);
194941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (query == null || query.test(si)) {
195041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                result.add(si.clone(cloneFlag));
195141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
195241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
195341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
195441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
195541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
195641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
195741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Package: ");
195841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(mPackageName);
195941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
196041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
196141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
196241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
196341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Calls: ");
196441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(getApiCallCount(s));
196541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
196641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
196741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // This should be after getApiCallCount(), which may update it.
196841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
196941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
197041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Last reset: [");
197141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(mLastResetTime);
197241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("] ");
197341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(s.formatTime(mLastResetTime));
197441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
197541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
197641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println("      Shortcuts:");
197741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        long totalBitmapSize = 0;
197841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
197941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int size = shortcuts.size();
198041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = 0; i < size; i++) {
198141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = shortcuts.valueAt(i);
198241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            pw.print("        ");
198341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            pw.println(si.toInsecureString());
198434d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki            if (si.getBitmapPath() != null) {
198541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                final long len = new File(si.getBitmapPath()).length();
198641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.print("          ");
198741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.print("bitmap size=");
198841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.println(len);
198941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
199041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                totalBitmapSize += len;
199141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
199241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
199341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
199441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
199541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Total bitmap size: ");
199641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(totalBitmapSize);
199741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(" (");
199841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
199941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println(")");
200041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
200141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
200241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
200341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.startTag(null, ShortcutService.TAG_PACKAGE);
200441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
200541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_NAME, mPackageName);
200641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
200741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_CALL_COUNT, mApiCallCount);
200841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_LAST_RESET, mLastResetTime);
200941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
201041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int size = mShortcuts.size();
201141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int j = 0; j < size; j++) {
201241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            saveShortcut(out, mShortcuts.valueAt(j));
201341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
201441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
201541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.endTag(null, ShortcutService.TAG_PACKAGE);
201641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
201741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
201841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
201941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
202041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.startTag(null, ShortcutService.TAG_SHORTCUT);
202141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ID, si.getId());
202241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // writeAttr(out, "package", si.getPackageName()); // not needed
202341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ACTIVITY, si.getActivityComponent());
202441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // writeAttr(out, "icon", si.getIcon());  // We don't save it.
202541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_TITLE, si.getTitle());
202641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_INTENT, si.getIntentNoExtras());
202741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_WEIGHT, si.getWeight());
202841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_TIMESTAMP,
202941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                si.getLastChangedTimestamp());
203041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_FLAGS, si.getFlags());
203141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ICON_RES, si.getIconResourceId());
203241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_BITMAP_PATH, si.getBitmapPath());
203341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
203441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeTagExtra(out, ShortcutService.TAG_INTENT_EXTRAS,
203541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                si.getIntentPersistableExtras());
203641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeTagExtra(out, ShortcutService.TAG_EXTRAS, si.getExtras());
203741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
203841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.endTag(null, ShortcutService.TAG_SHORTCUT);
203941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
204041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
204141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId)
204241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
204341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
204441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final String packageName = ShortcutService.parseStringAttribute(parser,
204541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_NAME);
204641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
204741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final PackageShortcuts ret = new PackageShortcuts(userId, packageName);
204841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
204941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mDynamicShortcutCount =
205041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_DYNAMIC_COUNT);
205141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mApiCallCount =
205241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_CALL_COUNT);
205341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mLastResetTime =
205441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_LAST_RESET);
205541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
205641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int outerDepth = parser.getDepth();
205741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int type;
205841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
205941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
206041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (type != XmlPullParser.START_TAG) {
206141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                continue;
206241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
206341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final int depth = parser.getDepth();
206441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final String tag = parser.getName();
206541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            switch (tag) {
206641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_SHORTCUT:
206741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    final ShortcutInfo si = parseShortcut(parser, packageName);
206841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
206941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    // Don't use addShortcut(), we don't need to save the icon.
207041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    ret.mShortcuts.put(si.getId(), si);
207141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
207241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
207341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
207441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
207541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return ret;
207641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
207741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
207841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
207941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
208041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String id;
208141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ComponentName activityComponent;
208241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Icon icon;
208341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String title;
208441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        Intent intent;
208541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        PersistableBundle intentPersistableExtras = null;
208641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int weight;
208741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        PersistableBundle extras = null;
208841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        long lastChangedTimestamp;
208941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int flags;
209041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int iconRes;
209141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String bitmapPath;
209241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
209341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        id = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_ID);
209441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        activityComponent = ShortcutService.parseComponentNameAttribute(parser,
209541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_ACTIVITY);
209641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        title = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_TITLE);
209741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        intent = ShortcutService.parseIntentAttribute(parser, ShortcutService.ATTR_INTENT);
209841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        weight = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_WEIGHT);
209941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
210041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_TIMESTAMP);
210141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        flags = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_FLAGS);
210241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        iconRes = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_ICON_RES);
210341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        bitmapPath = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_BITMAP_PATH);
210441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
210541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int outerDepth = parser.getDepth();
210641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int type;
210741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
210841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
210941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (type != XmlPullParser.START_TAG) {
211041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                continue;
211141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
211241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final int depth = parser.getDepth();
211341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final String tag = parser.getName();
211441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (ShortcutService.DEBUG_LOAD) {
211541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
211641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                        depth, type, tag));
211741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
211841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            switch (tag) {
211941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_INTENT_EXTRAS:
212041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
212141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
212241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_EXTRAS:
212341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    extras = PersistableBundle.restoreFromXml(parser);
212441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
212541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
212641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
212741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
212841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return new ShortcutInfo(
212941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                id, packageName, activityComponent, /* icon =*/ null, title, intent,
213041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
213141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                iconRes, bitmapPath);
213241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
21336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki}
2134