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