ShortcutService.java revision 2d5b465fa9235e66ec176f6d6ffaaa0c18143e41
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
1172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    static final boolean DEBUG = true; // 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
6906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
6916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Update the last reset time.
6926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
6936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void updateTimes() {
6946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long now = injectCurrentTimeMillis();
6966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long prevLastResetTime = mRawLastResetTime;
6986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mRawLastResetTime == 0) { // first launch.
7006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
7016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
7026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else if (now < mRawLastResetTime) {
7036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Clock rewound.
7046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
7056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
7066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else {
7076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Do it properly.
7086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((mRawLastResetTime + mResetInterval) <= now) {
7096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mRawLastResetTime += mResetInterval;
7106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (prevLastResetTime != mRawLastResetTime) {
7136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            scheduleSaveBaseState();
7146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user state. */
7186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
7196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
7203f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) {
7213f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        UserShortcuts userPackages = mUsers.get(userId);
7226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (userPackages == null) {
7236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackages = loadUserLocked(userId);
7246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (userPackages == null) {
7253f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                userPackages = new UserShortcuts(userId);
7266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7273f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            mUsers.put(userId, userPackages);
7286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return userPackages;
7306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user per-package state. */
7336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
7346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
7356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private PackageShortcuts getPackageShortcutsLocked(
7366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @NonNull String packageName, @UserIdInt int userId) {
7373f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final UserShortcuts userPackages = getUserShortcutsLocked(userId);
7383f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        PackageShortcuts shortcuts = userPackages.getPackages().get(packageName);
7396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcuts == null) {
7405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcuts = new PackageShortcuts(userId, packageName);
7413f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            userPackages.getPackages().put(packageName, shortcuts);
7426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return shortcuts;
7446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Caller validation ===
7476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) {
7495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.getBitmapPath() != null) {
7505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (DEBUG) {
7515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                Slog.d(TAG, "Removing " + shortcut.getBitmapPath());
7525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
7535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            new File(shortcut.getBitmapPath()).delete();
7545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setBitmapPath(null);
7565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setIconResourceId(0);
7575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES);
7585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
7625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static class FileOutputStreamWithPath extends FileOutputStream {
7635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        private final File mFile;
7645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public FileOutputStreamWithPath(File file) throws FileNotFoundException {
7665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            super(file);
7675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            mFile = file;
7685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public File getFile() {
7715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mFile;
7725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
7745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    /**
7765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * Build the cached bitmap filename for a shortcut icon.
7775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     *
7785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * The filename will be based on the ID, except certain characters will be escaped.
7795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     */
7805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
7815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
7825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            throws IOException {
7835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final File packagePath = new File(getUserBitmapFilePath(userId),
7845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                shortcut.getPackageName());
7855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!packagePath.isDirectory()) {
7865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            packagePath.mkdirs();
7875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!packagePath.isDirectory()) {
7885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                throw new IOException("Unable to create directory " + packagePath);
7895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
7905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            SELinux.restorecon(packagePath);
7915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
7925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
7935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final String baseName = String.valueOf(injectCurrentTimeMillis());
7945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (int suffix = 0;; suffix++) {
7955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
7965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final File file = new File(packagePath, filename);
7975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!file.exists()) {
7985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (DEBUG) {
7995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
8005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return new FileOutputStreamWithPath(file);
8025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
8055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) {
8075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
8085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
8095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final long token = Binder.clearCallingIdentity();
8125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        try {
8135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            // Clear icon info on the shortcut.
8145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setIconResourceId(0);
8155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.setBitmapPath(null);
8165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final Icon icon = shortcut.getIcon();
8185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (icon == null) {
8195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return; // has no icon
8205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Bitmap bitmap = null;
8235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            try {
8245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                switch (icon.getType()) {
8255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_RESOURCE: {
8265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        injectValidateIconResPackage(shortcut, icon);
8275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.setIconResourceId(icon.getResId());
8295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
8305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        return;
8315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_BITMAP: {
8335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        bitmap = icon.getBitmap();
8345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        break;
8355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    case Icon.TYPE_URI: {
8375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        final Uri uri = ContentProvider.maybeAddUserId(icon.getUri(), userId);
8385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
8405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            bitmap = BitmapFactory.decodeStream(is);
8425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        } catch (IOException e) {
8445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            Slog.e(TAG, "Unable to load icon from " + uri);
8455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            return;
8465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        }
8475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        break;
8485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    default:
8505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        // This shouldn't happen because we've already validated the icon, but
8515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        // just in case.
8525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        throw ShortcutInfo.getInvalidIconException();
8535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (bitmap == null) {
8555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.e(TAG, "Null bitmap detected");
8565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return;
8575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                // Shrink and write to the file.
8595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                File path = null;
8605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                try {
8615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut);
8625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    try {
8635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        path = out.getFile();
8645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shrinkBitmap(bitmap, mMaxIconDimension)
8665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                                .compress(mIconPersistFormat, mIconPersistQuality, out);
8675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.setBitmapPath(out.getFile().getAbsolutePath());
8695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
8705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    } finally {
8715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        IoUtils.closeQuietly(out);
8725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                } catch (IOException|RuntimeException e) {
8745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    // STOPSHIP Change wtf to e
8755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
8765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (path != null && path.exists()) {
8775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        path.delete();
8785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
8795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } finally {
8815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (bitmap != null) {
8825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    bitmap.recycle();
8835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
8845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                // Once saved, we won't use the original icon information, so null it out.
8855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                shortcut.clearIcon();
8865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
8875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        } finally {
8885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Binder.restoreCallingIdentity(token);
8895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
8905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
8915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
8925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // Unfortunately we can't do this check in unit tests because we fake creator package names,
8935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // so override in unit tests.
8945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // TODO CTS this case.
8955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
8965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!shortcut.getPackageName().equals(icon.getResPackage())) {
8975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            throw new IllegalArgumentException(
8985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    "Icon resource must reside in shortcut owner package");
8995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
9015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
9035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
9045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        // Original width/height.
9055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int ow = in.getWidth();
9065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int oh = in.getHeight();
9075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if ((ow <= maxSize) && (oh <= maxSize)) {
9085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (DEBUG) {
9095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
9105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
9115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return in;
9125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int longerDimension = Math.max(ow, oh);
9145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        // New width and height.
9165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int nw = ow * maxSize / longerDimension;
9175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int nh = oh * maxSize / longerDimension;
9185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (DEBUG) {
9195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
9205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    ow, oh, nw, nh));
9215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
9225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
9245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final Canvas c = new Canvas(scaledBitmap);
9255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final RectF dst = new RectF(0, 0, nw, nh);
9275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
9295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        in.recycle();
9315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return scaledBitmap;
9335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
9345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // === Caller validation ===
9365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
9376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerSystem() {
9386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
9406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerShell() {
9436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
9456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceSystemOrShell() {
9486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerSystem() || isCallerShell(),
9496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                "Caller must be system or shell");
9506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceShell() {
9536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerShell(), "Caller must be shell");
9546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
9576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(packageName, "packageName");
9586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (isCallerSystem()) {
9606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // no check
9616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Otherwise, make sure the arguments are valid.
9666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (UserHandle.getUserId(callingUid) != userId) {
9676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new SecurityException("Invalid user-ID");
9686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
9706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // Caller is valid.
9716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new SecurityException("Caller UID= doesn't own " + packageName);
9736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Test overrides it.
9765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
9776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
9786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Is MATCH_UNINSTALLED_PACKAGES correct to get SD card app info?
9806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mContext.getPackageManager().getPackageUidAsUser(packageName,
9826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
9835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            | PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
9846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NameNotFoundException e) {
9856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return -1;
9866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
9906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
9916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
9926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void enforceMaxDynamicShortcuts(int numShortcuts) {
9936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (numShortcuts > mMaxDynamicShortcuts) {
9946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
9956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
9996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Sends a notification to LauncherApps
10006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Write to file
10016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
10026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) {
10036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        notifyListeners(packageName, userId);
10046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveUser(userId);
10056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
10086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutChangeListener> copy;
10096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> shortcuts = new ArrayList<>();
10106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
10116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy = new ArrayList<>(mListeners);
10126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId)
10146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    .findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
10156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        for (int i = copy.size() - 1; i >= 0; i--) {
10176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy.get(i).onShortcutChanged(packageName, shortcuts, userId);
10186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
10226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Clean up / validate an incoming shortcut.
10236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure all mandatory fields are set.
10246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure the intent's extras are persistable, and them to set
10256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     *  {@link ShortcutInfo#mIntentPersistableExtras}.  Also clear its extras.
10266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Clear flags.
10275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     *
10285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki     * TODO Detailed unit tests
10296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
10305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
10316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkNotNull(shortcut, "Null shortcut detected");
10326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcut.getActivityComponent() != null) {
10336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkState(
10346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    shortcut.getPackageName().equals(
10356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            shortcut.getActivityComponent().getPackageName()),
10366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    "Activity package name mismatch");
10376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (!forUpdate) {
10405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            shortcut.enforceMandatoryFields();
10415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (shortcut.getIcon() != null) {
10435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            ShortcutInfo.validateIcon(shortcut.getIcon());
10445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validateForXml(shortcut.getId());
10475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validateForXml(shortcut.getTitle());
10485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validatePersistableBundleForXml(shortcut.getIntentPersistableExtras());
10495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        validatePersistableBundleForXml(shortcut.getExtras());
10506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        shortcut.setFlags(0);
10525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
10545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those
10555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    // characters.
10565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
10575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static void validatePersistableBundleForXml(PersistableBundle b) {
10585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (b == null || b.size() == 0) {
10595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
10606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (String key : b.keySet()) {
10625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            validateForXml(key);
10635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final Object value = b.get(key);
10645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (value == null) {
10655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                continue;
10665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof String) {
10675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                validateForXml((String) value);
10685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof String[]) {
10695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                for (String v : (String[]) value) {
10705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    validateForXml(v);
10715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
10725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            } else if (value instanceof PersistableBundle) {
10735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                validatePersistableBundleForXml((PersistableBundle) value);
10745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
10755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static void validateForXml(String s) {
10795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        if (TextUtils.isEmpty(s)) {
10805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return;
10815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        for (int i = s.length() - 1; i >= 0; i--) {
10835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!isAllowedInXml(s.charAt(i))) {
10845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                throw new IllegalArgumentException("Unsupported character detected in: " + s);
10855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
10865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
10875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
10886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    private static boolean isAllowedInXml(char c) {
10905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
10916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === APIs ===
10946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
10966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
10976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
10986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
10996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
11016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int size = newShortcuts.size();
11026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
11076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
11086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
11096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            enforceMaxDynamicShortcuts(size);
11116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcuts.
11136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
11145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
11156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // First, remove all un-pinned; dynamic shortcuts
11185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            ps.deleteAllDynamicShortcuts(this);
11196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Then, add/update all.  We need to make sure to take over "pinned" flag.
11216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
11226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo newShortcut = newShortcuts.get(i);
11236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
11246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                ps.updateShortcutWithCapping(this, newShortcut);
11256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
11336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
11375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        final int size = newShortcuts.size();
11386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            // Throttling.
11435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            if (!ps.tryApiCall(this)) {
11445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return false;
11456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            for (int i = 0; i < size; i++) {
11485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo source = newShortcuts.get(i);
11495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
11505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo target = ps.findShortcutById(source.getId());
11525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (target != null) {
11535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    final boolean replacingIcon = (source.getIcon() != null);
11545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (replacingIcon) {
11555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        removeIcon(userId, target);
11565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
11575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    target.copyNonNullFieldsFrom(source);
11595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
11605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    if (replacingIcon) {
11615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        saveIconAndFixUpShortcut(userId, target);
11625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    }
11635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
11645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
11656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
11736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
11786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
11806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
11816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
11826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
11836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcut.
11855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
11866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Add it.
11886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
11896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            ps.updateShortcutWithCapping(this, newShortcut);
11906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
11946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteDynamicShortcut(String packageName, String shortcutId,
11986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
12016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this, shortcutId);
12046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
12066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
12106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(this);
12146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
12166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
12206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
12246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
12256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isDynamic);
12266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
12316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
12326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
12356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
12366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isPinned);
12376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
12416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
12426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutInfo> ret = new ArrayList<>();
12446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
12466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new ParceledListSlice<>(ret);
12486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
12526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws RemoteException {
12536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mMaxDynamicShortcuts;
12566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
12606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return mMaxDailyUpdates
12646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
12656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
12706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
12716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getNextResetTimeLocked();
12746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @Override
12785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
12795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        synchronized (mLock) {
12805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            return mMaxIconDimension;
12815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
12825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
12835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
12846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
12856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Reset all throttling, for developer options and command line.  Only system/shell can call it.
12866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
12876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
12886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void resetThrottling() {
12896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceSystemOrShell();
12906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        resetThrottlingInner();
12926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
12936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
12956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void resetThrottlingInner() {
12966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
12976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = injectCurrentTimeMillis();
12986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveBaseState();
13005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        Slog.i(TAG, "ShortcutManager: throttling counter reset");
13016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
13026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13032d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    // We override this method in unit tests to do a simpler check.
13042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
13052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        return hasShortcutHostPermissionInner(callingPackage, userId);
13062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
13072d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13082d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    // This method is extracted so we can directly call this method from unit tests,
13092d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    // even when hasShortcutPermission() is overridden.
13102d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    @VisibleForTesting
13112d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
13122d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        synchronized (mLock) {
13132d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            long start = 0;
13142d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (DEBUG) {
13152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                start = System.currentTimeMillis();
13162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13182d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            final UserShortcuts user = getUserShortcutsLocked(userId);
13192d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13202d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
13212d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            // Default launcher from package manager.
13232d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            final ComponentName defaultLauncher = injectPackageManagerInternal()
13242d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    .getHomeActivitiesAsUser(allHomeCandidates, userId);
13252d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            ComponentName detected;
13272d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (defaultLauncher != null) {
13282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                detected = defaultLauncher;
13292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                if (DEBUG) {
13302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    Slog.v(TAG, "Default launcher from PM: " + detected);
13312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
13322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            } else {
13332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                detected = user.getLauncherComponent();
13342d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13352d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // TODO: Make sure it's still enabled.
13362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                if (DEBUG) {
13372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    Slog.v(TAG, "Cached launcher: " + detected);
13382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
13392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (detected == null) {
13422d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // If we reach here, that means it's the first check since the user was created,
13432d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // and there's already multiple launchers and there's no default set.
13442d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // Find the system one with the highest priority.
13452d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // (We need to check the priority too because of FallbackHome in Settings.)
13462d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // If there's no system launcher yet, then no one can access shortcuts, until
13472d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // the user explicitly
13482d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                final int size = allHomeCandidates.size();
13492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13502d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                int lastPriority = Integer.MIN_VALUE;
13512d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                for (int i = 0; i < size; i++) {
13522d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    final ResolveInfo ri = allHomeCandidates.get(i);
13532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    if (!ri.activityInfo.applicationInfo.isSystemApp()) {
13542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        continue;
13552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    }
13562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    if (DEBUG) {
13572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
13582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                                ri.activityInfo.getComponentName(), ri.priority));
13592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    }
13602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    if (ri.priority < lastPriority) {
13612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        continue;
13622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    }
13632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    detected = ri.activityInfo.getComponentName();
13642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    lastPriority = ri.priority;
13652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
13662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (DEBUG) {
13682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                long end = System.currentTimeMillis();
13692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start));
13702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            if (detected != null) {
13722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                if (DEBUG) {
13732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    Slog.v(TAG, "Detected launcher: " + detected);
13742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
13752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                user.setLauncherComponent(this, detected);
13762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                return detected.getPackageName().equals(callingPackage);
13772d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            } else {
13782d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // Default launcher not found.
13792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                return false;
13802d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
13812d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
13822d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
13832d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
13846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
13856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Entry point from {@link LauncherApps}.
13866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
13876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class LocalService extends ShortcutServiceInternal {
13886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcuts(
13906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage, long changedSince,
13916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable String packageName, @Nullable ComponentName componentName,
13926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int queryFlags, int userId) {
13936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>();
13946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final int cloneFlag =
13956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
13966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
13976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
13986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (packageName != null) {
14016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags,
14026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            userId, ret, cloneFlag);
14036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                } else {
14046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    final ArrayMap<String, PackageShortcuts> packages =
14053f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                            getUserShortcutsLocked(userId).getPackages();
14065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    for (int i = packages.size() - 1; i >= 0; i--) {
14076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        getShortcutsInnerLocked(
14086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                packages.keyAt(i),
14096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                changedSince, componentName, queryFlags, userId, ret, cloneFlag);
14106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
14116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
14126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
14146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince,
14176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable ComponentName componentName, int queryFlags,
14186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
14196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId).findAll(ret,
14206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    (ShortcutInfo si) -> {
14216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (si.getLastChangedTimestamp() < changedSince) {
14226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
14236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
14246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (componentName != null
14256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && !componentName.equals(si.getActivityComponent())) {
14266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
14276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
14286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchDynamic =
14296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
14306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && si.isDynamic();
14316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchPinned =
14326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
14336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                        && si.isPinned();
14346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return matchDynamic || matchPinned;
14356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }, cloneFlag);
14366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcutInfo(
14406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage,
14416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String packageName, @Nullable List<String> ids, int userId) {
14426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
14436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
14446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
14466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArraySet<String> idSet = new ArraySet<>(ids);
14476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).findAll(ret,
14496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        (ShortcutInfo si) -> idSet.contains(si.getId()),
14506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
14516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
14536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
14576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull List<String> shortcutIds, int userId) {
14586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
14596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
14606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkNotNull(shortcutIds, "shortcutIds");
14616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                getPackageShortcutsLocked(packageName, userId).replacePinned(
14645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        ShortcutService.this, callingPackage, shortcutIds);
14656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackageChanged(packageName, userId);
14676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Intent createShortcutIntent(@NonNull String callingPackage,
147143204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki                @NonNull String packageName, @NonNull String shortcutId, int userId) {
14726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
147343204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
147443204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki            Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
14756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo fullShortcut =
147843204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki                        getPackageShortcutsLocked(packageName, userId)
14795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        .findShortcutById(shortcutId);
14805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return fullShortcut == null ? null : fullShortcut.getIntent();
14816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void addListener(@NonNull ShortcutChangeListener listener) {
14866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mListeners.add(Preconditions.checkNotNull(listener));
14886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        @Override
14925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public int getShortcutIconResId(@NonNull String callingPackage,
14935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                @NonNull ShortcutInfo shortcut, int userId) {
14945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            Preconditions.checkNotNull(shortcut, "shortcut");
14955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
14965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            synchronized (mLock) {
14975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
14985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
14995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                return (shortcutInfo != null && shortcutInfo.hasIconResource())
15005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                        ? shortcutInfo.getIconResourceId() : 0;
15015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
15025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
15035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        @Override
15055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
150634d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                @NonNull ShortcutInfo shortcutIn, int userId) {
150734d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki            Preconditions.checkNotNull(shortcutIn, "shortcut");
15085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            synchronized (mLock) {
15105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
151134d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
15125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
15135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return null;
15145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
15155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                try {
151634d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                    if (shortcutInfo.getBitmapPath() == null) {
151734d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
151834d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                        return null;
151934d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki                    }
15205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return ParcelFileDescriptor.open(
15215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            new File(shortcutInfo.getBitmapPath()),
15225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                            ParcelFileDescriptor.MODE_READ_ONLY);
15235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                } catch (FileNotFoundException e) {
15245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath());
15255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                    return null;
15265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki                }
15275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            }
15285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        }
15292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
15302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        @Override
15312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
15322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId);
15332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
15346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Dump ===
15376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
15396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
15406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
15416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                != PackageManager.PERMISSION_GRANTED) {
15426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Permission Denial: can't dump UserManager from from pid="
15436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + Binder.getCallingPid()
15446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + ", uid=" + Binder.getCallingUid()
15456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + " without permission "
15466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + android.Manifest.permission.DUMP);
15476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
15486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        dumpInner(pw);
15506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
15536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void dumpInner(PrintWriter pw) {
15546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
15556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long now = injectCurrentTimeMillis();
15566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("Now: [");
15576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(now);
15586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(now));
15605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Raw last reset: [");
15626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(mRawLastResetTime);
15636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(mRawLastResetTime));
15656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long last = getLastResetTimeLocked();
15676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Last reset: [");
15686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(last);
15696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(last));
15716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            final long next = getNextResetTimeLocked();
15736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Next reset: [");
15746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(next);
15756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
15766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(next));
15776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
15786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Max icon dim: ");
15805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mMaxIconDimension);
15815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Icon format: ");
15825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mIconPersistFormat);
15835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print("  Icon quality: ");
15845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.print(mIconPersistQuality);
15855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki            pw.println();
15865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
15876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15883f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            for (int i = 0; i < mUsers.size(); i++) {
15893f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                pw.println();
15903f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                mUsers.valueAt(i).dump(this, pw, "  ");
15916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
15926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
15936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
159541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    static String formatTime(long time) {
15966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Time tobj = new Time();
15976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        tobj.set(time);
15986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return tobj.format("%Y-%m-%d %H:%M:%S");
15996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Shell support ===
16026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
16046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
16056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String[] args, ResultReceiver resultReceiver) throws RemoteException {
16066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceShell();
16086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
16106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
16116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16122d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    static class CommandException extends Exception {
16132d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        public CommandException(String message) {
16142d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            super(message);
16152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
16162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
16172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
16186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
16196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Handle "adb shell cmd".
16206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
16216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class MyShellCommand extends ShellCommand {
16222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
16232d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private int mUserId = UserHandle.USER_SYSTEM;
16242d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
16252d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void parseOptions(boolean takeUser)
16262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                throws CommandException {
16272d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            String opt;
16282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            while ((opt = getNextOption()) != null) {
16292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                switch (opt) {
16302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "--user":
16312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        if (takeUser) {
16322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                            mUserId = UserHandle.parseUserArg(getNextArgRequired());
16332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                            break;
16342d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        }
16352d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        // fallthrough
16362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    default:
16372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        throw new CommandException("Unknown option: " + opt);
16382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
16392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
16402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
16412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
16426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
16436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public int onCommand(String cmd) {
16446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (cmd == null) {
16456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return handleDefaultCommands(cmd);
16466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
16476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
16482d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            try {
16492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                switch (cmd) {
16502d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "reset-package-throttling":
16512d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleResetPackageThrottling();
16522d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "reset-throttling":
16542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleResetThrottling();
16552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "override-config":
16572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleOverrideConfig();
16582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "reset-config":
16602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleResetConfig();
16612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "clear-default-launcher":
16632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleClearDefaultLauncher();
16642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "get-default-launcher":
16662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleGetDefaultLauncher();
16672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    case "refresh-default-launcher":
16692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        handleRefreshDefaultLauncher();
16702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        break;
16712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    default:
16722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        return handleDefaultCommands(cmd);
16732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                }
16742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            } catch (CommandException e) {
16752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                pw.println("Error: " + e.getMessage());
16762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                return 1;
16774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
16782d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("Success");
16792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            return 0;
16806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
16816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
16826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
16836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onHelp() {
16846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
16856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Usage: cmd shortcut COMMAND [options ...]");
16866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
16886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for a package");
16896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-throttling");
16916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for all packages and users");
16926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
16934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("cmd shortcut override-config CONFIG");
16944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("    Override the configuration for testing (will last until reboot)");
16954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println();
16964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("cmd shortcut reset-config");
16974362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println("    Reset the configuration set with \"update-config\"");
16984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            pw.println();
16992d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("cmd shortcut clear-default-launcher [--user USER_ID]");
17002d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("    Clear the cached default launcher");
17012d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println();
17022d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
17032d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("    Show the cached default launcher");
17042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println();
17052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]");
17062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println("    Refresh the cached default launcher");
17072d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            pw.println();
17086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
17096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int handleResetThrottling() {
17116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            resetThrottling();
17126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
17136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
17146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleResetPackageThrottling() throws CommandException {
17162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            parseOptions(/* takeUser =*/ true);
17176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final String packageName = getNextArgRequired();
17196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
17212d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine();
17222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                saveUserLocked(mUserId);
17236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
17246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
17254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
17262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleOverrideConfig() throws CommandException {
17274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            final String config = getNextArgRequired();
17284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
17294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            synchronized (mLock) {
17304362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                if (!updateConfigurationLocked(config)) {
17312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    throw new CommandException("override-config failed.  See logcat for details.");
17324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                }
17334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
17344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
17354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
17362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleResetConfig() {
17374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            synchronized (mLock) {
17384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki                loadConfigurationLocked();
17394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki            }
17402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17422d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void clearLauncher() {
17432d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            synchronized (mLock) {
17442d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                getUserShortcutsLocked(mUserId).setLauncherComponent(
17452d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        ShortcutService.this, null);
17462d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
17472d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17482d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void showLauncher() {
17502d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            synchronized (mLock) {
17512d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                // This ensures to set the cached launcher.  Package name doesn't matter.
17522d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                hasShortcutHostPermissionInner("-", mUserId);
17532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                getOutPrintWriter().println("Launcher: "
17552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                        + getUserShortcutsLocked(mUserId).getLauncherComponent());
17562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            }
17572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleClearDefaultLauncher() throws CommandException {
17602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            parseOptions(/* takeUser =*/ true);
17612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            clearLauncher();
17632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleGetDefaultLauncher() throws CommandException {
17662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            parseOptions(/* takeUser =*/ true);
17672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            showLauncher();
17692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
17702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        private void handleRefreshDefaultLauncher() throws CommandException {
17722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            parseOptions(/* takeUser =*/ true);
17732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
17742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            clearLauncher();
17752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            showLauncher();
17764362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        }
17776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Unit test support ===
17806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
17826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long injectCurrentTimeMillis() {
17836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return System.currentTimeMillis();
17846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
17876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    int injectBinderCallingUid() {
17886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return getCallingUid();
17896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectSystemDataPath() {
17926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return Environment.getDataSystemDirectory();
17936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
17946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
17956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectUserDataPath(@UserIdInt int userId) {
17965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
17975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
17985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
17994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
18005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    boolean injectIsLowRamDevice() {
18015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return ActivityManager.isLowRamDeviceStatic();
18025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
18035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
18042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    PackageManagerInternal injectPackageManagerInternal() {
18052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        return mPackageManagerInternal;
18062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
18072d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
18085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    File getUserBitmapFilePath(@UserIdInt int userId) {
18095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki        return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
18106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
18133f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    SparseArray<UserShortcuts> getShortcutsForTest() {
18143f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return mUsers;
18156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
18184362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxDynamicShortcutsForTest() {
18194362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxDynamicShortcuts;
18204362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
18214362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
18224362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
18234362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxDailyUpdatesForTest() {
18244362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxDailyUpdates;
18254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    }
18264362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki
18274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    @VisibleForTesting
18284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    long getResetIntervalForTest() {
18294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mResetInterval;
18306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
18334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getMaxIconDimensionForTest() {
18344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mMaxIconDimension;
18356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
18366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
18376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
18384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    CompressFormat getIconPersistFormatForTest() {
18394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mIconPersistFormat;
18405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    }
18415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki
18425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki    @VisibleForTesting
18434362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki    int getIconPersistQualityForTest() {
18444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki        return mIconPersistQuality;
18456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
184641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
184741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @VisibleForTesting
184841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
184941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        synchronized (mLock) {
185041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return getPackageShortcutsLocked(packageName, userId).findShortcutById(shortcutId);
185141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
185241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
185341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki}
185441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
18552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki/**
18562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki * Per-user information.
18572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki */
18583f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onukiclass UserShortcuts {
18593f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private static final String TAG = ShortcutService.TAG;
18603f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18613f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    @UserIdInt
18623f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    final int mUserId;
18633f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18643f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>();
18653f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    private ComponentName mLauncherComponent;
18672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
18683f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public UserShortcuts(int userId) {
18693f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        mUserId = userId;
18703f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
18713f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18723f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public ArrayMap<String, PackageShortcuts> getPackages() {
18733f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return mPackages;
18743f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
18753f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18763f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
18773f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        out.startTag(null, ShortcutService.TAG_USER);
18783f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        ShortcutService.writeTagValue(out, ShortcutService.TAG_LAUNCHER,
18802d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                mLauncherComponent);
18813f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18822d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        for (int i = 0; i < mPackages.size(); i++) {
18832d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            mPackages.valueAt(i).saveToXml(out);
18843f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
18853f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18863f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        out.endTag(null, ShortcutService.TAG_USER);
18873f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
18883f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18893f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public static UserShortcuts loadFromXml(XmlPullParser parser, int userId)
18903f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            throws IOException, XmlPullParserException {
18913f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final UserShortcuts ret = new UserShortcuts(userId);
18923f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
18933f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        final int outerDepth = parser.getDepth();
18943f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        int type;
18953f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
18963f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
18973f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            if (type != XmlPullParser.START_TAG) {
18983f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                continue;
18993f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            }
19003f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            final int depth = parser.getDepth();
19013f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            final String tag = parser.getName();
19023f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            switch (tag) {
19032d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                case ShortcutService.TAG_LAUNCHER:
19042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
19052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                            parser, ShortcutService.ATTR_VALUE);
19062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki                    continue;
19073f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                case ShortcutService.TAG_PACKAGE:
19083f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
19093f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
19103f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    // Don't use addShortcut(), we don't need to save the icon.
19113f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    ret.getPackages().put(shortcuts.mPackageName, shortcuts);
19123f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki                    continue;
19133f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            }
19143f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
19153f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
19163f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        return ret;
19173f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
19183f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
19192d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    public ComponentName getLauncherComponent() {
19202d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        return mLauncherComponent;
19212d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
19222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
19232d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) {
19242d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        if (Objects.equal(mLauncherComponent, launcherComponent)) {
19252d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki            return;
19262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        }
19272d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        mLauncherComponent = launcherComponent;
19282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        s.scheduleSaveUser(mUserId);
19292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki    }
19302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
19313f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
19322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print(prefix);
19333f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.print("User: ");
19343f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.print(mUserId);
19353f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        pw.println();
19363f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
19372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print(prefix);
19382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print("  ");
19392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print("Default launcher: ");
19402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.print(mLauncherComponent);
19412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki        pw.println();
19422d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki
19433f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        for (int i = 0; i < mPackages.size(); i++) {
19443f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki            mPackages.valueAt(i).dump(s, pw, prefix + "  ");
19453f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki        }
19463f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki    }
19473f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki}
19483f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki
194941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki/**
195041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * All the information relevant to shortcuts from a single package (per-user).
195141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */
195241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onukiclass PackageShortcuts {
195341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static final String TAG = ShortcutService.TAG;
195441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
195541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @UserIdInt
195641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final int mUserId;
195741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
195841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @NonNull
195941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final String mPackageName;
196041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
196141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
196241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * All the shortcuts from the package, keyed on IDs.
196341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
196441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
196541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
196641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
196741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * # of dynamic shortcuts.
196841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
196941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private int mDynamicShortcutCount = 0;
197041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
197141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
197241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * # of times the package has called rate-limited APIs.
197341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
197441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private int mApiCallCount;
197541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
197641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
197741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * When {@link #mApiCallCount} was reset last time.
197841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
197941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private long mLastResetTime;
198041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
198141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    PackageShortcuts(int userId, String packageName) {
198241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mUserId = userId;
198341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mPackageName = packageName;
198441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
198541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
198641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
198741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @Nullable
198841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public ShortcutInfo findShortcutById(String id) {
198941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return mShortcuts.get(id);
199041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
199141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
199241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
199341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @NonNull String id) {
199441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo shortcut = mShortcuts.remove(id);
199541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (shortcut != null) {
199641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            s.removeIcon(mUserId, shortcut);
199741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
199841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
199941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return shortcut;
200041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
200141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
200241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
200341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        deleteShortcut(s, newShortcut.getId());
200441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        s.saveIconAndFixUpShortcut(mUserId, newShortcut);
200541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mShortcuts.put(newShortcut.getId(), newShortcut);
200641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
200741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
200841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
200941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Add a shortcut, or update one with the same ID, with taking over existing flags.
201041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     *
201141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * It checks the max number of dynamic shortcuts.
201241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
201341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
201441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void updateShortcutWithCapping(@NonNull ShortcutService s,
201541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @NonNull ShortcutInfo newShortcut) {
201641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
201741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
201841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int oldFlags = 0;
201941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int newDynamicCount = mDynamicShortcutCount;
202041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
202141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut != null) {
202241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            oldFlags = oldShortcut.getFlags();
202341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (oldShortcut.isDynamic()) {
202441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                newDynamicCount--;
202541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
202641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
202741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (newShortcut.isDynamic()) {
202841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            newDynamicCount++;
202941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
203041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Make sure there's still room.
203141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        s.enforceMaxDynamicShortcuts(newDynamicCount);
203241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
203341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Okay, make it dynamic and add.
203441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        newShortcut.addFlags(oldFlags);
203541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
203641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        addShortcut(s, newShortcut);
203741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mDynamicShortcutCount = newDynamicCount;
203841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
203941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
204041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
204141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Remove all shortcuts that aren't pinned nor dynamic.
204241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
204341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private void removeOrphans(@NonNull ShortcutService s) {
204441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ArrayList<String> removeList = null; // Lazily initialize.
204541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
204641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
204741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = mShortcuts.valueAt(i);
204841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
204941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (si.isPinned() || si.isDynamic()) continue;
205041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
205141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (removeList == null) {
205241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                removeList = new ArrayList<>();
205341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
205441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            removeList.add(si.getId());
205541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
205641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (removeList != null) {
205741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            for (int i = removeList.size() - 1 ; i >= 0; i--) {
205841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                deleteShortcut(s, removeList.get(i));
205941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
206041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
206141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
206241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
206341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
206441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
206541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
206641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
206741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
206841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        removeOrphans(s);
206941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mDynamicShortcutCount = 0;
207041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
207141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
207241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
207341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
207441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
207541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
207641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut == null) {
207741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return;
207841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
207941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut.isDynamic()) {
208041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mDynamicShortcutCount--;
208141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
208241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (oldShortcut.isPinned()) {
208341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
208441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        } else {
208541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            deleteShortcut(s, shortcutId);
208641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
208741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
208841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
208941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
209041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void replacePinned(@NonNull ShortcutService s, String launcherPackage,
209141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            List<String> shortcutIds) {
209241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
209341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // TODO Should be per launcherPackage.
209441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
209541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // First, un-pin all shortcuts
209641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
209741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
209841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
209941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
210041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Then pin ALL
210141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = shortcutIds.size() - 1; i >= 0; i--) {
210241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i));
210341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (shortcut != null) {
210441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
210541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
210641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
210741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
210841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        removeOrphans(s);
210941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
211041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
211141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
211241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Number of calls that the caller has made, since the last reset.
211341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
211441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
211541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public int getApiCallCount(@NonNull ShortcutService s) {
211641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final long last = s.getLastResetTimeLocked();
211741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
211841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final long now = s.injectCurrentTimeMillis();
211941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (mLastResetTime > now) {
212041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            // Clock rewound. // TODO Test it
212141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mLastResetTime = now;
212241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
212341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
212441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // If not reset yet, then reset.
212541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (mLastResetTime < last) {
212641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mApiCallCount = 0;
212741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            mLastResetTime = last;
212841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
212941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return mApiCallCount;
213041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
213141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
213241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
213341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
213441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * and return true.  Otherwise just return false.
213541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
213641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
213741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public boolean tryApiCall(@NonNull ShortcutService s) {
213841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
213941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            return false;
214041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
214141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mApiCallCount++;
214241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return true;
214341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
214441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
214541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
214641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void resetRateLimitingForCommandLine() {
214741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mApiCallCount = 0;
214841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        mLastResetTime = 0;
214941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
215041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
215141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    /**
215241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     * Find all shortcuts that match {@code query}.
215341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki     */
215441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    @GuardedBy("mLock")
215541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void findAll(@NonNull List<ShortcutInfo> result,
215641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
215741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = 0; i < mShortcuts.size(); i++) {
215841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = mShortcuts.valueAt(i);
215941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (query == null || query.test(si)) {
216041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                result.add(si.clone(cloneFlag));
216141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
216241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
216341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
216441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
216541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
216641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
216741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Package: ");
216841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(mPackageName);
216941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
217041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
217141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
217241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
217341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Calls: ");
217441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(getApiCallCount(s));
217541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
217641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
217741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // This should be after getApiCallCount(), which may update it.
217841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
217941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
218041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Last reset: [");
218141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(mLastResetTime);
218241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("] ");
218341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(s.formatTime(mLastResetTime));
218441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println();
218541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
218641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println("      Shortcuts:");
218741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        long totalBitmapSize = 0;
218841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
218941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int size = shortcuts.size();
219041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int i = 0; i < size; i++) {
219141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final ShortcutInfo si = shortcuts.valueAt(i);
219241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            pw.print("        ");
219341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            pw.println(si.toInsecureString());
219434d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki            if (si.getBitmapPath() != null) {
219541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                final long len = new File(si.getBitmapPath()).length();
219641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.print("          ");
219741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.print("bitmap size=");
219841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                pw.println(len);
219941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
220041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                totalBitmapSize += len;
220141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
220241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
220341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(prefix);
220441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("  ");
220541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print("Total bitmap size: ");
220641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(totalBitmapSize);
220741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(" (");
220841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
220941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        pw.println(")");
221041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
221141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
221241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
221341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.startTag(null, ShortcutService.TAG_PACKAGE);
221441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
221541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_NAME, mPackageName);
221641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
221741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_CALL_COUNT, mApiCallCount);
221841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_LAST_RESET, mLastResetTime);
221941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
222041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int size = mShortcuts.size();
222141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        for (int j = 0; j < size; j++) {
222241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            saveShortcut(out, mShortcuts.valueAt(j));
222341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
222441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
222541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.endTag(null, ShortcutService.TAG_PACKAGE);
222641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
222741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
222841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
222941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
223041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.startTag(null, ShortcutService.TAG_SHORTCUT);
223141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ID, si.getId());
223241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // writeAttr(out, "package", si.getPackageName()); // not needed
223341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ACTIVITY, si.getActivityComponent());
223441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // writeAttr(out, "icon", si.getIcon());  // We don't save it.
223541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_TITLE, si.getTitle());
223641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_INTENT, si.getIntentNoExtras());
223741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_WEIGHT, si.getWeight());
223841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_TIMESTAMP,
223941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                si.getLastChangedTimestamp());
224041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_FLAGS, si.getFlags());
224141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_ICON_RES, si.getIconResourceId());
224241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeAttr(out, ShortcutService.ATTR_BITMAP_PATH, si.getBitmapPath());
224341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
224441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeTagExtra(out, ShortcutService.TAG_INTENT_EXTRAS,
224541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                si.getIntentPersistableExtras());
224641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ShortcutService.writeTagExtra(out, ShortcutService.TAG_EXTRAS, si.getExtras());
224741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
224841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        out.endTag(null, ShortcutService.TAG_SHORTCUT);
224941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
225041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
225141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId)
225241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
225341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
225441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final String packageName = ShortcutService.parseStringAttribute(parser,
225541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_NAME);
225641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
225741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final PackageShortcuts ret = new PackageShortcuts(userId, packageName);
225841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
225941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mDynamicShortcutCount =
226041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_DYNAMIC_COUNT);
226141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mApiCallCount =
226241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_CALL_COUNT);
226341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ret.mLastResetTime =
226441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_LAST_RESET);
226541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
226641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int outerDepth = parser.getDepth();
226741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int type;
226841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
226941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
227041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (type != XmlPullParser.START_TAG) {
227141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                continue;
227241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
227341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final int depth = parser.getDepth();
227441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final String tag = parser.getName();
227541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            switch (tag) {
227641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_SHORTCUT:
227741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    final ShortcutInfo si = parseShortcut(parser, packageName);
227841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
227941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    // Don't use addShortcut(), we don't need to save the icon.
228041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    ret.mShortcuts.put(si.getId(), si);
228141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
228241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
228341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
228441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
228541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return ret;
228641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
228741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
228841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
228941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throws IOException, XmlPullParserException {
229041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String id;
229141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        ComponentName activityComponent;
229241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        // Icon icon;
229341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String title;
229441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        Intent intent;
229541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        PersistableBundle intentPersistableExtras = null;
229641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int weight;
229741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        PersistableBundle extras = null;
229841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        long lastChangedTimestamp;
229941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int flags;
230041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int iconRes;
230141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        String bitmapPath;
230241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
230341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        id = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_ID);
230441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        activityComponent = ShortcutService.parseComponentNameAttribute(parser,
230541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_ACTIVITY);
230641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        title = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_TITLE);
230741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        intent = ShortcutService.parseIntentAttribute(parser, ShortcutService.ATTR_INTENT);
230841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        weight = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_WEIGHT);
230941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
231041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                ShortcutService.ATTR_TIMESTAMP);
231141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        flags = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_FLAGS);
231241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        iconRes = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_ICON_RES);
231341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        bitmapPath = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_BITMAP_PATH);
231441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki
231541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        final int outerDepth = parser.getDepth();
231641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        int type;
231741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
231841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
231941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (type != XmlPullParser.START_TAG) {
232041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                continue;
232141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
232241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final int depth = parser.getDepth();
232341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            final String tag = parser.getName();
232441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            if (ShortcutService.DEBUG_LOAD) {
232541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
232641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                        depth, type, tag));
232741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
232841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            switch (tag) {
232941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_INTENT_EXTRAS:
233041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
233141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
233241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                case ShortcutService.TAG_EXTRAS:
233341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    extras = PersistableBundle.restoreFromXml(parser);
233441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                    continue;
233541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            }
233641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki            throw ShortcutService.throwForInvalidTag(depth, tag);
233741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        }
233841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki        return new ShortcutInfo(
233941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                id, packageName, activityComponent, /* icon =*/ null, title, intent,
234041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
234141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki                iconRes, bitmapPath);
234241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki    }
23436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki}
2344