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