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