ShortcutService.java revision 4554d0e5b69433ddaa698e976ee584f7f4f14948
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;
312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onukiimport android.content.pm.PackageManagerInternal;
326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ParceledListSlice;
332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onukiimport android.content.pm.ResolveInfo;
346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutInfo;
356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutServiceInternal;
366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Bitmap;
385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Bitmap.CompressFormat;
395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.BitmapFactory;
405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Canvas;
415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.RectF;
426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.graphics.drawable.Icon;
435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.net.Uri;
446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Binder;
456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Environment;
466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Handler;
475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.os.ParcelFileDescriptor;
486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.PersistableBundle;
496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Process;
506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.RemoteException;
516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.ResultReceiver;
525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.os.SELinux;
536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.ShellCommand;
546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.UserHandle;
556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.text.TextUtils;
565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.text.format.Formatter;
576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.text.format.Time;
586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.ArrayMap;
596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.ArraySet;
606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.AtomicFile;
614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onukiimport android.util.KeyValueListParser;
626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.Slog;
636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.SparseArray;
645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.util.TypedValue;
656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.Xml;
666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.annotations.GuardedBy;
686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.annotations.VisibleForTesting;
696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.os.BackgroundThread;
706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.util.FastXmlSerializer;
716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.util.Preconditions;
726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.server.LocalServices;
736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.server.SystemService;
746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport libcore.io.IoUtils;
762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onukiimport libcore.util.Objects;
776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlPullParser;
796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlPullParserException;
806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlSerializer;
816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.File;
836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileDescriptor;
846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileInputStream;
856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileNotFoundException;
866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileOutputStream;
876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.IOException;
885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport java.io.InputStream;
896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.PrintWriter;
906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.net.URISyntaxException;
916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.nio.charset.StandardCharsets;
926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.ArrayList;
936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.List;
946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.function.Predicate;
956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki/**
976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * TODO:
986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Detect when already registered instances are passed to APIs again, which might break
1005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   internal bitmap handling.
1016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
1026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
1035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   -> Need to scan all packages when a user starts too.
1045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *   -> Clear data -> remove all dynamic?  but not the pinned?
1056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
1066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Pinned per each launcher package (multiple launchers)
1076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
1085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Make save async (should we?)
1095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *
1105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Scan and remove orphan bitmaps (just in case).
1115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki *
1125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Backup & restore
1136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */
1146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukipublic class ShortcutService extends IShortcutService.Stub {
1155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static final String TAG = "ShortcutService";
1166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1174554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    static final boolean DEBUG = false; // STOPSHIP if true
11841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final boolean DEBUG_LOAD = false; // STOPSHIP if true
1196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1204362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1214362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
1224362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1234362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1244362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_DAILY_UPDATES = 10;
1254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1264362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
1284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1304362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
1314362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
1344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1364362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
1374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
1406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
1426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String FILENAME_BASE_STATE = "shortcut_service.xml";
1456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String DIRECTORY_PER_USER = "shortcut_service";
1486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
1516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static final String DIRECTORY_BITMAPS = "bitmaps";
1536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_ROOT = "root";
1553f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    static final String TAG_USER = "user";
15641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_PACKAGE = "package";
15741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_LAST_RESET_TIME = "last_reset_time";
15841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_INTENT_EXTRAS = "intent-extras";
15941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_EXTRAS = "extras";
16041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String TAG_SHORTCUT = "shortcut";
1612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    static final String TAG_LAUNCHER = "launcher";
16241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
16341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_VALUE = "value";
16441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_NAME = "name";
16541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
16641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_CALL_COUNT = "call-count";
16741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_LAST_RESET = "last-reset";
16841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ID = "id";
16941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ACTIVITY = "activity";
17041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_TITLE = "title";
17141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_INTENT = "intent";
17241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_WEIGHT = "weight";
17341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_TIMESTAMP = "timestamp";
17441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_FLAGS = "flags";
17541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_ICON_RES = "icon-res";
17641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static final String ATTR_BITMAP_PATH = "bitmap-path";
1776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
1794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    interface ConfigConstants {
1804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the throttling reset interval, in seconds. (long)
1824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
1844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max number of modifying API calls per app for every interval. (int)
1874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1884362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_DAILY_UPDATES = "max_daily_updates";
1894362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1904362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1914362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max icon dimensions in DP, for non-low-memory devices.
1924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
1944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
1954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
1964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max icon dimensions in DP, for low-memory devices.
1974362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
1984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
1994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
2004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
2014362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for the max dynamic shortcuts per app. (int)
2024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
2034362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_MAX_SHORTCUTS = "max_shortcuts";
2044362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
2054362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
20641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki         * Key name for icon compression quality, 0-100.
2074362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
2084362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_ICON_QUALITY = "icon_quality";
2094362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
2104362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        /**
2114362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
2124362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki         */
2134362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        String KEY_ICON_FORMAT = "icon_format";
2144362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
2154362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
21641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final Context mContext;
2176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Object mLock = new Object();
2196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Handler mHandler;
2216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
2246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long mRawLastResetTime;
2276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2293f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki     * User ID -> UserShortcuts
2306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
2323f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private final SparseArray<UserShortcuts> mUsers = new SparseArray<>();
2336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Max number of dynamic shortcuts that each application can have at a time.
2366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private int mMaxDynamicShortcuts;
2386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Max number of updating API calls that each application can make a day.
2416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
24241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    int mMaxDailyUpdates;
2436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Actual throttling-reset interval.  By default it's a day.
2466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long mResetInterval;
2486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    /**
2505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * Icon max width/height in pixels.
2515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     */
2525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private int mMaxIconDimension;
2535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
2544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private CompressFormat mIconPersistFormat;
2554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private int mIconPersistQuality;
2565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
2572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    private final PackageManagerInternal mPackageManagerInternal;
2582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
2596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ShortcutService(Context context) {
2606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mContext = Preconditions.checkNotNull(context);
2616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
2626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mHandler = new Handler(BackgroundThread.get().getLooper());
2632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
2646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
2656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
2676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * System service lifecycle.
2686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
2696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public static final class Lifecycle extends SystemService {
2706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ShortcutService mService;
2716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Lifecycle(Context context) {
2736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            super(context);
2746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mService = new ShortcutService(context);
2756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onStart() {
2796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            publishBinderService(Context.SHORTCUT_SERVICE, mService);
2806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onBootPhase(int phase) {
2846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mService.onBootPhase(phase);
2856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
2886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onCleanupUser(int userHandle) {
2896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mService.mLock) {
2906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mService.onCleanupUserInner(userHandle);
2916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
295f3a572b5c0cab23a435bd90414d25de84e00398eMakoto Onuki        public void onUnlockUser(int userId) {
2966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mService.mLock) {
2976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mService.onStartUserLocked(userId);
2986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onBootPhase(int phase) {
3046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
3056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.d(TAG, "onBootPhase: " + phase);
3066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        switch (phase) {
3086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            case SystemService.PHASE_LOCK_SETTINGS_READY:
3096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                initialize();
3106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                break;
3116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onStartUserLocked(int userId) {
3166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Preload
3176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getUserShortcutsLocked(userId);
3186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onCleanupUserInner(int userId) {
3226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Unload
3233f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        mUsers.delete(userId);
3246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the base state file name */
3276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private AtomicFile getBaseStateFile() {
3286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
3296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
3306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new AtomicFile(path);
3316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
3346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Init the instance. (load the state file, etc)
3356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
3366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void initialize() {
3376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
3384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            loadConfigurationLocked();
3396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            loadBaseStateLocked();
3406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3434362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    /**
3444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     * Load the configuration from Settings.
3454362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     */
3464362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    private void loadConfigurationLocked() {
3474362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        updateConfigurationLocked(injectShortcutManagerConstants());
3484362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
3494362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3504362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    /**
3514362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     * Load the configuration from Settings.
3524362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki     */
3534362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
3544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    boolean updateConfigurationLocked(String config) {
3554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        boolean result = true;
3564362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3574362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        final KeyValueListParser parser = new KeyValueListParser(',');
3584362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        try {
3594362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            parser.setString(config);
3604362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        } catch (IllegalArgumentException e) {
3614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            // Failed to parse the settings string, log this and move on
3624362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            // with defaults.
3634362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            Slog.e(TAG, "Bad shortcut manager settings", e);
3644362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            result = false;
3654362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
3664362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3674362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mResetInterval = parser.getLong(
3684362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
3694362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                * 1000L;
3704362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3714362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxDailyUpdates = (int) parser.getLong(
3724362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES);
3734362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3744362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxDynamicShortcuts = (int) parser.getLong(
3754362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP);
3764362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        final int iconDimensionDp = injectIsLowRamDevice()
3784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ? (int) parser.getLong(
3794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
3804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
3814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                : (int) parser.getLong(
3824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
3834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                    DEFAULT_MAX_ICON_DIMENSION_DP);
3844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mMaxIconDimension = injectDipToPixel(iconDimensionDp);
3864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mIconPersistFormat = CompressFormat.valueOf(
3884362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
3894362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3904362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        mIconPersistQuality = (int) parser.getLong(
3914362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                ConfigConstants.KEY_ICON_QUALITY,
3924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                DEFAULT_ICON_PERSIST_QUALITY);
3934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return result;
3954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
3964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
3976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
3984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    String injectShortcutManagerConstants() {
3994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return android.provider.Settings.Global.getString(
4004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                mContext.getContentResolver(),
4014362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
4024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
4035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
4044362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
4054362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int injectDipToPixel(int dip) {
4064362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
4074362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                mContext.getResources().getDisplayMetrics());
4086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // === Persisting ===
4116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
41341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static String parseStringAttribute(XmlPullParser parser, String attribute) {
4146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return parser.getAttributeValue(null, attribute);
4156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
41741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static int parseIntAttribute(XmlPullParser parser, String attribute) {
41841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return (int) parseLongAttribute(parser, attribute);
41941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
42041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
42141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static long parseLongAttribute(XmlPullParser parser, String attribute) {
4226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
4256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
4276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return Long.parseLong(value);
4286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NumberFormatException e) {
4296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Error parsing long " + value);
4306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
4316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
43541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
4366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return ComponentName.unflattenFromString(value);
4416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
44441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
4456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
4506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return Intent.parseUri(value, /* flags =*/ 0);
4516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (URISyntaxException e) {
4526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Error parsing intent", e);
4536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
45741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
4586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) return;
4596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, tag);
4616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.attribute(null, ATTR_VALUE, value);
4626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, tag);
4636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
46541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
4666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeTagValue(out, tag, Long.toString(value));
4676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
4702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        if (name == null) return;
4712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        writeTagValue(out, tag, name.flattenToString());
4722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
4732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
47441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
4756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws IOException, XmlPullParserException {
4766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (bundle == null) return;
4776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, tag);
4796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        bundle.saveToXml(out);
4806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, tag);
4816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
48341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, String value) throws IOException {
4846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) return;
4856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.attribute(null, name, value);
4876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
48941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
4906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, String.valueOf(value));
4916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
49341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
4946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (comp == null) return;
4956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, comp.flattenToString());
4966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
49841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
4996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (intent == null) return;
5006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, intent.toUri(/* flags =*/ 0));
5026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
5056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void saveBaseStateLocked() {
5066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = getBaseStateFile();
5076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Saving to " + file.getBaseFile());
5096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        FileOutputStream outs = null;
5126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
5136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            outs = file.startWrite();
5146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Write to XML
5166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlSerializer out = new FastXmlSerializer();
5176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.setOutput(outs, StandardCharsets.UTF_8.name());
5186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startDocument(null, true);
5196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startTag(null, TAG_ROOT);
5206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Body.
5226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
5236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Epilogue.
5256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endTag(null, TAG_ROOT);
5266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endDocument();
5276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Close.
5296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.finishWrite(outs);
5306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException e) {
5316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
5326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.failWrite(outs);
5336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void loadBaseStateLocked() {
5376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mRawLastResetTime = 0;
5386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = getBaseStateFile();
5406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Loading from " + file.getBaseFile());
5426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try (FileInputStream in = file.openRead()) {
5446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
5456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            parser.setInput(in, StandardCharsets.UTF_8.name());
5466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int type;
5486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
5496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (type != XmlPullParser.START_TAG) {
5506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
5516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int depth = parser.getDepth();
5536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // Check the root tag
5546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String tag = parser.getName();
5556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (depth == 1) {
5566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    if (!TAG_ROOT.equals(tag)) {
5576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        Slog.e(TAG, "Invalid root tag: " + tag);
5586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return;
5596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
5606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
5616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // Assume depth == 2
5636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (tag) {
5646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case TAG_LAST_RESET_TIME:
5656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
5666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
5676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    default:
5686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        Slog.e(TAG, "Invalid tag: " + tag);
5696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
5706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
5726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (FileNotFoundException e) {
5736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Use the default
5746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
5756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
5766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = 0;
5786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Adjust the last reset time.
5806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getLastResetTimeLocked();
5816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void saveUserLocked(@UserIdInt int userId) {
5846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
5856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Saving to " + path);
5876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
5896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = new AtomicFile(path);
5906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        FileOutputStream outs = null;
5916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
5926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            outs = file.startWrite();
5936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Write to XML
5956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlSerializer out = new FastXmlSerializer();
5966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.setOutput(outs, StandardCharsets.UTF_8.name());
5976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startDocument(null, true);
5986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5993f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            getUserShortcutsLocked(userId).saveToXml(out);
6006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endDocument();
6026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Close.
6046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.finishWrite(outs);
6056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
6066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
6076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.failWrite(outs);
6086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
61141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static IOException throwForInvalidTag(int depth, String tag) throws IOException {
6126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
6136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
6163f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private UserShortcuts loadUserLocked(@UserIdInt int userId) {
6176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
6186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
6196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Loading from " + path);
6206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = new AtomicFile(path);
6226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final FileInputStream in;
6246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
6256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            in = file.openRead();
6266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (FileNotFoundException e) {
6276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (DEBUG) {
6286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                Slog.i(TAG, "Not found " + path);
6296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
6306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
6316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6323f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        UserShortcuts ret = null;
6336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
6346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
6356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            parser.setInput(in, StandardCharsets.UTF_8.name());
6366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int type;
6386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
6396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (type != XmlPullParser.START_TAG) {
6406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
6416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int depth = parser.getDepth();
6436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String tag = parser.getName();
6456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (DEBUG_LOAD) {
6466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    Slog.d(TAG, String.format("depth=%d type=%d name=%s",
6476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            depth, type, tag));
6486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6493f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                if ((depth == 1) && TAG_USER.equals(tag)) {
6503f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    ret = UserShortcuts.loadFromXml(parser, userId);
6513f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    continue;
6526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                throwForInvalidTag(depth, tag);
6546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
6556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
6566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
6576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
6586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
6596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } finally {
6606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            IoUtils.closeQuietly(in);
6616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // TODO Actually make it async.
6656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void scheduleSaveBaseState() {
6666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
6676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            saveBaseStateLocked();
6686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // TODO Actually make it async.
6722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    void scheduleSaveUser(@UserIdInt int userId) {
6736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
6746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            saveUserLocked(userId);
6756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the last reset time. */
6796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long getLastResetTimeLocked() {
6806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        updateTimes();
6816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mRawLastResetTime;
6826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the next reset time. */
6856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long getNextResetTimeLocked() {
6866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        updateTimes();
6876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mRawLastResetTime + mResetInterval;
6886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6904554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    static boolean isClockValid(long time) {
6914554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT
6924554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    }
6934554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki
6946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
6956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Update the last reset time.
6966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
6976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void updateTimes() {
6986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long now = injectCurrentTimeMillis();
7006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long prevLastResetTime = mRawLastResetTime;
7026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mRawLastResetTime == 0) { // first launch.
7046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
7056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
7066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else if (now < mRawLastResetTime) {
7076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Clock rewound.
7084554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki            if (isClockValid(now)) {
7094554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki                // TODO Randomize??
7104554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki                mRawLastResetTime = now;
7114554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki            }
7126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else {
7136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Do it properly.
7146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((mRawLastResetTime + mResetInterval) <= now) {
7156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mRawLastResetTime += mResetInterval;
7166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (prevLastResetTime != mRawLastResetTime) {
7196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            scheduleSaveBaseState();
7206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user state. */
7246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
7256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
7263f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) {
7273f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        UserShortcuts userPackages = mUsers.get(userId);
7286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (userPackages == null) {
7296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackages = loadUserLocked(userId);
7306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (userPackages == null) {
7313f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                userPackages = new UserShortcuts(userId);
7326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7333f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            mUsers.put(userId, userPackages);
7346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return userPackages;
7366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user per-package state. */
7396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
7406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
7416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private PackageShortcuts getPackageShortcutsLocked(
7426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @NonNull String packageName, @UserIdInt int userId) {
7433f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final UserShortcuts userPackages = getUserShortcutsLocked(userId);
7443f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        PackageShortcuts shortcuts = userPackages.getPackages().get(packageName);
7456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcuts == null) {
7465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcuts = new PackageShortcuts(userId, packageName);
7473f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            userPackages.getPackages().put(packageName, shortcuts);
7486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return shortcuts;
7506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Caller validation ===
7536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) {
7555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.getBitmapPath() != null) {
7565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (DEBUG) {
7575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                Slog.d(TAG, "Removing " + shortcut.getBitmapPath());
7585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
7595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            new File(shortcut.getBitmapPath()).delete();
7605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setBitmapPath(null);
7625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setIconResourceId(0);
7635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES);
7645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
7685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static class FileOutputStreamWithPath extends FileOutputStream {
7695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        private final File mFile;
7705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public FileOutputStreamWithPath(File file) throws FileNotFoundException {
7725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            super(file);
7735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            mFile = file;
7745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public File getFile() {
7775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mFile;
7785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    /**
7825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * Build the cached bitmap filename for a shortcut icon.
7835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     *
7845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * The filename will be based on the ID, except certain characters will be escaped.
7855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     */
7865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
7875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
7885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            throws IOException {
7895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final File packagePath = new File(getUserBitmapFilePath(userId),
7905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                shortcut.getPackageName());
7915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!packagePath.isDirectory()) {
7925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            packagePath.mkdirs();
7935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!packagePath.isDirectory()) {
7945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                throw new IOException("Unable to create directory " + packagePath);
7955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
7965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            SELinux.restorecon(packagePath);
7975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final String baseName = String.valueOf(injectCurrentTimeMillis());
8005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (int suffix = 0;; suffix++) {
8015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
8025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final File file = new File(packagePath, filename);
8035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!file.exists()) {
8045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (DEBUG) {
8055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
8065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return new FileOutputStreamWithPath(file);
8085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
8115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) {
8135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
8145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
8155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final long token = Binder.clearCallingIdentity();
8185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        try {
8195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            // Clear icon info on the shortcut.
8205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setIconResourceId(0);
8215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setBitmapPath(null);
8225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final Icon icon = shortcut.getIcon();
8245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (icon == null) {
8255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return; // has no icon
8265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Bitmap bitmap = null;
8295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            try {
8305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                switch (icon.getType()) {
8315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_RESOURCE: {
8325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        injectValidateIconResPackage(shortcut, icon);
8335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.setIconResourceId(icon.getResId());
8355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
8365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        return;
8375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_BITMAP: {
8395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        bitmap = icon.getBitmap();
8405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        break;
8415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_URI: {
8435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        final Uri uri = ContentProvider.maybeAddUserId(icon.getUri(), userId);
8445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
8465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            bitmap = BitmapFactory.decodeStream(is);
8485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        } catch (IOException e) {
8505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            Slog.e(TAG, "Unable to load icon from " + uri);
8515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            return;
8525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        }
8535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        break;
8545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    default:
8565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        // This shouldn't happen because we've already validated the icon, but
8575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        // just in case.
8585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        throw ShortcutInfo.getInvalidIconException();
8595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (bitmap == null) {
8615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.e(TAG, "Null bitmap detected");
8625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return;
8635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                // Shrink and write to the file.
8655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                File path = null;
8665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                try {
8675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut);
8685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    try {
8695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        path = out.getFile();
8705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shrinkBitmap(bitmap, mMaxIconDimension)
8725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                                .compress(mIconPersistFormat, mIconPersistQuality, out);
8735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.setBitmapPath(out.getFile().getAbsolutePath());
8755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
8765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    } finally {
8775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        IoUtils.closeQuietly(out);
8785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                } catch (IOException|RuntimeException e) {
8805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    // STOPSHIP Change wtf to e
8815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
8825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (path != null && path.exists()) {
8835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        path.delete();
8845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } finally {
8875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (bitmap != null) {
8885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    bitmap.recycle();
8895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                // Once saved, we won't use the original icon information, so null it out.
8915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                shortcut.clearIcon();
8925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        } finally {
8945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Binder.restoreCallingIdentity(token);
8955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
8975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // Unfortunately we can't do this check in unit tests because we fake creator package names,
8995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // so override in unit tests.
9005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // TODO CTS this case.
9015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
9025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!shortcut.getPackageName().equals(icon.getResPackage())) {
9035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            throw new IllegalArgumentException(
9045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    "Icon resource must reside in shortcut owner package");
9055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
9075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
9095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
9105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        // Original width/height.
9115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int ow = in.getWidth();
9125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int oh = in.getHeight();
9135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if ((ow <= maxSize) && (oh <= maxSize)) {
9145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (DEBUG) {
9155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
9165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
9175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return in;
9185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int longerDimension = Math.max(ow, oh);
9205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        // New width and height.
9225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int nw = ow * maxSize / longerDimension;
9235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int nh = oh * maxSize / longerDimension;
9245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (DEBUG) {
9255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
9265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    ow, oh, nw, nh));
9275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
9305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final Canvas c = new Canvas(scaledBitmap);
9315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final RectF dst = new RectF(0, 0, nw, nh);
9335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
9355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        in.recycle();
9375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return scaledBitmap;
9395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
9405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // === Caller validation ===
9425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerSystem() {
9446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
9466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerShell() {
9496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
9516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceSystemOrShell() {
9546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerSystem() || isCallerShell(),
9556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                "Caller must be system or shell");
9566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceShell() {
9596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerShell(), "Caller must be shell");
9606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
9636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(packageName, "packageName");
9646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (isCallerSystem()) {
9666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // no check
9676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Otherwise, make sure the arguments are valid.
9726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (UserHandle.getUserId(callingUid) != userId) {
9736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new SecurityException("Invalid user-ID");
9746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
9766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // Caller is valid.
9776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new SecurityException("Caller UID= doesn't own " + packageName);
9796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Test overrides it.
9825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
9836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
9846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Is MATCH_UNINSTALLED_PACKAGES correct to get SD card app info?
9866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mContext.getPackageManager().getPackageUidAsUser(packageName,
9886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
9895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            | PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
9906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NameNotFoundException e) {
9916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return -1;
9926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
9966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
9976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
9986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void enforceMaxDynamicShortcuts(int numShortcuts) {
9996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (numShortcuts > mMaxDynamicShortcuts) {
10006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
10016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
10056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Sends a notification to LauncherApps
10066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Write to file
10076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
10086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) {
10096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        notifyListeners(packageName, userId);
10106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveUser(userId);
10116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
10146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutChangeListener> copy;
10156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> shortcuts = new ArrayList<>();
10166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
10176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy = new ArrayList<>(mListeners);
10186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId)
10206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    .findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
10216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        for (int i = copy.size() - 1; i >= 0; i--) {
10236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy.get(i).onShortcutChanged(packageName, shortcuts, userId);
10246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
10286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Clean up / validate an incoming shortcut.
10296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure all mandatory fields are set.
10306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure the intent's extras are persistable, and them to set
10316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     *  {@link ShortcutInfo#mIntentPersistableExtras}.  Also clear its extras.
10326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Clear flags.
10335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     *
10345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * TODO Detailed unit tests
10356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
10365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
10376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkNotNull(shortcut, "Null shortcut detected");
10386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcut.getActivityComponent() != null) {
10396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkState(
10406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    shortcut.getPackageName().equals(
10416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            shortcut.getActivityComponent().getPackageName()),
10426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    "Activity package name mismatch");
10436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!forUpdate) {
10465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.enforceMandatoryFields();
10475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.getIcon() != null) {
10495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            ShortcutInfo.validateIcon(shortcut.getIcon());
10505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validateForXml(shortcut.getId());
10535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validateForXml(shortcut.getTitle());
10545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validatePersistableBundleForXml(shortcut.getIntentPersistableExtras());
10555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validatePersistableBundleForXml(shortcut.getExtras());
10566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        shortcut.setFlags(0);
10585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
10605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those
10615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // characters.
10625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
10635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static void validatePersistableBundleForXml(PersistableBundle b) {
10645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (b == null || b.size() == 0) {
10655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
10666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (String key : b.keySet()) {
10685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            validateForXml(key);
10695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final Object value = b.get(key);
10705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (value == null) {
10715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                continue;
10725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof String) {
10735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                validateForXml((String) value);
10745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof String[]) {
10755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                for (String v : (String[]) value) {
10765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    validateForXml(v);
10775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
10785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof PersistableBundle) {
10795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                validatePersistableBundleForXml((PersistableBundle) value);
10805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
10815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static void validateForXml(String s) {
10855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (TextUtils.isEmpty(s)) {
10865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
10875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (int i = s.length() - 1; i >= 0; i--) {
10895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!isAllowedInXml(s.charAt(i))) {
10905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                throw new IllegalArgumentException("Unsupported character detected in: " + s);
10915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
10925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static boolean isAllowedInXml(char c) {
10965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
10976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === APIs ===
11006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
11036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
11076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int size = newShortcuts.size();
11086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
11136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
11146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
11156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            enforceMaxDynamicShortcuts(size);
11176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcuts.
11196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
11205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
11216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // First, remove all un-pinned; dynamic shortcuts
11245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            ps.deleteAllDynamicShortcuts(this);
11256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Then, add/update all.  We need to make sure to take over "pinned" flag.
11276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
11286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo newShortcut = newShortcuts.get(i);
11296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
11306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                ps.updateShortcutWithCapping(this, newShortcut);
11316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
11396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
11435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int size = newShortcuts.size();
11446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            // Throttling.
11495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!ps.tryApiCall(this)) {
11505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return false;
11516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            for (int i = 0; i < size; i++) {
11545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo source = newShortcuts.get(i);
11555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
11565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo target = ps.findShortcutById(source.getId());
11585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (target != null) {
11595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    final boolean replacingIcon = (source.getIcon() != null);
11605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (replacingIcon) {
11615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        removeIcon(userId, target);
11625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
11635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    target.copyNonNullFieldsFrom(source);
11655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (replacingIcon) {
11675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        saveIconAndFixUpShortcut(userId, target);
11685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
11695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
11705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
11716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
11796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
11866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
11876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
11886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcut.
11915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
11926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Add it.
11946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
11956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            ps.updateShortcutWithCapping(this, newShortcut);
11966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
12006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteDynamicShortcut(String packageName, String shortcutId,
12046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
12076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this, shortcutId);
12106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
12126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
12166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(this);
12206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
12226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
12266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
12306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
12316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isDynamic);
12326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
12376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
12416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
12426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isPinned);
12436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
12476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
12486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutInfo> ret = new ArrayList<>();
12506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
12526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new ParceledListSlice<>(ret);
12546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
12586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws RemoteException {
12596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mMaxDynamicShortcuts;
12626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
12666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return mMaxDailyUpdates
12706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
12716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
12766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getNextResetTimeLocked();
12806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @Override
12845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
12855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        synchronized (mLock) {
12865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mMaxIconDimension;
12875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
12885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
12895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
12906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
12916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Reset all throttling, for developer options and command line.  Only system/shell can call it.
12926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
12936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void resetThrottling() {
12956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceSystemOrShell();
12966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12974554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        resetThrottlingInner(getCallingUserId());
12986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13004554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    void resetThrottlingInner(@UserIdInt int userId) {
13016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
13024554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki            getUserShortcutsLocked(userId).resetThrottling();
13036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13044554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        scheduleSaveUser(userId);
13055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        Slog.i(TAG, "ShortcutManager: throttling counter reset");
13066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
13076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13082d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    // We override this method in unit tests to do a simpler check.
13092d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
13102d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        return hasShortcutHostPermissionInner(callingPackage, userId);
13112d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
13122d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13132d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    // This method is extracted so we can directly call this method from unit tests,
13142d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    // even when hasShortcutPermission() is overridden.
13152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    @VisibleForTesting
13162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
13172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        synchronized (mLock) {
13182d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            long start = 0;
13192d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (DEBUG) {
13202d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                start = System.currentTimeMillis();
13212d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13232d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            final UserShortcuts user = getUserShortcutsLocked(userId);
13242d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13252d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
13262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13272d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            // Default launcher from package manager.
13282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            final ComponentName defaultLauncher = injectPackageManagerInternal()
13292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    .getHomeActivitiesAsUser(allHomeCandidates, userId);
13302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            ComponentName detected;
13322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (defaultLauncher != null) {
13332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                detected = defaultLauncher;
13342d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                if (DEBUG) {
13352d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    Slog.v(TAG, "Default launcher from PM: " + detected);
13362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
13372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            } else {
13382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                detected = user.getLauncherComponent();
13392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // TODO: Make sure it's still enabled.
13412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                if (DEBUG) {
13422d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    Slog.v(TAG, "Cached launcher: " + detected);
13432d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
13442d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13452d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13462d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (detected == null) {
13472d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // If we reach here, that means it's the first check since the user was created,
13482d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // and there's already multiple launchers and there's no default set.
13492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // Find the system one with the highest priority.
13502d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // (We need to check the priority too because of FallbackHome in Settings.)
13512d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // If there's no system launcher yet, then no one can access shortcuts, until
13522d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // the user explicitly
13532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                final int size = allHomeCandidates.size();
13542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                int lastPriority = Integer.MIN_VALUE;
13562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                for (int i = 0; i < size; i++) {
13572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    final ResolveInfo ri = allHomeCandidates.get(i);
13582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    if (!ri.activityInfo.applicationInfo.isSystemApp()) {
13592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        continue;
13602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    }
13612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    if (DEBUG) {
13622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
13632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                                ri.activityInfo.getComponentName(), ri.priority));
13642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    }
13652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    if (ri.priority < lastPriority) {
13662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        continue;
13672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    }
13682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    detected = ri.activityInfo.getComponentName();
13692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    lastPriority = ri.priority;
13702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
13712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (DEBUG) {
13732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                long end = System.currentTimeMillis();
13742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start));
13752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (detected != null) {
13772d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                if (DEBUG) {
13782d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    Slog.v(TAG, "Detected launcher: " + detected);
13792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
13802d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                user.setLauncherComponent(this, detected);
13812d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                return detected.getPackageName().equals(callingPackage);
13822d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            } else {
13832d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // Default launcher not found.
13842d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                return false;
13852d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13862d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
13872d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
13882d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
13906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Entry point from {@link LauncherApps}.
13916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
13926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class LocalService extends ShortcutServiceInternal {
13936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcuts(
13956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage, long changedSince,
13966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable String packageName, @Nullable ComponentName componentName,
13976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int queryFlags, int userId) {
13986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>();
13996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final int cloneFlag =
14006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
14016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
14026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
14036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (packageName != null) {
14066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags,
14076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            userId, ret, cloneFlag);
14086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                } else {
14096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    final ArrayMap<String, PackageShortcuts> packages =
14103f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                            getUserShortcutsLocked(userId).getPackages();
14115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    for (int i = packages.size() - 1; i >= 0; i--) {
14126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        getShortcutsInnerLocked(
14136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                packages.keyAt(i),
14146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                changedSince, componentName, queryFlags, userId, ret, cloneFlag);
14156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
14166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
14176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
14196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince,
14226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable ComponentName componentName, int queryFlags,
14236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
14246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId).findAll(ret,
14256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    (ShortcutInfo si) -> {
14266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (si.getLastChangedTimestamp() < changedSince) {
14276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
14286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
14296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (componentName != null
14306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && !componentName.equals(si.getActivityComponent())) {
14316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
14326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
14336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchDynamic =
14346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
14356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && si.isDynamic();
14366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchPinned =
14376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
14386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                        && si.isPinned();
14396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return matchDynamic || matchPinned;
14406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }, cloneFlag);
14416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcutInfo(
14456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage,
14466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String packageName, @Nullable List<String> ids, int userId) {
14476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
14486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
14496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
14516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArraySet<String> idSet = new ArraySet<>(ids);
14526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).findAll(ret,
14546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        (ShortcutInfo si) -> idSet.contains(si.getId()),
14556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
14566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
14586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
14626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull List<String> shortcutIds, int userId) {
14636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
14646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
14656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkNotNull(shortcutIds, "shortcutIds");
14666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                getPackageShortcutsLocked(packageName, userId).replacePinned(
14695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        ShortcutService.this, callingPackage, shortcutIds);
14706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackageChanged(packageName, userId);
14726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Intent createShortcutIntent(@NonNull String callingPackage,
147643204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki                @NonNull String packageName, @NonNull String shortcutId, int userId) {
14776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
147843204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
147943204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki            Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
14806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo fullShortcut =
148343204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki                        getPackageShortcutsLocked(packageName, userId)
14845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        .findShortcutById(shortcutId);
14855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return fullShortcut == null ? null : fullShortcut.getIntent();
14866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void addListener(@NonNull ShortcutChangeListener listener) {
14916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mListeners.add(Preconditions.checkNotNull(listener));
14936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        @Override
14975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public int getShortcutIconResId(@NonNull String callingPackage,
14985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                @NonNull ShortcutInfo shortcut, int userId) {
14995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Preconditions.checkNotNull(shortcut, "shortcut");
15005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            synchronized (mLock) {
15025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
15035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
15045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return (shortcutInfo != null && shortcutInfo.hasIconResource())
15055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        ? shortcutInfo.getIconResourceId() : 0;
15065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
15075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
15085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        @Override
15105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
151134d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                @NonNull ShortcutInfo shortcutIn, int userId) {
151234d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki            Preconditions.checkNotNull(shortcutIn, "shortcut");
15135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            synchronized (mLock) {
15155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
151634d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
15175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
15185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return null;
15195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
15205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                try {
152134d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                    if (shortcutInfo.getBitmapPath() == null) {
152234d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
152334d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        return null;
152434d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                    }
15255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return ParcelFileDescriptor.open(
15265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            new File(shortcutInfo.getBitmapPath()),
15275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            ParcelFileDescriptor.MODE_READ_ONLY);
15285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                } catch (FileNotFoundException e) {
15295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath());
15305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return null;
15315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
15325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
15335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
15342d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
15352d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        @Override
15362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
15372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId);
15382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
15396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Dump ===
15426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
15446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
15456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
15466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                != PackageManager.PERMISSION_GRANTED) {
15476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Permission Denial: can't dump UserManager from from pid="
15486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + Binder.getCallingPid()
15496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + ", uid=" + Binder.getCallingUid()
15506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + " without permission "
15516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + android.Manifest.permission.DUMP);
15526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
15536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        dumpInner(pw);
15556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
15586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void dumpInner(PrintWriter pw) {
15596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
15606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long now = injectCurrentTimeMillis();
15616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("Now: [");
15626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(now);
15636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(now));
15655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Raw last reset: [");
15676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(mRawLastResetTime);
15686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(mRawLastResetTime));
15706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long last = getLastResetTimeLocked();
15726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Last reset: [");
15736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(last);
15746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(last));
15766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final long next = getNextResetTimeLocked();
15786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Next reset: [");
15796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(next);
15806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(next));
15826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
15836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Max icon dim: ");
15855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mMaxIconDimension);
15865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Icon format: ");
15875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mIconPersistFormat);
15885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Icon quality: ");
15895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mIconPersistQuality);
15905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.println();
15915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15933f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            for (int i = 0; i < mUsers.size(); i++) {
15943f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                pw.println();
15953f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                mUsers.valueAt(i).dump(this, pw, "  ");
15966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
160041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static String formatTime(long time) {
16016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Time tobj = new Time();
16026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        tobj.set(time);
16036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return tobj.format("%Y-%m-%d %H:%M:%S");
16046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Shell support ===
16076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
16096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
16106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String[] args, ResultReceiver resultReceiver) throws RemoteException {
16116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceShell();
16136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
16156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    static class CommandException extends Exception {
16182d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        public CommandException(String message) {
16192d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            super(message);
16202d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
16212d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
16222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
16236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
16246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Handle "adb shell cmd".
16256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
16266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class MyShellCommand extends ShellCommand {
16272d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
16282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private int mUserId = UserHandle.USER_SYSTEM;
16292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
16302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void parseOptions(boolean takeUser)
16312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                throws CommandException {
16322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            String opt;
16332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            while ((opt = getNextOption()) != null) {
16342d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                switch (opt) {
16352d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "--user":
16362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        if (takeUser) {
16372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                            mUserId = UserHandle.parseUserArg(getNextArgRequired());
16382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                            break;
16392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        }
16402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        // fallthrough
16412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    default:
16422d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        throw new CommandException("Unknown option: " + opt);
16432d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
16442d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
16452d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
16462d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
16476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
16486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public int onCommand(String cmd) {
16496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (cmd == null) {
16506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return handleDefaultCommands(cmd);
16516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
16526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
16532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            try {
16542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                switch (cmd) {
16552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "reset-package-throttling":
16562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleResetPackageThrottling();
16572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "reset-throttling":
16592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleResetThrottling();
16602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "override-config":
16622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleOverrideConfig();
16632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "reset-config":
16652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleResetConfig();
16662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "clear-default-launcher":
16682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleClearDefaultLauncher();
16692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "get-default-launcher":
16712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleGetDefaultLauncher();
16722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "refresh-default-launcher":
16742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleRefreshDefaultLauncher();
16752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    default:
16772d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        return handleDefaultCommands(cmd);
16782d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
16792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            } catch (CommandException e) {
16802d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                pw.println("Error: " + e.getMessage());
16812d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                return 1;
16824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
16832d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("Success");
16842d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            return 0;
16856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
16866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
16886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onHelp() {
16896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
16906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Usage: cmd shortcut COMMAND [options ...]");
16916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
16936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for a package");
16946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-throttling");
16966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for all packages and users");
16976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("cmd shortcut override-config CONFIG");
16994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("    Override the configuration for testing (will last until reboot)");
17004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println();
17014362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("cmd shortcut reset-config");
17024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("    Reset the configuration set with \"update-config\"");
17034362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println();
17042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("cmd shortcut clear-default-launcher [--user USER_ID]");
17052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("    Clear the cached default launcher");
17062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println();
17072d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
17082d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("    Show the cached default launcher");
17092d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println();
17102d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]");
17112d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("    Refresh the cached default launcher");
17122d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println();
17136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
17146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17154554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        private int handleResetThrottling() throws CommandException {
17164554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki            parseOptions(/* takeUser =*/ true);
17174554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki
17184554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki            resetThrottlingInner(mUserId);
17196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
17206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
17216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleResetPackageThrottling() throws CommandException {
17232d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            parseOptions(/* takeUser =*/ true);
17246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final String packageName = getNextArgRequired();
17266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
17282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine();
17292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                saveUserLocked(mUserId);
17306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
17316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
17324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
17332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleOverrideConfig() throws CommandException {
17344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            final String config = getNextArgRequired();
17354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
17364362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            synchronized (mLock) {
17374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                if (!updateConfigurationLocked(config)) {
17382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    throw new CommandException("override-config failed.  See logcat for details.");
17394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                }
17404362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
17414362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
17424362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
17432d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleResetConfig() {
17444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            synchronized (mLock) {
17454362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                loadConfigurationLocked();
17464362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
17472d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17482d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void clearLauncher() {
17502d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            synchronized (mLock) {
17512d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                getUserShortcutsLocked(mUserId).setLauncherComponent(
17522d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        ShortcutService.this, null);
17532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
17542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void showLauncher() {
17572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            synchronized (mLock) {
17582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // This ensures to set the cached launcher.  Package name doesn't matter.
17592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                hasShortcutHostPermissionInner("-", mUserId);
17602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                getOutPrintWriter().println("Launcher: "
17622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        + getUserShortcutsLocked(mUserId).getLauncherComponent());
17632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
17642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleClearDefaultLauncher() throws CommandException {
17672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            parseOptions(/* takeUser =*/ true);
17682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            clearLauncher();
17702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleGetDefaultLauncher() throws CommandException {
17732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            parseOptions(/* takeUser =*/ true);
17742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            showLauncher();
17762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17772d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17782d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleRefreshDefaultLauncher() throws CommandException {
17792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            parseOptions(/* takeUser =*/ true);
17802d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17812d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            clearLauncher();
17822d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            showLauncher();
17834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
17846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Unit test support ===
17876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
17896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long injectCurrentTimeMillis() {
17906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return System.currentTimeMillis();
17916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
17946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    int injectBinderCallingUid() {
17956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return getCallingUid();
17966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17984554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    final int getCallingUserId() {
17994554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        return UserHandle.getUserId(injectBinderCallingUid());
18004554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    }
18014554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki
18026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectSystemDataPath() {
18036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return Environment.getDataSystemDirectory();
18046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectUserDataPath(@UserIdInt int userId) {
18075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
18085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
18095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
18104362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
18115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    boolean injectIsLowRamDevice() {
18125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return ActivityManager.isLowRamDeviceStatic();
18135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
18145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
18152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    PackageManagerInternal injectPackageManagerInternal() {
18162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        return mPackageManagerInternal;
18172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
18182d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
18195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    File getUserBitmapFilePath(@UserIdInt int userId) {
18205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
18216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
18243f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    SparseArray<UserShortcuts> getShortcutsForTest() {
18253f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return mUsers;
18266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
18294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxDynamicShortcutsForTest() {
18304362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxDynamicShortcuts;
18314362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
18324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
18334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
18344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxDailyUpdatesForTest() {
18354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxDailyUpdates;
18364362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
18374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
18384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
18394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    long getResetIntervalForTest() {
18404362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mResetInterval;
18416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
18444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxIconDimensionForTest() {
18454362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxIconDimension;
18466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
18494362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    CompressFormat getIconPersistFormatForTest() {
18504362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mIconPersistFormat;
18515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
18525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
18535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
18544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getIconPersistQualityForTest() {
18554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mIconPersistQuality;
18566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
185741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
185841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @VisibleForTesting
185941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
186041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        synchronized (mLock) {
186141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return getPackageShortcutsLocked(packageName, userId).findShortcutById(shortcutId);
186241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
186341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
186441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki}
186541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
18662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki/**
18672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki * Per-user information.
18682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki */
18693f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onukiclass UserShortcuts {
18703f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private static final String TAG = ShortcutService.TAG;
18713f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18723f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    @UserIdInt
18733f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    final int mUserId;
18743f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18753f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>();
18763f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18772d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    private ComponentName mLauncherComponent;
18782d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
18793f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public UserShortcuts(int userId) {
18803f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        mUserId = userId;
18813f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
18823f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18833f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public ArrayMap<String, PackageShortcuts> getPackages() {
18843f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return mPackages;
18853f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
18863f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18873f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
18883f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        out.startTag(null, ShortcutService.TAG_USER);
18893f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18902d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        ShortcutService.writeTagValue(out, ShortcutService.TAG_LAUNCHER,
18912d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                mLauncherComponent);
18923f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18932d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        for (int i = 0; i < mPackages.size(); i++) {
18942d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            mPackages.valueAt(i).saveToXml(out);
18953f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
18963f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18973f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        out.endTag(null, ShortcutService.TAG_USER);
18983f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
18993f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
19003f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public static UserShortcuts loadFromXml(XmlPullParser parser, int userId)
19013f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            throws IOException, XmlPullParserException {
19023f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final UserShortcuts ret = new UserShortcuts(userId);
19033f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
19043f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final int outerDepth = parser.getDepth();
19053f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        int type;
19063f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
19073f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
19083f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            if (type != XmlPullParser.START_TAG) {
19093f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                continue;
19103f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            }
19113f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            final int depth = parser.getDepth();
19123f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            final String tag = parser.getName();
19133f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            switch (tag) {
19142d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                case ShortcutService.TAG_LAUNCHER:
19152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
19162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                            parser, ShortcutService.ATTR_VALUE);
19172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    continue;
19183f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                case ShortcutService.TAG_PACKAGE:
19193f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
19203f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
19213f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    // Don't use addShortcut(), we don't need to save the icon.
19223f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    ret.getPackages().put(shortcuts.mPackageName, shortcuts);
19233f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    continue;
19243f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            }
19253f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
19263f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
19273f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return ret;
19283f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
19293f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
19302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    public ComponentName getLauncherComponent() {
19312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        return mLauncherComponent;
19322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
19332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
19342d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) {
19352d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        if (Objects.equal(mLauncherComponent, launcherComponent)) {
19362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            return;
19372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
19382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        mLauncherComponent = launcherComponent;
19392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        s.scheduleSaveUser(mUserId);
19402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
19412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
19424554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    public void resetThrottling() {
19434554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        for (int i = mPackages.size() - 1; i >= 0; i--) {
19444554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki            mPackages.valueAt(i).resetThrottling();
19454554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        }
19464554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    }
19474554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki
19483f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
19492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print(prefix);
19503f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.print("User: ");
19513f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.print(mUserId);
19523f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.println();
19533f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
19542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print(prefix);
19552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print("  ");
19562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print("Default launcher: ");
19572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print(mLauncherComponent);
19582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.println();
19592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
19603f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        for (int i = 0; i < mPackages.size(); i++) {
19613f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            mPackages.valueAt(i).dump(s, pw, prefix + "  ");
19623f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
19633f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
19643f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki}
19653f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
196641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki/**
196741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * All the information relevant to shortcuts from a single package (per-user).
196841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */
196941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onukiclass PackageShortcuts {
197041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static final String TAG = ShortcutService.TAG;
197141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
197241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @UserIdInt
197341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final int mUserId;
197441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
197541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @NonNull
197641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final String mPackageName;
197741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
197841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
197941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * All the shortcuts from the package, keyed on IDs.
198041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
198141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
198241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
198341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
198441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * # of dynamic shortcuts.
198541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
198641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private int mDynamicShortcutCount = 0;
198741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
198841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
198941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * # of times the package has called rate-limited APIs.
199041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
199141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private int mApiCallCount;
199241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
199341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
199441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * When {@link #mApiCallCount} was reset last time.
199541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
199641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private long mLastResetTime;
199741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
199841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    PackageShortcuts(int userId, String packageName) {
199941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mUserId = userId;
200041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mPackageName = packageName;
200141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
200241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
200341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @Nullable
200441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public ShortcutInfo findShortcutById(String id) {
200541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return mShortcuts.get(id);
200641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
200741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
200841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
200941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @NonNull String id) {
201041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo shortcut = mShortcuts.remove(id);
201141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (shortcut != null) {
201241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            s.removeIcon(mUserId, shortcut);
201341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
201441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
201541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return shortcut;
201641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
201741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
201841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
201941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        deleteShortcut(s, newShortcut.getId());
202041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        s.saveIconAndFixUpShortcut(mUserId, newShortcut);
202141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mShortcuts.put(newShortcut.getId(), newShortcut);
202241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
202341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
202441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
202541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Add a shortcut, or update one with the same ID, with taking over existing flags.
202641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     *
202741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * It checks the max number of dynamic shortcuts.
202841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
202941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void updateShortcutWithCapping(@NonNull ShortcutService s,
203041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @NonNull ShortcutInfo newShortcut) {
203141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
203241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
203341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int oldFlags = 0;
203441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int newDynamicCount = mDynamicShortcutCount;
203541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
203641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut != null) {
203741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            oldFlags = oldShortcut.getFlags();
203841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (oldShortcut.isDynamic()) {
203941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                newDynamicCount--;
204041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
204141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
204241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (newShortcut.isDynamic()) {
204341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            newDynamicCount++;
204441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
204541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Make sure there's still room.
204641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        s.enforceMaxDynamicShortcuts(newDynamicCount);
204741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
204841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Okay, make it dynamic and add.
204941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        newShortcut.addFlags(oldFlags);
205041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
205141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        addShortcut(s, newShortcut);
205241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mDynamicShortcutCount = newDynamicCount;
205341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
205441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
205541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
205641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Remove all shortcuts that aren't pinned nor dynamic.
205741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
205841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private void removeOrphans(@NonNull ShortcutService s) {
205941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ArrayList<String> removeList = null; // Lazily initialize.
206041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
206141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
206241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = mShortcuts.valueAt(i);
206341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
206441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (si.isPinned() || si.isDynamic()) continue;
206541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
206641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (removeList == null) {
206741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                removeList = new ArrayList<>();
206841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
206941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            removeList.add(si.getId());
207041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
207141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (removeList != null) {
207241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            for (int i = removeList.size() - 1 ; i >= 0; i--) {
207341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                deleteShortcut(s, removeList.get(i));
207441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
207541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
207641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
207741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
207841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
207941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
208041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
208141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
208241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        removeOrphans(s);
208341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mDynamicShortcutCount = 0;
208441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
208541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
208641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
208741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
208841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
208941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut == null) {
209041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return;
209141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
209241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut.isDynamic()) {
209341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mDynamicShortcutCount--;
209441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
209541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut.isPinned()) {
209641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
209741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        } else {
209841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            deleteShortcut(s, shortcutId);
209941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
210041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
210141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
210241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void replacePinned(@NonNull ShortcutService s, String launcherPackage,
210341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            List<String> shortcutIds) {
210441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
210541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // TODO Should be per launcherPackage.
210641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
210741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // First, un-pin all shortcuts
210841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
210941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
211041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
211141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
211241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Then pin ALL
211341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = shortcutIds.size() - 1; i >= 0; i--) {
211441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i));
211541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (shortcut != null) {
211641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
211741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
211841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
211941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
212041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        removeOrphans(s);
212141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
212241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
212341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
212441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Number of calls that the caller has made, since the last reset.
212541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
212641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public int getApiCallCount(@NonNull ShortcutService s) {
212741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final long last = s.getLastResetTimeLocked();
212841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
212941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final long now = s.injectCurrentTimeMillis();
21304554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        if (ShortcutService.isClockValid(now) && mLastResetTime > now) {
213141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            // Clock rewound. // TODO Test it
213241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mLastResetTime = now;
213341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
213441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
213541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // If not reset yet, then reset.
213641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (mLastResetTime < last) {
213741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mApiCallCount = 0;
213841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mLastResetTime = last;
213941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
214041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return mApiCallCount;
214141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
214241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
214341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
214441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
214541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * and return true.  Otherwise just return false.
214641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
214741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public boolean tryApiCall(@NonNull ShortcutService s) {
214841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
214941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return false;
215041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
215141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mApiCallCount++;
215241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return true;
215341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
215441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
215541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void resetRateLimitingForCommandLine() {
215641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mApiCallCount = 0;
215741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mLastResetTime = 0;
215841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
215941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
216041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
216141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Find all shortcuts that match {@code query}.
216241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
216341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void findAll(@NonNull List<ShortcutInfo> result,
216441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
216541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = 0; i < mShortcuts.size(); i++) {
216641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = mShortcuts.valueAt(i);
216741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (query == null || query.test(si)) {
216841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                result.add(si.clone(cloneFlag));
216941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
217041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
217141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
217241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
21734554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    public void resetThrottling() {
21744554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki        mApiCallCount = 0;
21754554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki    }
21764554d0e5b69433ddaa698e976ee584f7f4f14948Makoto Onuki
217741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
217841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
217941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Package: ");
218041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(mPackageName);
218141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
218241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
218341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
218441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
218541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Calls: ");
218641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(getApiCallCount(s));
218741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
218841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
218941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // This should be after getApiCallCount(), which may update it.
219041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
219141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
219241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Last reset: [");
219341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(mLastResetTime);
219441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("] ");
219541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(s.formatTime(mLastResetTime));
219641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
219741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
219841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println("      Shortcuts:");
219941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        long totalBitmapSize = 0;
220041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
220141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int size = shortcuts.size();
220241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = 0; i < size; i++) {
220341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = shortcuts.valueAt(i);
220441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            pw.print("        ");
220541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            pw.println(si.toInsecureString());
220634d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki            if (si.getBitmapPath() != null) {
220741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                final long len = new File(si.getBitmapPath()).length();
220841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.print("          ");
220941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.print("bitmap size=");
221041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.println(len);
221141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
221241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                totalBitmapSize += len;
221341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
221441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
221541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
221641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
221741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Total bitmap size: ");
221841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(totalBitmapSize);
221941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(" (");
222041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
222141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println(")");
222241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
222341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
222441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
222541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.startTag(null, ShortcutService.TAG_PACKAGE);
222641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
222741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_NAME, mPackageName);
222841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
222941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_CALL_COUNT, mApiCallCount);
223041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_LAST_RESET, mLastResetTime);
223141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
223241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int size = mShortcuts.size();
223341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int j = 0; j < size; j++) {
223441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            saveShortcut(out, mShortcuts.valueAt(j));
223541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
223641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
223741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.endTag(null, ShortcutService.TAG_PACKAGE);
223841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
223941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
224041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
224141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
224241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.startTag(null, ShortcutService.TAG_SHORTCUT);
224341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ID, si.getId());
224441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // writeAttr(out, "package", si.getPackageName()); // not needed
224541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ACTIVITY, si.getActivityComponent());
224641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // writeAttr(out, "icon", si.getIcon());  // We don't save it.
224741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_TITLE, si.getTitle());
224841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_INTENT, si.getIntentNoExtras());
224941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_WEIGHT, si.getWeight());
225041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_TIMESTAMP,
225141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                si.getLastChangedTimestamp());
225241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_FLAGS, si.getFlags());
225341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ICON_RES, si.getIconResourceId());
225441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_BITMAP_PATH, si.getBitmapPath());
225541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
225641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeTagExtra(out, ShortcutService.TAG_INTENT_EXTRAS,
225741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                si.getIntentPersistableExtras());
225841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeTagExtra(out, ShortcutService.TAG_EXTRAS, si.getExtras());
225941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
226041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.endTag(null, ShortcutService.TAG_SHORTCUT);
226141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
226241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
226341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId)
226441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
226541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
226641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final String packageName = ShortcutService.parseStringAttribute(parser,
226741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_NAME);
226841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
226941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final PackageShortcuts ret = new PackageShortcuts(userId, packageName);
227041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
227141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mDynamicShortcutCount =
227241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_DYNAMIC_COUNT);
227341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mApiCallCount =
227441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_CALL_COUNT);
227541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mLastResetTime =
227641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_LAST_RESET);
227741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
227841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int outerDepth = parser.getDepth();
227941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int type;
228041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
228141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
228241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (type != XmlPullParser.START_TAG) {
228341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                continue;
228441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
228541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final int depth = parser.getDepth();
228641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final String tag = parser.getName();
228741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            switch (tag) {
228841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_SHORTCUT:
228941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    final ShortcutInfo si = parseShortcut(parser, packageName);
229041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
229141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    // Don't use addShortcut(), we don't need to save the icon.
229241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    ret.mShortcuts.put(si.getId(), si);
229341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
229441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
229541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
229641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
229741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return ret;
229841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
229941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
230041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
230141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
230241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String id;
230341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ComponentName activityComponent;
230441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Icon icon;
230541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String title;
230641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        Intent intent;
230741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        PersistableBundle intentPersistableExtras = null;
230841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int weight;
230941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        PersistableBundle extras = null;
231041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        long lastChangedTimestamp;
231141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int flags;
231241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int iconRes;
231341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String bitmapPath;
231441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
231541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        id = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_ID);
231641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        activityComponent = ShortcutService.parseComponentNameAttribute(parser,
231741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_ACTIVITY);
231841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        title = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_TITLE);
231941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        intent = ShortcutService.parseIntentAttribute(parser, ShortcutService.ATTR_INTENT);
232041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        weight = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_WEIGHT);
232141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
232241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_TIMESTAMP);
232341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        flags = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_FLAGS);
232441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        iconRes = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_ICON_RES);
232541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        bitmapPath = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_BITMAP_PATH);
232641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
232741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int outerDepth = parser.getDepth();
232841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int type;
232941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
233041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
233141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (type != XmlPullParser.START_TAG) {
233241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                continue;
233341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
233441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final int depth = parser.getDepth();
233541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final String tag = parser.getName();
233641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (ShortcutService.DEBUG_LOAD) {
233741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
233841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                        depth, type, tag));
233941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
234041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            switch (tag) {
234141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_INTENT_EXTRAS:
234241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
234341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
234441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_EXTRAS:
234541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    extras = PersistableBundle.restoreFromXml(parser);
234641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
234741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
234841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
234941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
235041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return new ShortcutInfo(
235141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                id, packageName, activityComponent, /* icon =*/ null, title, intent,
235241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
235341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                iconRes, bitmapPath);
235441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
23556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki}
2356