ShortcutService.java revision 2d5b465fa9235e66ec176f6d6ffaaa0c18143e41
16f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki/* 26f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Copyright (C) 2016 The Android Open Source Project 36f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * 46f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License"); 56f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * you may not use this file except in compliance with the License. 66f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * You may obtain a copy of the License at 76f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * 86f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * http://www.apache.org/licenses/LICENSE-2.0 96f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * 106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Unless required by applicable law or agreed to in writing, software 116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS, 126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * See the License for the specific language governing permissions and 146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * limitations under the License. 156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukipackage com.android.server.pm; 176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.NonNull; 196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.Nullable; 206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.UserIdInt; 215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.app.ActivityManager; 226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.ComponentName; 235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.content.ContentProvider; 246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.Context; 256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.Intent; 266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.IShortcutService; 276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.LauncherApps; 286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.LauncherApps.ShortcutQuery; 296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.PackageManager; 306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.PackageManager.NameNotFoundException; 312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onukiimport android.content.pm.PackageManagerInternal; 326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ParceledListSlice; 332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onukiimport android.content.pm.ResolveInfo; 346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutInfo; 356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutServiceInternal; 366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; 375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Bitmap; 385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Bitmap.CompressFormat; 395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.BitmapFactory; 405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.Canvas; 415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.graphics.RectF; 426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.graphics.drawable.Icon; 435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.net.Uri; 446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Binder; 456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Environment; 466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Handler; 475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.os.ParcelFileDescriptor; 486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.PersistableBundle; 496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Process; 506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.RemoteException; 516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.ResultReceiver; 525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.os.SELinux; 536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.ShellCommand; 546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.UserHandle; 556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.text.TextUtils; 565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.text.format.Formatter; 576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.text.format.Time; 586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.ArrayMap; 596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.ArraySet; 606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.AtomicFile; 614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onukiimport android.util.KeyValueListParser; 626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.Slog; 636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.SparseArray; 645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport android.util.TypedValue; 656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.Xml; 666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.annotations.GuardedBy; 686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.annotations.VisibleForTesting; 696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.os.BackgroundThread; 706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.util.FastXmlSerializer; 716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.util.Preconditions; 726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.server.LocalServices; 736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.server.SystemService; 746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport libcore.io.IoUtils; 762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onukiimport libcore.util.Objects; 776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlPullParser; 796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlPullParserException; 806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlSerializer; 816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.File; 836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileDescriptor; 846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileInputStream; 856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileNotFoundException; 866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileOutputStream; 876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.IOException; 885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onukiimport java.io.InputStream; 896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.PrintWriter; 906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.net.URISyntaxException; 916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.nio.charset.StandardCharsets; 926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.ArrayList; 936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.List; 946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.function.Predicate; 956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki/** 976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * TODO: 986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * 995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Detect when already registered instances are passed to APIs again, which might break 1005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * internal bitmap handling. 1016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * 1026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res 1035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * -> Need to scan all packages when a user starts too. 1045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * -> Clear data -> remove all dynamic? but not the pinned? 1056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * 1066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Pinned per each launcher package (multiple launchers) 1076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * 1085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Make save async (should we?) 1095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * 1105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Scan and remove orphan bitmaps (just in case). 1115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * 1125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * - Backup & restore 1136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 1146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukipublic class ShortcutService extends IShortcutService.Stub { 1155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki static final String TAG = "ShortcutService"; 1166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 1172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki static final boolean DEBUG = true; // STOPSHIP if true 11841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final boolean DEBUG_LOAD = false; // STOPSHIP if true 1196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 1204362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 1214362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day 1224362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1234362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 1244362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki static final int DEFAULT_MAX_DAILY_UPDATES = 10; 1254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1264362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 1274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5; 1284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 1304362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; 1314362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 1334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48; 1344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 1364362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name(); 1374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 1394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki static final int DEFAULT_ICON_PERSIST_QUALITY = 100; 1406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 1416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private static final int SAVE_DELAY_MS = 5000; // in milliseconds. 1426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 1436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 1446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki static final String FILENAME_BASE_STATE = "shortcut_service.xml"; 1456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 1466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 1476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki static final String DIRECTORY_PER_USER = "shortcut_service"; 1486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 1496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 1506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki static final String FILENAME_USER_PACKAGES = "shortcuts.xml"; 1516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 1525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki static final String DIRECTORY_BITMAPS = "bitmaps"; 1536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 15441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String TAG_ROOT = "root"; 1553f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki static final String TAG_USER = "user"; 15641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String TAG_PACKAGE = "package"; 15741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String TAG_LAST_RESET_TIME = "last_reset_time"; 15841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String TAG_INTENT_EXTRAS = "intent-extras"; 15941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String TAG_EXTRAS = "extras"; 16041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String TAG_SHORTCUT = "shortcut"; 1612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki static final String TAG_LAUNCHER = "launcher"; 16241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 16341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_VALUE = "value"; 16441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_NAME = "name"; 16541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_DYNAMIC_COUNT = "dynamic-count"; 16641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_CALL_COUNT = "call-count"; 16741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_LAST_RESET = "last-reset"; 16841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_ID = "id"; 16941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_ACTIVITY = "activity"; 17041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_TITLE = "title"; 17141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_INTENT = "intent"; 17241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_WEIGHT = "weight"; 17341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_TIMESTAMP = "timestamp"; 17441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_FLAGS = "flags"; 17541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_ICON_RES = "icon-res"; 17641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static final String ATTR_BITMAP_PATH = "bitmap-path"; 1776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 1784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 1794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki interface ConfigConstants { 1804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 1814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * Key name for the throttling reset interval, in seconds. (long) 1824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 1834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki String KEY_RESET_INTERVAL_SEC = "reset_interval_sec"; 1844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 1864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * Key name for the max number of modifying API calls per app for every interval. (int) 1874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 1884362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki String KEY_MAX_DAILY_UPDATES = "max_daily_updates"; 1894362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1904362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 1914362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * Key name for the max icon dimensions in DP, for non-low-memory devices. 1924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 1934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp"; 1944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 1954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 1964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * Key name for the max icon dimensions in DP, for low-memory devices. 1974362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 1984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram"; 1994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 2004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 2014362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * Key name for the max dynamic shortcuts per app. (int) 2024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 2034362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki String KEY_MAX_SHORTCUTS = "max_shortcuts"; 2044362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 2054362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 20641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * Key name for icon compression quality, 0-100. 2074362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 2084362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki String KEY_ICON_QUALITY = "icon_quality"; 2094362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 2104362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 2114362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * Key name for icon compression format: "PNG", "JPEG" or "WEBP" 2124362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 2134362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki String KEY_ICON_FORMAT = "icon_format"; 2144362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 2154362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 21641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final Context mContext; 2176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private final Object mLock = new Object(); 2196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private final Handler mHandler; 2216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @GuardedBy("mLock") 2236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1); 2246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @GuardedBy("mLock") 2266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private long mRawLastResetTime; 2276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 2293f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki * User ID -> UserShortcuts 2306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 2316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @GuardedBy("mLock") 2323f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki private final SparseArray<UserShortcuts> mUsers = new SparseArray<>(); 2336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 2356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Max number of dynamic shortcuts that each application can have at a time. 2366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 2376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private int mMaxDynamicShortcuts; 2386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 2406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Max number of updating API calls that each application can make a day. 2416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 24241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki int mMaxDailyUpdates; 2436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 2456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Actual throttling-reset interval. By default it's a day. 2466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 2476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private long mResetInterval; 2486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki /** 2505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * Icon max width/height in pixels. 2515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki */ 2525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki private int mMaxIconDimension; 2535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 2544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki private CompressFormat mIconPersistFormat; 2554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki private int mIconPersistQuality; 2565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 2572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private final PackageManagerInternal mPackageManagerInternal; 2582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 2596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public ShortcutService(Context context) { 2606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mContext = Preconditions.checkNotNull(context); 2616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki LocalServices.addService(ShortcutServiceInternal.class, new LocalService()); 2626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mHandler = new Handler(BackgroundThread.get().getLooper()); 2632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); 2646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 2656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 2676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * System service lifecycle. 2686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 2696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public static final class Lifecycle extends SystemService { 2706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ShortcutService mService; 2716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public Lifecycle(Context context) { 2736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki super(context); 2746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mService = new ShortcutService(context); 2756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 2766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 2786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void onStart() { 2796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki publishBinderService(Context.SHORTCUT_SERVICE, mService); 2806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 2816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 2836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void onBootPhase(int phase) { 2846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mService.onBootPhase(phase); 2856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 2866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 2886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void onCleanupUser(int userHandle) { 2896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mService.mLock) { 2906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mService.onCleanupUserInner(userHandle); 2916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 2926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 2936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 2946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 295f3a572b5c0cab23a435bd90414d25de84e00398eMakoto Onuki public void onUnlockUser(int userId) { 2966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mService.mLock) { 2976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mService.onStartUserLocked(userId); 2986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 2996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 3026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** lifecycle event */ 3036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki void onBootPhase(int phase) { 3046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (DEBUG) { 3056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.d(TAG, "onBootPhase: " + phase); 3066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki switch (phase) { 3086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki case SystemService.PHASE_LOCK_SETTINGS_READY: 3096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki initialize(); 3106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki break; 3116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 3146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** lifecycle event */ 3156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki void onStartUserLocked(int userId) { 3166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Preload 3176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki getUserShortcutsLocked(userId); 3186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 3206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** lifecycle event */ 3216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki void onCleanupUserInner(int userId) { 3226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Unload 3233f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki mUsers.delete(userId); 3246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 3266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** Return the base state file name */ 3276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private AtomicFile getBaseStateFile() { 3286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE); 3296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki path.mkdirs(); 3306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return new AtomicFile(path); 3316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 3336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 3346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Init the instance. (load the state file, etc) 3356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 3366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void initialize() { 3376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 3384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki loadConfigurationLocked(); 3396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki loadBaseStateLocked(); 3406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 3426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 3434362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 3444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * Load the configuration from Settings. 3454362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 3464362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki private void loadConfigurationLocked() { 3474362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki updateConfigurationLocked(injectShortcutManagerConstants()); 3484362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 3494362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3504362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki /** 3514362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * Load the configuration from Settings. 3524362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki */ 3534362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 3544362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki boolean updateConfigurationLocked(String config) { 3554362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki boolean result = true; 3564362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3574362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki final KeyValueListParser parser = new KeyValueListParser(','); 3584362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki try { 3594362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki parser.setString(config); 3604362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } catch (IllegalArgumentException e) { 3614362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki // Failed to parse the settings string, log this and move on 3624362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki // with defaults. 3634362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki Slog.e(TAG, "Bad shortcut manager settings", e); 3644362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki result = false; 3654362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 3664362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3674362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki mResetInterval = parser.getLong( 3684362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC) 3694362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki * 1000L; 3704362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3714362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki mMaxDailyUpdates = (int) parser.getLong( 3724362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES); 3734362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3744362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki mMaxDynamicShortcuts = (int) parser.getLong( 3754362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP); 3764362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki final int iconDimensionDp = injectIsLowRamDevice() 3784362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki ? (int) parser.getLong( 3794362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, 3804362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP) 3814362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki : (int) parser.getLong( 3824362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki ConfigConstants.KEY_MAX_ICON_DIMENSION_DP, 3834362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki DEFAULT_MAX_ICON_DIMENSION_DP); 3844362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3854362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki mMaxIconDimension = injectDipToPixel(iconDimensionDp); 3864362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3874362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki mIconPersistFormat = CompressFormat.valueOf( 3884362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT)); 3894362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3904362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki mIconPersistQuality = (int) parser.getLong( 3914362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki ConfigConstants.KEY_ICON_QUALITY, 3924362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki DEFAULT_ICON_PERSIST_QUALITY); 3934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return result; 3954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 3964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 3976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 3984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki String injectShortcutManagerConstants() { 3994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return android.provider.Settings.Global.getString( 4004362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki mContext.getContentResolver(), 4014362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS); 4024362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 4035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 4044362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 4054362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki int injectDipToPixel(int dip) { 4064362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, 4074362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki mContext.getResources().getDisplayMetrics()); 4086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 4105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // === Persisting === 4116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 4126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Nullable 41341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static String parseStringAttribute(XmlPullParser parser, String attribute) { 4146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return parser.getAttributeValue(null, attribute); 4156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 41741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static int parseIntAttribute(XmlPullParser parser, String attribute) { 41841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return (int) parseLongAttribute(parser, attribute); 41941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 42041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 42141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static long parseLongAttribute(XmlPullParser parser, String attribute) { 4226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final String value = parseStringAttribute(parser, attribute); 4236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (TextUtils.isEmpty(value)) { 4246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return 0; 4256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki try { 4276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return Long.parseLong(value); 4286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (NumberFormatException e) { 4296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.e(TAG, "Error parsing long " + value); 4306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return 0; 4316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 4346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Nullable 43541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) { 4366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final String value = parseStringAttribute(parser, attribute); 4376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (TextUtils.isEmpty(value)) { 4386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return null; 4396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return ComponentName.unflattenFromString(value); 4416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 4436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Nullable 44441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static Intent parseIntentAttribute(XmlPullParser parser, String attribute) { 4456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final String value = parseStringAttribute(parser, attribute); 4466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (TextUtils.isEmpty(value)) { 4476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return null; 4486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki try { 4506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return Intent.parseUri(value, /* flags =*/ 0); 4516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (URISyntaxException e) { 4526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.e(TAG, "Error parsing intent", e); 4536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return null; 4546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 45741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException { 4586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (TextUtils.isEmpty(value)) return; 4596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 4606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.startTag(null, tag); 4616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.attribute(null, ATTR_VALUE, value); 4626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.endTag(null, tag); 4636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 46541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException { 4666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki writeTagValue(out, tag, Long.toString(value)); 4676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 4692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException { 4702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (name == null) return; 4712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki writeTagValue(out, tag, name.flattenToString()); 4722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 4732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 47441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle) 4756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki throws IOException, XmlPullParserException { 4766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (bundle == null) return; 4776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 4786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.startTag(null, tag); 4796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki bundle.saveToXml(out); 4806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.endTag(null, tag); 4816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 48341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static void writeAttr(XmlSerializer out, String name, String value) throws IOException { 4846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (TextUtils.isEmpty(value)) return; 4856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 4866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.attribute(null, name, value); 4876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 48941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static void writeAttr(XmlSerializer out, String name, long value) throws IOException { 4906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki writeAttr(out, name, String.valueOf(value)); 4916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 49341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException { 4946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (comp == null) return; 4956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki writeAttr(out, name, comp.flattenToString()); 4966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 4976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 49841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException { 4996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (intent == null) return; 5006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki writeAttr(out, name, intent.toUri(/* flags =*/ 0)); 5026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 5056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki void saveBaseStateLocked() { 5066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final AtomicFile file = getBaseStateFile(); 5076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (DEBUG) { 5086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.i(TAG, "Saving to " + file.getBaseFile()); 5096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki FileOutputStream outs = null; 5126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki try { 5136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki outs = file.startWrite(); 5146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Write to XML 5166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki XmlSerializer out = new FastXmlSerializer(); 5176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.setOutput(outs, StandardCharsets.UTF_8.name()); 5186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.startDocument(null, true); 5196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.startTag(null, TAG_ROOT); 5206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Body. 5226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime); 5236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Epilogue. 5256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.endTag(null, TAG_ROOT); 5266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.endDocument(); 5276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Close. 5296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki file.finishWrite(outs); 5306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (IOException e) { 5316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 5326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki file.failWrite(outs); 5336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void loadBaseStateLocked() { 5376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mRawLastResetTime = 0; 5386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final AtomicFile file = getBaseStateFile(); 5406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (DEBUG) { 5416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.i(TAG, "Loading from " + file.getBaseFile()); 5426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki try (FileInputStream in = file.openRead()) { 5446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki XmlPullParser parser = Xml.newPullParser(); 5456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki parser.setInput(in, StandardCharsets.UTF_8.name()); 5466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki int type; 5486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 5496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (type != XmlPullParser.START_TAG) { 5506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki continue; 5516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final int depth = parser.getDepth(); 5536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Check the root tag 5546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final String tag = parser.getName(); 5556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (depth == 1) { 5566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (!TAG_ROOT.equals(tag)) { 5576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.e(TAG, "Invalid root tag: " + tag); 5586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return; 5596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki continue; 5616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Assume depth == 2 5636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki switch (tag) { 5646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki case TAG_LAST_RESET_TIME: 5656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE); 5666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki break; 5676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki default: 5686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.e(TAG, "Invalid tag: " + tag); 5696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki break; 5706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (FileNotFoundException e) { 5736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Use the default 5746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (IOException|XmlPullParserException e) { 5756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); 5766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mRawLastResetTime = 0; 5786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Adjust the last reset time. 5806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki getLastResetTimeLocked(); 5816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void saveUserLocked(@UserIdInt int userId) { 5846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); 5856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (DEBUG) { 5866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.i(TAG, "Saving to " + path); 5876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 5886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki path.mkdirs(); 5896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final AtomicFile file = new AtomicFile(path); 5906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki FileOutputStream outs = null; 5916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki try { 5926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki outs = file.startWrite(); 5936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Write to XML 5956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki XmlSerializer out = new FastXmlSerializer(); 5966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.setOutput(outs, StandardCharsets.UTF_8.name()); 5976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.startDocument(null, true); 5986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 5993f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki getUserShortcutsLocked(userId).saveToXml(out); 6006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki out.endDocument(); 6026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Close. 6046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki file.finishWrite(outs); 6056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (IOException|XmlPullParserException e) { 6066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 6076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki file.failWrite(outs); 6086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 61141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static IOException throwForInvalidTag(int depth, String tag) throws IOException { 6126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth)); 6136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Nullable 6163f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki private UserShortcuts loadUserLocked(@UserIdInt int userId) { 6176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); 6186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (DEBUG) { 6196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.i(TAG, "Loading from " + path); 6206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final AtomicFile file = new AtomicFile(path); 6226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final FileInputStream in; 6246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki try { 6256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki in = file.openRead(); 6266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (FileNotFoundException e) { 6276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (DEBUG) { 6286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.i(TAG, "Not found " + path); 6296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return null; 6316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6323f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki UserShortcuts ret = null; 6336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki try { 6346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki XmlPullParser parser = Xml.newPullParser(); 6356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki parser.setInput(in, StandardCharsets.UTF_8.name()); 6366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki int type; 6386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 6396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (type != XmlPullParser.START_TAG) { 6406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki continue; 6416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final int depth = parser.getDepth(); 6436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final String tag = parser.getName(); 6456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (DEBUG_LOAD) { 6466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.d(TAG, String.format("depth=%d type=%d name=%s", 6476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki depth, type, tag)); 6486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6493f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki if ((depth == 1) && TAG_USER.equals(tag)) { 6503f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki ret = UserShortcuts.loadFromXml(parser, userId); 6513f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki continue; 6526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki throwForInvalidTag(depth, tag); 6546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return ret; 6566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (IOException|XmlPullParserException e) { 6576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); 6586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return null; 6596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } finally { 6606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki IoUtils.closeQuietly(in); 6616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // TODO Actually make it async. 6656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void scheduleSaveBaseState() { 6666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 6676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki saveBaseStateLocked(); 6686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // TODO Actually make it async. 6722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki void scheduleSaveUser(@UserIdInt int userId) { 6736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 6746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki saveUserLocked(userId); 6756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** Return the last reset time. */ 6796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki long getLastResetTimeLocked() { 6806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki updateTimes(); 6816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return mRawLastResetTime; 6826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** Return the next reset time. */ 6856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki long getNextResetTimeLocked() { 6866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki updateTimes(); 6876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return mRawLastResetTime + mResetInterval; 6886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 6896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 6916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Update the last reset time. 6926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 6936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void updateTimes() { 6946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final long now = injectCurrentTimeMillis(); 6966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final long prevLastResetTime = mRawLastResetTime; 6986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 6996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (mRawLastResetTime == 0) { // first launch. 7006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // TODO Randomize?? 7016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mRawLastResetTime = now; 7026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } else if (now < mRawLastResetTime) { 7036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Clock rewound. 7046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // TODO Randomize?? 7056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mRawLastResetTime = now; 7066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } else { 7076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // TODO Do it properly. 7086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki while ((mRawLastResetTime + mResetInterval) <= now) { 7096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mRawLastResetTime += mResetInterval; 7106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (prevLastResetTime != mRawLastResetTime) { 7136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki scheduleSaveBaseState(); 7146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 7176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** Return the per-user state. */ 7186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @GuardedBy("mLock") 7196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @NonNull 7203f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki private UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) { 7213f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki UserShortcuts userPackages = mUsers.get(userId); 7226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (userPackages == null) { 7236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki userPackages = loadUserLocked(userId); 7246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (userPackages == null) { 7253f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki userPackages = new UserShortcuts(userId); 7266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7273f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki mUsers.put(userId, userPackages); 7286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return userPackages; 7306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 7326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** Return the per-user per-package state. */ 7336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @GuardedBy("mLock") 7346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @NonNull 7356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private PackageShortcuts getPackageShortcutsLocked( 7366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @NonNull String packageName, @UserIdInt int userId) { 7373f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki final UserShortcuts userPackages = getUserShortcutsLocked(userId); 7383f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki PackageShortcuts shortcuts = userPackages.getPackages().get(packageName); 7396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (shortcuts == null) { 7405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcuts = new PackageShortcuts(userId, packageName); 7413f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki userPackages.getPackages().put(packageName, shortcuts); 7426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return shortcuts; 7446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 7456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 7466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // === Caller validation === 7476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 7485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) { 7495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (shortcut.getBitmapPath() != null) { 7505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (DEBUG) { 7515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.d(TAG, "Removing " + shortcut.getBitmapPath()); 7525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 7535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki new File(shortcut.getBitmapPath()).delete(); 7545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 7555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.setBitmapPath(null); 7565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.setIconResourceId(0); 7575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES); 7585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 7595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 7605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 7615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki @VisibleForTesting 7625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki static class FileOutputStreamWithPath extends FileOutputStream { 7635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki private final File mFile; 7645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 7655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki public FileOutputStreamWithPath(File file) throws FileNotFoundException { 7665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki super(file); 7675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki mFile = file; 7685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 7695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 7705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki public File getFile() { 7715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return mFile; 7725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 7735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 7745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 7755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki /** 7765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * Build the cached bitmap filename for a shortcut icon. 7775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * 7785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * The filename will be based on the ID, except certain characters will be escaped. 7795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki */ 7805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki @VisibleForTesting 7815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut) 7825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki throws IOException { 7835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final File packagePath = new File(getUserBitmapFilePath(userId), 7845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.getPackageName()); 7855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (!packagePath.isDirectory()) { 7865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki packagePath.mkdirs(); 7875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (!packagePath.isDirectory()) { 7885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki throw new IOException("Unable to create directory " + packagePath); 7895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 7905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki SELinux.restorecon(packagePath); 7915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 7925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 7935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final String baseName = String.valueOf(injectCurrentTimeMillis()); 7945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki for (int suffix = 0;; suffix++) { 7955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png"; 7965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final File file = new File(packagePath, filename); 7975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (!file.exists()) { 7985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (DEBUG) { 7995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.d(TAG, "Saving icon to " + file.getAbsolutePath()); 8005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return new FileOutputStreamWithPath(file); 8025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) { 8075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (shortcut.hasIconFile() || shortcut.hasIconResource()) { 8085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return; 8095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final long token = Binder.clearCallingIdentity(); 8125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki try { 8135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // Clear icon info on the shortcut. 8145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.setIconResourceId(0); 8155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.setBitmapPath(null); 8165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final Icon icon = shortcut.getIcon(); 8185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (icon == null) { 8195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return; // has no icon 8205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Bitmap bitmap = null; 8235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki try { 8245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki switch (icon.getType()) { 8255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki case Icon.TYPE_RESOURCE: { 8265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki injectValidateIconResPackage(shortcut, icon); 8275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.setIconResourceId(icon.getResId()); 8295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES); 8305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return; 8315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki case Icon.TYPE_BITMAP: { 8335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki bitmap = icon.getBitmap(); 8345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki break; 8355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki case Icon.TYPE_URI: { 8375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final Uri uri = ContentProvider.maybeAddUserId(icon.getUri(), userId); 8385504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki try (InputStream is = mContext.getContentResolver().openInputStream(uri)) { 8405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki bitmap = BitmapFactory.decodeStream(is); 8425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } catch (IOException e) { 8445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.e(TAG, "Unable to load icon from " + uri); 8455504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return; 8465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki break; 8485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki default: 8505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // This shouldn't happen because we've already validated the icon, but 8515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // just in case. 8525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki throw ShortcutInfo.getInvalidIconException(); 8535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (bitmap == null) { 8555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.e(TAG, "Null bitmap detected"); 8565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return; 8575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // Shrink and write to the file. 8595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki File path = null; 8605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki try { 8615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut); 8625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki try { 8635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki path = out.getFile(); 8645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shrinkBitmap(bitmap, mMaxIconDimension) 8665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki .compress(mIconPersistFormat, mIconPersistQuality, out); 8675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.setBitmapPath(out.getFile().getAbsolutePath()); 8695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE); 8705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } finally { 8715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki IoUtils.closeQuietly(out); 8725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } catch (IOException|RuntimeException e) { 8745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // STOPSHIP Change wtf to e 8755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e); 8765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (path != null && path.exists()) { 8775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki path.delete(); 8785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } finally { 8815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (bitmap != null) { 8825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki bitmap.recycle(); 8835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // Once saved, we won't use the original icon information, so null it out. 8855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.clearIcon(); 8865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } finally { 8885504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Binder.restoreCallingIdentity(token); 8895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 8915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 8925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // Unfortunately we can't do this check in unit tests because we fake creator package names, 8935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // so override in unit tests. 8945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // TODO CTS this case. 8955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) { 8965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (!shortcut.getPackageName().equals(icon.getResPackage())) { 8975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki throw new IllegalArgumentException( 8985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki "Icon resource must reside in shortcut owner package"); 8995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 9005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 9015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki @VisibleForTesting 9035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki static Bitmap shrinkBitmap(Bitmap in, int maxSize) { 9045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // Original width/height. 9055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final int ow = in.getWidth(); 9065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final int oh = in.getHeight(); 9075504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if ((ow <= maxSize) && (oh <= maxSize)) { 9085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (DEBUG) { 9095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh)); 9105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 9115504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return in; 9125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 9135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final int longerDimension = Math.max(ow, oh); 9145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // New width and height. 9165504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final int nw = ow * maxSize / longerDimension; 9175504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final int nh = oh * maxSize / longerDimension; 9185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (DEBUG) { 9195504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d", 9205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki ow, oh, nw, nh)); 9215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 9225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888); 9245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final Canvas c = new Canvas(scaledBitmap); 9255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final RectF dst = new RectF(0, 0, nw, nh); 9275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null); 9295504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki in.recycle(); 9315504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9325504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return scaledBitmap; 9335504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 9345504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9355504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // === Caller validation === 9365504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 9376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private boolean isCallerSystem() { 9386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final int callingUid = injectBinderCallingUid(); 9396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID); 9406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private boolean isCallerShell() { 9436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final int callingUid = injectBinderCallingUid(); 9446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; 9456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void enforceSystemOrShell() { 9486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkState(isCallerSystem() || isCallerShell(), 9496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki "Caller must be system or shell"); 9506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void enforceShell() { 9536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkState(isCallerShell(), "Caller must be shell"); 9546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) { 9576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkStringNotEmpty(packageName, "packageName"); 9586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (isCallerSystem()) { 9606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return; // no check 9616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final int callingUid = injectBinderCallingUid(); 9646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Otherwise, make sure the arguments are valid. 9666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (UserHandle.getUserId(callingUid) != userId) { 9676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki throw new SecurityException("Invalid user-ID"); 9686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) { 9706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return; // Caller is valid. 9716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki throw new SecurityException("Caller UID= doesn't own " + packageName); 9736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Test overrides it. 9765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) { 9776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki try { 9786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // TODO Is MATCH_UNINSTALLED_PACKAGES correct to get SD card app info? 9806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return mContext.getPackageManager().getPackageUidAsUser(packageName, 9826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE 9835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki | PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); 9846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } catch (NameNotFoundException e) { 9856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return -1; 9866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 9906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}. 9916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 9926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki void enforceMaxDynamicShortcuts(int numShortcuts) { 9936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (numShortcuts > mMaxDynamicShortcuts) { 9946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded"); 9956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 9976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 9986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 9996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Sends a notification to LauncherApps 10006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Write to file 10016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 10026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) { 10036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki notifyListeners(packageName, userId); 10046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki scheduleSaveUser(userId); 10056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 10066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) { 10086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ArrayList<ShortcutChangeListener> copy; 10096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final List<ShortcutInfo> shortcuts = new ArrayList<>(); 10106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 10116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki copy = new ArrayList<>(mListeners); 10126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki getPackageShortcutsLocked(packageName, userId) 10146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki .findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); 10156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 10166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki for (int i = copy.size() - 1; i >= 0; i--) { 10176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki copy.get(i).onShortcutChanged(packageName, shortcuts, userId); 10186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 10196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 10206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 10226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Clean up / validate an incoming shortcut. 10236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Make sure all mandatory fields are set. 10246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Make sure the intent's extras are persistable, and them to set 10256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * {@link ShortcutInfo#mIntentPersistableExtras}. Also clear its extras. 10266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Clear flags. 10275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * 10285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki * TODO Detailed unit tests 10296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 10305504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { 10316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkNotNull(shortcut, "Null shortcut detected"); 10326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (shortcut.getActivityComponent() != null) { 10336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkState( 10346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki shortcut.getPackageName().equals( 10356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki shortcut.getActivityComponent().getPackageName()), 10366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki "Activity package name mismatch"); 10376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 10386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10395504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (!forUpdate) { 10405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.enforceMandatoryFields(); 10415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (shortcut.getIcon() != null) { 10435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki ShortcutInfo.validateIcon(shortcut.getIcon()); 10445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10465504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki validateForXml(shortcut.getId()); 10475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki validateForXml(shortcut.getTitle()); 10485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki validatePersistableBundleForXml(shortcut.getIntentPersistableExtras()); 10495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki validatePersistableBundleForXml(shortcut.getExtras()); 10506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.setFlags(0); 10525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 10545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those 10555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // characters. 10565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 10575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki private static void validatePersistableBundleForXml(PersistableBundle b) { 10585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (b == null || b.size() == 0) { 10595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return; 10606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 10615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki for (String key : b.keySet()) { 10625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki validateForXml(key); 10635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final Object value = b.get(key); 10645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (value == null) { 10655504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki continue; 10665504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } else if (value instanceof String) { 10675504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki validateForXml((String) value); 10685504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } else if (value instanceof String[]) { 10695504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki for (String v : (String[]) value) { 10705504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki validateForXml(v); 10715504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } else if (value instanceof PersistableBundle) { 10735504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki validatePersistableBundleForXml((PersistableBundle) value); 10745504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10755504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10765504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki private static void validateForXml(String s) { 10795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (TextUtils.isEmpty(s)) { 10805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return; 10815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki for (int i = s.length() - 1; i >= 0; i--) { 10835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (!isAllowedInXml(s.charAt(i))) { 10845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki throw new IllegalArgumentException("Unsupported character detected in: " + s); 10855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10875504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 10886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10895504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki private static boolean isAllowedInXml(char c) { 10905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); 10916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 10926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // === APIs === 10946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 10956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 10966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, 10976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @UserIdInt int userId) { 10986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 10996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 11016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final int size = newShortcuts.size(); 11026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 11046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId); 11056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Throttling. 11076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (!ps.tryApiCall(this)) { 11086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return false; 11096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki enforceMaxDynamicShortcuts(size); 11116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Validate the shortcuts. 11136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki for (int i = 0; i < size; i++) { 11145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false); 11156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // First, remove all un-pinned; dynamic shortcuts 11185504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki ps.deleteAllDynamicShortcuts(this); 11196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Then, add/update all. We need to make sure to take over "pinned" flag. 11216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki for (int i = 0; i < size; i++) { 11226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ShortcutInfo newShortcut = newShortcuts.get(i); 11236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); 11246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ps.updateShortcutWithCapping(this, newShortcut); 11256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki userPackageChanged(packageName, userId); 11286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return true; 11296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 11326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, 11336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @UserIdInt int userId) { 11346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 11356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 11375504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final int size = newShortcuts.size(); 11386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 11405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId); 11416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki // Throttling. 11435504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (!ps.tryApiCall(this)) { 11445504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return false; 11456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11475504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki for (int i = 0; i < size; i++) { 11485504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final ShortcutInfo source = newShortcuts.get(i); 11495504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); 11505504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 11515504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final ShortcutInfo target = ps.findShortcutById(source.getId()); 11525504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (target != null) { 11535504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final boolean replacingIcon = (source.getIcon() != null); 11545504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (replacingIcon) { 11555504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki removeIcon(userId, target); 11565504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 11575504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 11585504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki target.copyNonNullFieldsFrom(source); 11595504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 11605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (replacingIcon) { 11615504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki saveIconAndFixUpShortcut(userId, target); 11625504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 11635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 11645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 11656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki userPackageChanged(packageName, userId); 11676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return true; 11696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 11726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut, 11736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @UserIdInt int userId) { 11746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 11756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 11776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId); 11786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Throttling. 11806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (!ps.tryApiCall(this)) { 11816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return false; 11826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Validate the shortcut. 11855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false); 11866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Add it. 11886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); 11896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ps.updateShortcutWithCapping(this, newShortcut); 11906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki userPackageChanged(packageName, userId); 11926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return true; 11946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 11956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 11966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 11976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void deleteDynamicShortcut(String packageName, String shortcutId, 11986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @UserIdInt int userId) { 11996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 12006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided"); 12016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 12035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this, shortcutId); 12046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki userPackageChanged(packageName, userId); 12066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 12096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) { 12106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 12116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 12135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(this); 12146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki userPackageChanged(packageName, userId); 12166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 12196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName, 12206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @UserIdInt int userId) { 12216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 12226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 12236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return getShortcutsWithQueryLocked( 12246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 12256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ShortcutInfo::isDynamic); 12266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 12306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName, 12316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @UserIdInt int userId) { 12326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 12336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 12346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return getShortcutsWithQueryLocked( 12356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 12366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ShortcutInfo::isPinned); 12376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName, 12416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) { 12426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ArrayList<ShortcutInfo> ret = new ArrayList<>(); 12446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags); 12466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return new ParceledListSlice<>(ret); 12486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 12516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId) 12526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki throws RemoteException { 12536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 12546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return mMaxDynamicShortcuts; 12566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 12596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public int getRemainingCallCount(String packageName, @UserIdInt int userId) { 12606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 12616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 12636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return mMaxDailyUpdates 12646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this); 12656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 12696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public long getRateLimitResetTime(String packageName, @UserIdInt int userId) { 12706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki verifyCaller(packageName, userId); 12716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 12736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return getNextResetTimeLocked(); 12746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12775504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki @Override 12785504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki public int getIconMaxDimensions(String packageName, int userId) throws RemoteException { 12795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki synchronized (mLock) { 12805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return mMaxIconDimension; 12815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 12825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 12835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 12846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 12856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Reset all throttling, for developer options and command line. Only system/shell can call it. 12866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 12876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 12886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void resetThrottling() { 12896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki enforceSystemOrShell(); 12906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki resetThrottlingInner(); 12926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 12946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 12956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki void resetThrottlingInner() { 12966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 12976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mRawLastResetTime = injectCurrentTimeMillis(); 12986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 12996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki scheduleSaveBaseState(); 13005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.i(TAG, "ShortcutManager: throttling counter reset"); 13016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 13026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 13032d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // We override this method in unit tests to do a simpler check. 13042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) { 13052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return hasShortcutHostPermissionInner(callingPackage, userId); 13062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13072d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13082d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // This method is extracted so we can directly call this method from unit tests, 13092d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // even when hasShortcutPermission() is overridden. 13102d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki @VisibleForTesting 13112d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { 13122d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki synchronized (mLock) { 13132d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki long start = 0; 13142d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (DEBUG) { 13152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki start = System.currentTimeMillis(); 13162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13182d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki final UserShortcuts user = getUserShortcutsLocked(userId); 13192d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13202d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); 13212d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // Default launcher from package manager. 13232d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki final ComponentName defaultLauncher = injectPackageManagerInternal() 13242d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki .getHomeActivitiesAsUser(allHomeCandidates, userId); 13252d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki ComponentName detected; 13272d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (defaultLauncher != null) { 13282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki detected = defaultLauncher; 13292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (DEBUG) { 13302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki Slog.v(TAG, "Default launcher from PM: " + detected); 13312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } else { 13332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki detected = user.getLauncherComponent(); 13342d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13352d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // TODO: Make sure it's still enabled. 13362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (DEBUG) { 13372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki Slog.v(TAG, "Cached launcher: " + detected); 13382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (detected == null) { 13422d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // If we reach here, that means it's the first check since the user was created, 13432d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // and there's already multiple launchers and there's no default set. 13442d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // Find the system one with the highest priority. 13452d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // (We need to check the priority too because of FallbackHome in Settings.) 13462d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // If there's no system launcher yet, then no one can access shortcuts, until 13472d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // the user explicitly 13482d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki final int size = allHomeCandidates.size(); 13492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13502d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki int lastPriority = Integer.MIN_VALUE; 13512d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki for (int i = 0; i < size; i++) { 13522d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki final ResolveInfo ri = allHomeCandidates.get(i); 13532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (!ri.activityInfo.applicationInfo.isSystemApp()) { 13542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki continue; 13552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (DEBUG) { 13572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d", 13582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki ri.activityInfo.getComponentName(), ri.priority)); 13592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (ri.priority < lastPriority) { 13612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki continue; 13622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki detected = ri.activityInfo.getComponentName(); 13642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki lastPriority = ri.priority; 13652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (DEBUG) { 13682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki long end = System.currentTimeMillis(); 13692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start)); 13702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (detected != null) { 13722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (DEBUG) { 13732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki Slog.v(TAG, "Detected launcher: " + detected); 13742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki user.setLauncherComponent(this, detected); 13762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return detected.getPackageName().equals(callingPackage); 13772d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } else { 13782d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // Default launcher not found. 13792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return false; 13802d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13812d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13822d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 13832d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 13846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 13856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Entry point from {@link LauncherApps}. 13866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 13876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private class LocalService extends ShortcutServiceInternal { 13886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 13896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public List<ShortcutInfo> getShortcuts( 13906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @NonNull String callingPackage, long changedSince, 13916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Nullable String packageName, @Nullable ComponentName componentName, 13926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki int queryFlags, int userId) { 13936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ArrayList<ShortcutInfo> ret = new ArrayList<>(); 13946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final int cloneFlag = 13956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0) 13966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER 13976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO; 13986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 13996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 14006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (packageName != null) { 14016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags, 14026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki userId, ret, cloneFlag); 14036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } else { 14046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ArrayMap<String, PackageShortcuts> packages = 14053f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki getUserShortcutsLocked(userId).getPackages(); 14065504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki for (int i = packages.size() - 1; i >= 0; i--) { 14076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki getShortcutsInnerLocked( 14086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki packages.keyAt(i), 14096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki changedSince, componentName, queryFlags, userId, ret, cloneFlag); 14106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return ret; 14146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 14166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince, 14176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Nullable ComponentName componentName, int queryFlags, 14186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) { 14196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki getPackageShortcutsLocked(packageName, userId).findAll(ret, 14206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki (ShortcutInfo si) -> { 14216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (si.getLastChangedTimestamp() < changedSince) { 14226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return false; 14236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (componentName != null 14256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki && !componentName.equals(si.getActivityComponent())) { 14266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return false; 14276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final boolean matchDynamic = 14296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0) 14306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki && si.isDynamic(); 14316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final boolean matchPinned = 14326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0) 14336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki && si.isPinned(); 14346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return matchDynamic || matchPinned; 14356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki }, cloneFlag); 14366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 14386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 14396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public List<ShortcutInfo> getShortcutInfo( 14406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @NonNull String callingPackage, 14416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @NonNull String packageName, @Nullable List<String> ids, int userId) { 14426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Calling permission must be checked by LauncherAppsImpl. 14436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkStringNotEmpty(packageName, "packageName"); 14446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 14456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size()); 14466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ArraySet<String> idSet = new ArraySet<>(ids); 14476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 14486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki getPackageShortcutsLocked(packageName, userId).findAll(ret, 14496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki (ShortcutInfo si) -> idSet.contains(si.getId()), 14506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); 14516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return ret; 14536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 14556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 14566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName, 14576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @NonNull List<String> shortcutIds, int userId) { 14586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Calling permission must be checked by LauncherAppsImpl. 14596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkStringNotEmpty(packageName, "packageName"); 14606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Preconditions.checkNotNull(shortcutIds, "shortcutIds"); 14616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 14626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 14635504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki getPackageShortcutsLocked(packageName, userId).replacePinned( 14645504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki ShortcutService.this, callingPackage, shortcutIds); 14656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki userPackageChanged(packageName, userId); 14676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 14696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 14706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public Intent createShortcutIntent(@NonNull String callingPackage, 147143204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki @NonNull String packageName, @NonNull String shortcutId, int userId) { 14726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Calling permission must be checked by LauncherAppsImpl. 147343204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty"); 147443204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); 14756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 14766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 14776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final ShortcutInfo fullShortcut = 147843204b8357d781f284037fb8b7b7050ed04a2103Makoto Onuki getPackageShortcutsLocked(packageName, userId) 14795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki .findShortcutById(shortcutId); 14805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return fullShortcut == null ? null : fullShortcut.getIntent(); 14816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 14846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 14856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void addListener(@NonNull ShortcutChangeListener listener) { 14866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 14876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki mListeners.add(Preconditions.checkNotNull(listener)); 14886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 14905504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 14915504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki @Override 14925504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki public int getShortcutIconResId(@NonNull String callingPackage, 14935504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki @NonNull ShortcutInfo shortcut, int userId) { 14945504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Preconditions.checkNotNull(shortcut, "shortcut"); 14955504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 14965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki synchronized (mLock) { 14975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final ShortcutInfo shortcutInfo = getPackageShortcutsLocked( 14985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki shortcut.getPackageName(), userId).findShortcutById(shortcut.getId()); 14995504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return (shortcutInfo != null && shortcutInfo.hasIconResource()) 15005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki ? shortcutInfo.getIconResourceId() : 0; 15015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 15025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 15035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 15045504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki @Override 15055504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage, 150634d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki @NonNull ShortcutInfo shortcutIn, int userId) { 150734d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki Preconditions.checkNotNull(shortcutIn, "shortcut"); 15085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 15095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki synchronized (mLock) { 15105504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final ShortcutInfo shortcutInfo = getPackageShortcutsLocked( 151134d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId()); 15125504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki if (shortcutInfo == null || !shortcutInfo.hasIconFile()) { 15135504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return null; 15145504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 15155504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki try { 151634d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki if (shortcutInfo.getBitmapPath() == null) { 151734d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki Slog.w(TAG, "null bitmap detected in getShortcutIconFd()"); 151834d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki return null; 151934d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki } 15205504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return ParcelFileDescriptor.open( 15215504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki new File(shortcutInfo.getBitmapPath()), 15225504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki ParcelFileDescriptor.MODE_READ_ONLY); 15235504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } catch (FileNotFoundException e) { 15245504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath()); 15255504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return null; 15265504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 15275504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 15285504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 15292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 15302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki @Override 15312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) { 15322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId); 15332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 15346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 15356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 15366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // === Dump === 15376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 15386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 15396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 15406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 15416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki != PackageManager.PERMISSION_GRANTED) { 15426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println("Permission Denial: can't dump UserManager from from pid=" 15436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki + Binder.getCallingPid() 15446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki + ", uid=" + Binder.getCallingUid() 15456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki + " without permission " 15466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki + android.Manifest.permission.DUMP); 15476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return; 15486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 15496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki dumpInner(pw); 15506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 15516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 15526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 15536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki void dumpInner(PrintWriter pw) { 15546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 15556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final long now = injectCurrentTimeMillis(); 15566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print("Now: ["); 15576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(now); 15586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print("] "); 15596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(formatTime(now)); 15605504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 15616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(" Raw last reset: ["); 15626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(mRawLastResetTime); 15636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print("] "); 15646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(formatTime(mRawLastResetTime)); 15656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 15666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final long last = getLastResetTimeLocked(); 15676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(" Last reset: ["); 15686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(last); 15696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print("] "); 15706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(formatTime(last)); 15716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 15725504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki final long next = getNextResetTimeLocked(); 15736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(" Next reset: ["); 15746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(next); 15756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print("] "); 15766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.print(formatTime(next)); 15776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println(); 15786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 15795504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki pw.print(" Max icon dim: "); 15805504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki pw.print(mMaxIconDimension); 15815504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki pw.print(" Icon format: "); 15825504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki pw.print(mIconPersistFormat); 15835504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki pw.print(" Icon quality: "); 15845504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki pw.print(mIconPersistQuality); 15855504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki pw.println(); 15865504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 15876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 15883f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki for (int i = 0; i < mUsers.size(); i++) { 15893f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki pw.println(); 15903f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki mUsers.valueAt(i).dump(this, pw, " "); 15916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 15926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 15936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 15946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 159541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki static String formatTime(long time) { 15966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki Time tobj = new Time(); 15976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki tobj.set(time); 15986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return tobj.format("%Y-%m-%d %H:%M:%S"); 15996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 16006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 16016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // === Shell support === 16026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 16036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 16046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 16056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki String[] args, ResultReceiver resultReceiver) throws RemoteException { 16066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 16076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki enforceShell(); 16086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 16096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver); 16106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 16116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 16122d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki static class CommandException extends Exception { 16132d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki public CommandException(String message) { 16142d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki super(message); 16152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 16162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 16172d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 16186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki /** 16196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Handle "adb shell cmd". 16206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */ 16216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private class MyShellCommand extends ShellCommand { 16222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 16232d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private int mUserId = UserHandle.USER_SYSTEM; 16242d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 16252d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void parseOptions(boolean takeUser) 16262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki throws CommandException { 16272d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki String opt; 16282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki while ((opt = getNextOption()) != null) { 16292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki switch (opt) { 16302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case "--user": 16312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (takeUser) { 16322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki mUserId = UserHandle.parseUserArg(getNextArgRequired()); 16332d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki break; 16342d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 16352d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // fallthrough 16362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki default: 16372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki throw new CommandException("Unknown option: " + opt); 16382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 16392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 16402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 16412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 16426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 16436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public int onCommand(String cmd) { 16446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki if (cmd == null) { 16456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return handleDefaultCommands(cmd); 16466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 16476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final PrintWriter pw = getOutPrintWriter(); 16482d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki try { 16492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki switch (cmd) { 16502d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case "reset-package-throttling": 16512d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki handleResetPackageThrottling(); 16522d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki break; 16532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case "reset-throttling": 16542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki handleResetThrottling(); 16552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki break; 16562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case "override-config": 16572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki handleOverrideConfig(); 16582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki break; 16592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case "reset-config": 16602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki handleResetConfig(); 16612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki break; 16622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case "clear-default-launcher": 16632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki handleClearDefaultLauncher(); 16642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki break; 16652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case "get-default-launcher": 16662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki handleGetDefaultLauncher(); 16672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki break; 16682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case "refresh-default-launcher": 16692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki handleRefreshDefaultLauncher(); 16702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki break; 16712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki default: 16722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return handleDefaultCommands(cmd); 16732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 16742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } catch (CommandException e) { 16752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println("Error: " + e.getMessage()); 16762d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return 1; 16774362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 16782d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println("Success"); 16792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return 0; 16806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 16816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 16826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @Override 16836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki public void onHelp() { 16846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final PrintWriter pw = getOutPrintWriter(); 16856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println("Usage: cmd shortcut COMMAND [options ...]"); 16866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println(); 16876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE"); 16886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println(" Reset throttling for a package"); 16896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println(); 16906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println("cmd shortcut reset-throttling"); 16916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println(" Reset throttling for all packages and users"); 16926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki pw.println(); 16934362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki pw.println("cmd shortcut override-config CONFIG"); 16944362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki pw.println(" Override the configuration for testing (will last until reboot)"); 16954362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki pw.println(); 16964362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki pw.println("cmd shortcut reset-config"); 16974362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki pw.println(" Reset the configuration set with \"update-config\""); 16984362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki pw.println(); 16992d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println("cmd shortcut clear-default-launcher [--user USER_ID]"); 17002d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println(" Clear the cached default launcher"); 17012d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println(); 17022d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println("cmd shortcut get-default-launcher [--user USER_ID]"); 17032d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println(" Show the cached default launcher"); 17042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println(); 17052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]"); 17062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println(" Refresh the cached default launcher"); 17072d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println(); 17086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 17096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki private int handleResetThrottling() { 17116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki resetThrottling(); 17126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return 0; 17136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 17146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17152d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void handleResetPackageThrottling() throws CommandException { 17162d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki parseOptions(/* takeUser =*/ true); 17176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki final String packageName = getNextArgRequired(); 17196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki synchronized (mLock) { 17212d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine(); 17222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki saveUserLocked(mUserId); 17236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 17246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 17254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 17262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void handleOverrideConfig() throws CommandException { 17274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki final String config = getNextArgRequired(); 17284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 17294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki synchronized (mLock) { 17304362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki if (!updateConfigurationLocked(config)) { 17312d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki throw new CommandException("override-config failed. See logcat for details."); 17324362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 17334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 17344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 17354362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 17362d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void handleResetConfig() { 17374362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki synchronized (mLock) { 17384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki loadConfigurationLocked(); 17394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 17402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 17412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17422d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void clearLauncher() { 17432d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki synchronized (mLock) { 17442d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki getUserShortcutsLocked(mUserId).setLauncherComponent( 17452d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki ShortcutService.this, null); 17462d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 17472d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 17482d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17492d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void showLauncher() { 17502d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki synchronized (mLock) { 17512d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki // This ensures to set the cached launcher. Package name doesn't matter. 17522d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki hasShortcutHostPermissionInner("-", mUserId); 17532d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17542d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki getOutPrintWriter().println("Launcher: " 17552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki + getUserShortcutsLocked(mUserId).getLauncherComponent()); 17562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 17572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 17582d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17592d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void handleClearDefaultLauncher() throws CommandException { 17602d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki parseOptions(/* takeUser =*/ true); 17612d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17622d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki clearLauncher(); 17632d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 17642d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17652d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void handleGetDefaultLauncher() throws CommandException { 17662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki parseOptions(/* takeUser =*/ true); 17672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17682d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki showLauncher(); 17692d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 17702d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17712d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private void handleRefreshDefaultLauncher() throws CommandException { 17722d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki parseOptions(/* takeUser =*/ true); 17732d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 17742d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki clearLauncher(); 17752d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki showLauncher(); 17764362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 17776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 17786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // === Unit test support === 17806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Injection point. 17826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki long injectCurrentTimeMillis() { 17836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return System.currentTimeMillis(); 17846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 17856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki // Injection point. 17876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki int injectBinderCallingUid() { 17886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return getCallingUid(); 17896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 17906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki File injectSystemDataPath() { 17926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki return Environment.getDataSystemDirectory(); 17936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 17946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 17956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki File injectUserDataPath(@UserIdInt int userId) { 17965504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER); 17975504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 17985504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 17994362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 18005504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki boolean injectIsLowRamDevice() { 18015504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return ActivityManager.isLowRamDeviceStatic(); 18025504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 18035504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 18042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki PackageManagerInternal injectPackageManagerInternal() { 18052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return mPackageManagerInternal; 18062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 18072d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 18085504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki File getUserBitmapFilePath(@UserIdInt int userId) { 18095504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS); 18106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 18116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 18126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 18133f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki SparseArray<UserShortcuts> getShortcutsForTest() { 18143f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki return mUsers; 18156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 18166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 18176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 18184362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki int getMaxDynamicShortcutsForTest() { 18194362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return mMaxDynamicShortcuts; 18204362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 18214362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 18224362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 18234362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki int getMaxDailyUpdatesForTest() { 18244362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return mMaxDailyUpdates; 18254362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki } 18264362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki 18274362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki @VisibleForTesting 18284362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki long getResetIntervalForTest() { 18294362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return mResetInterval; 18306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 18316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 18326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 18334362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki int getMaxIconDimensionForTest() { 18344362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return mMaxIconDimension; 18356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 18366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki 18376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki @VisibleForTesting 18384362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki CompressFormat getIconPersistFormatForTest() { 18394362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return mIconPersistFormat; 18405504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki } 18415504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki 18425504622fb01ab9774b5e73d05f86ee03a8b68ab7Makoto Onuki @VisibleForTesting 18434362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki int getIconPersistQualityForTest() { 18444362a66dba0b4cfa9fadb6c8af10c590e4ba880dMakoto Onuki return mIconPersistQuality; 18456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki } 184641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 184741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @VisibleForTesting 184841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) { 184941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki synchronized (mLock) { 185041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return getPackageShortcutsLocked(packageName, userId).findShortcutById(shortcutId); 185141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 185241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 185341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki} 185441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 18552d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki/** 18562d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki * Per-user information. 18572d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki */ 18583f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onukiclass UserShortcuts { 18593f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki private static final String TAG = ShortcutService.TAG; 18603f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18613f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki @UserIdInt 18623f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki final int mUserId; 18633f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18643f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>(); 18653f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18662d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki private ComponentName mLauncherComponent; 18672d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 18683f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki public UserShortcuts(int userId) { 18693f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki mUserId = userId; 18703f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 18713f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18723f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki public ArrayMap<String, PackageShortcuts> getPackages() { 18733f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki return mPackages; 18743f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 18753f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18763f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { 18773f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki out.startTag(null, ShortcutService.TAG_USER); 18783f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18792d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki ShortcutService.writeTagValue(out, ShortcutService.TAG_LAUNCHER, 18802d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki mLauncherComponent); 18813f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18822d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki for (int i = 0; i < mPackages.size(); i++) { 18832d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki mPackages.valueAt(i).saveToXml(out); 18843f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 18853f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18863f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki out.endTag(null, ShortcutService.TAG_USER); 18873f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 18883f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18893f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki public static UserShortcuts loadFromXml(XmlPullParser parser, int userId) 18903f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki throws IOException, XmlPullParserException { 18913f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki final UserShortcuts ret = new UserShortcuts(userId); 18923f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 18933f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki final int outerDepth = parser.getDepth(); 18943f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki int type; 18953f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 18963f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 18973f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki if (type != XmlPullParser.START_TAG) { 18983f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki continue; 18993f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 19003f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki final int depth = parser.getDepth(); 19013f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki final String tag = parser.getName(); 19023f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki switch (tag) { 19032d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki case ShortcutService.TAG_LAUNCHER: 19042d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute( 19052d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki parser, ShortcutService.ATTR_VALUE); 19062d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki continue; 19073f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki case ShortcutService.TAG_PACKAGE: 19083f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId); 19093f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 19103f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki // Don't use addShortcut(), we don't need to save the icon. 19113f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki ret.getPackages().put(shortcuts.mPackageName, shortcuts); 19123f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki continue; 19133f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 19143f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki throw ShortcutService.throwForInvalidTag(depth, tag); 19153f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 19163f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki return ret; 19173f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 19183f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 19192d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki public ComponentName getLauncherComponent() { 19202d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return mLauncherComponent; 19212d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 19222d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 19232d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) { 19242d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki if (Objects.equal(mLauncherComponent, launcherComponent)) { 19252d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki return; 19262d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 19272d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki mLauncherComponent = launcherComponent; 19282d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki s.scheduleSaveUser(mUserId); 19292d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki } 19302d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 19313f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) { 19322d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.print(prefix); 19333f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki pw.print("User: "); 19343f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki pw.print(mUserId); 19353f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki pw.println(); 19363f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 19372d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.print(prefix); 19382d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.print(" "); 19392d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.print("Default launcher: "); 19402d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.print(mLauncherComponent); 19412d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki pw.println(); 19422d5b465fa9235e66ec176f6d6ffaaa0c18143e41Makoto Onuki 19433f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki for (int i = 0; i < mPackages.size(); i++) { 19443f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki mPackages.valueAt(i).dump(s, pw, prefix + " "); 19453f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 19463f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki } 19473f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki} 19483f4b1ca97ad7c31bdbe2ba29264841fb58683e81Makoto Onuki 194941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki/** 195041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * All the information relevant to shortcuts from a single package (per-user). 195141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 195241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onukiclass PackageShortcuts { 195341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki private static final String TAG = ShortcutService.TAG; 195441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 195541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @UserIdInt 195641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final int mUserId; 195741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 195841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @NonNull 195941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final String mPackageName; 196041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 196141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 196241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * All the shortcuts from the package, keyed on IDs. 196341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 196441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>(); 196541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 196641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 196741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * # of dynamic shortcuts. 196841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 196941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki private int mDynamicShortcutCount = 0; 197041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 197141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 197241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * # of times the package has called rate-limited APIs. 197341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 197441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki private int mApiCallCount; 197541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 197641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 197741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * When {@link #mApiCallCount} was reset last time. 197841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 197941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki private long mLastResetTime; 198041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 198141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki PackageShortcuts(int userId, String packageName) { 198241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mUserId = userId; 198341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mPackageName = packageName; 198441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 198541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 198641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 198741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @Nullable 198841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public ShortcutInfo findShortcutById(String id) { 198941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return mShortcuts.get(id); 199041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 199141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 199241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki private ShortcutInfo deleteShortcut(@NonNull ShortcutService s, 199341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @NonNull String id) { 199441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ShortcutInfo shortcut = mShortcuts.remove(id); 199541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (shortcut != null) { 199641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki s.removeIcon(mUserId, shortcut); 199741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED); 199841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 199941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return shortcut; 200041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 200141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 200241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) { 200341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki deleteShortcut(s, newShortcut.getId()); 200441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki s.saveIconAndFixUpShortcut(mUserId, newShortcut); 200541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mShortcuts.put(newShortcut.getId(), newShortcut); 200641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 200741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 200841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 200941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * Add a shortcut, or update one with the same ID, with taking over existing flags. 201041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * 201141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * It checks the max number of dynamic shortcuts. 201241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 201341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 201441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public void updateShortcutWithCapping(@NonNull ShortcutService s, 201541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @NonNull ShortcutInfo newShortcut) { 201641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); 201741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 201841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki int oldFlags = 0; 201941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki int newDynamicCount = mDynamicShortcutCount; 202041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 202141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (oldShortcut != null) { 202241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki oldFlags = oldShortcut.getFlags(); 202341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (oldShortcut.isDynamic()) { 202441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki newDynamicCount--; 202541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 202641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 202741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (newShortcut.isDynamic()) { 202841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki newDynamicCount++; 202941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 203041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // Make sure there's still room. 203141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki s.enforceMaxDynamicShortcuts(newDynamicCount); 203241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 203341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // Okay, make it dynamic and add. 203441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki newShortcut.addFlags(oldFlags); 203541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 203641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki addShortcut(s, newShortcut); 203741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mDynamicShortcutCount = newDynamicCount; 203841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 203941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 204041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 204141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * Remove all shortcuts that aren't pinned nor dynamic. 204241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 204341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki private void removeOrphans(@NonNull ShortcutService s) { 204441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ArrayList<String> removeList = null; // Lazily initialize. 204541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 204641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki for (int i = mShortcuts.size() - 1; i >= 0; i--) { 204741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ShortcutInfo si = mShortcuts.valueAt(i); 204841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 204941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (si.isPinned() || si.isDynamic()) continue; 205041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 205141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (removeList == null) { 205241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki removeList = new ArrayList<>(); 205341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 205441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki removeList.add(si.getId()); 205541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 205641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (removeList != null) { 205741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki for (int i = removeList.size() - 1 ; i >= 0; i--) { 205841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki deleteShortcut(s, removeList.get(i)); 205941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 206041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 206141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 206241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 206341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 206441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) { 206541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki for (int i = mShortcuts.size() - 1; i >= 0; i--) { 206641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC); 206741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 206841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki removeOrphans(s); 206941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mDynamicShortcutCount = 0; 207041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 207141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 207241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 207341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) { 207441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId); 207541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 207641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (oldShortcut == null) { 207741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return; 207841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 207941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (oldShortcut.isDynamic()) { 208041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mDynamicShortcutCount--; 208141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 208241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (oldShortcut.isPinned()) { 208341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC); 208441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } else { 208541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki deleteShortcut(s, shortcutId); 208641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 208741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 208841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 208941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 209041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public void replacePinned(@NonNull ShortcutService s, String launcherPackage, 209141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki List<String> shortcutIds) { 209241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 209341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // TODO Should be per launcherPackage. 209441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 209541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // First, un-pin all shortcuts 209641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki for (int i = mShortcuts.size() - 1; i >= 0; i--) { 209741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED); 209841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 209941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 210041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // Then pin ALL 210141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki for (int i = shortcutIds.size() - 1; i >= 0; i--) { 210241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i)); 210341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (shortcut != null) { 210441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki shortcut.addFlags(ShortcutInfo.FLAG_PINNED); 210541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 210641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 210741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 210841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki removeOrphans(s); 210941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 211041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 211141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 211241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * Number of calls that the caller has made, since the last reset. 211341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 211441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 211541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public int getApiCallCount(@NonNull ShortcutService s) { 211641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final long last = s.getLastResetTimeLocked(); 211741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 211841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final long now = s.injectCurrentTimeMillis(); 211941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (mLastResetTime > now) { 212041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // Clock rewound. // TODO Test it 212141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mLastResetTime = now; 212241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 212341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 212441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // If not reset yet, then reset. 212541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (mLastResetTime < last) { 212641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mApiCallCount = 0; 212741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mLastResetTime = last; 212841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 212941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return mApiCallCount; 213041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 213141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 213241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 213341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount} 213441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * and return true. Otherwise just return false. 213541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 213641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 213741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public boolean tryApiCall(@NonNull ShortcutService s) { 213841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (getApiCallCount(s) >= s.mMaxDailyUpdates) { 213941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return false; 214041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 214141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mApiCallCount++; 214241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return true; 214341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 214441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 214541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 214641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public void resetRateLimitingForCommandLine() { 214741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mApiCallCount = 0; 214841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki mLastResetTime = 0; 214941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 215041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 215141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki /** 215241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki * Find all shortcuts that match {@code query}. 215341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki */ 215441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @GuardedBy("mLock") 215541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public void findAll(@NonNull List<ShortcutInfo> result, 215641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki @Nullable Predicate<ShortcutInfo> query, int cloneFlag) { 215741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki for (int i = 0; i < mShortcuts.size(); i++) { 215841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ShortcutInfo si = mShortcuts.valueAt(i); 215941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (query == null || query.test(si)) { 216041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki result.add(si.clone(cloneFlag)); 216141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 216241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 216341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 216441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 216541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) { 216641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(prefix); 216741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print("Package: "); 216841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(mPackageName); 216941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.println(); 217041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 217141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(prefix); 217241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(" "); 217341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print("Calls: "); 217441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(getApiCallCount(s)); 217541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.println(); 217641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 217741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // This should be after getApiCallCount(), which may update it. 217841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(prefix); 217941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(" "); 218041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print("Last reset: ["); 218141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(mLastResetTime); 218241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print("] "); 218341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(s.formatTime(mLastResetTime)); 218441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.println(); 218541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 218641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.println(" Shortcuts:"); 218741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki long totalBitmapSize = 0; 218841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts; 218941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final int size = shortcuts.size(); 219041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki for (int i = 0; i < size; i++) { 219141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ShortcutInfo si = shortcuts.valueAt(i); 219241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(" "); 219341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.println(si.toInsecureString()); 219434d1c919fd4f6b9f1adb7d62dd16ba1fa8e91c79Makoto Onuki if (si.getBitmapPath() != null) { 219541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final long len = new File(si.getBitmapPath()).length(); 219641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(" "); 219741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print("bitmap size="); 219841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.println(len); 219941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 220041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki totalBitmapSize += len; 220141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 220241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 220341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(prefix); 220441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(" "); 220541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print("Total bitmap size: "); 220641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(totalBitmapSize); 220741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(" ("); 220841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize)); 220941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki pw.println(")"); 221041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 221141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 221241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException { 221341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki out.startTag(null, ShortcutService.TAG_PACKAGE); 221441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 221541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_NAME, mPackageName); 221641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_DYNAMIC_COUNT, mDynamicShortcutCount); 221741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_CALL_COUNT, mApiCallCount); 221841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_LAST_RESET, mLastResetTime); 221941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 222041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final int size = mShortcuts.size(); 222141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki for (int j = 0; j < size; j++) { 222241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki saveShortcut(out, mShortcuts.valueAt(j)); 222341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 222441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 222541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki out.endTag(null, ShortcutService.TAG_PACKAGE); 222641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 222741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 222841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki private static void saveShortcut(XmlSerializer out, ShortcutInfo si) 222941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki throws IOException, XmlPullParserException { 223041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki out.startTag(null, ShortcutService.TAG_SHORTCUT); 223141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_ID, si.getId()); 223241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // writeAttr(out, "package", si.getPackageName()); // not needed 223341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_ACTIVITY, si.getActivityComponent()); 223441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // writeAttr(out, "icon", si.getIcon()); // We don't save it. 223541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_TITLE, si.getTitle()); 223641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_INTENT, si.getIntentNoExtras()); 223741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_WEIGHT, si.getWeight()); 223841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_TIMESTAMP, 223941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki si.getLastChangedTimestamp()); 224041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_FLAGS, si.getFlags()); 224141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_ICON_RES, si.getIconResourceId()); 224241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeAttr(out, ShortcutService.ATTR_BITMAP_PATH, si.getBitmapPath()); 224341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 224441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeTagExtra(out, ShortcutService.TAG_INTENT_EXTRAS, 224541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki si.getIntentPersistableExtras()); 224641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.writeTagExtra(out, ShortcutService.TAG_EXTRAS, si.getExtras()); 224741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 224841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki out.endTag(null, ShortcutService.TAG_SHORTCUT); 224941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 225041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 225141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId) 225241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki throws IOException, XmlPullParserException { 225341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 225441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final String packageName = ShortcutService.parseStringAttribute(parser, 225541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.ATTR_NAME); 225641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 225741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final PackageShortcuts ret = new PackageShortcuts(userId, packageName); 225841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 225941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ret.mDynamicShortcutCount = 226041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_DYNAMIC_COUNT); 226141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ret.mApiCallCount = 226241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_CALL_COUNT); 226341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ret.mLastResetTime = 226441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_LAST_RESET); 226541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 226641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final int outerDepth = parser.getDepth(); 226741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki int type; 226841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 226941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 227041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (type != XmlPullParser.START_TAG) { 227141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki continue; 227241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 227341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final int depth = parser.getDepth(); 227441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final String tag = parser.getName(); 227541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki switch (tag) { 227641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki case ShortcutService.TAG_SHORTCUT: 227741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final ShortcutInfo si = parseShortcut(parser, packageName); 227841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 227941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // Don't use addShortcut(), we don't need to save the icon. 228041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ret.mShortcuts.put(si.getId(), si); 228141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki continue; 228241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 228341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki throw ShortcutService.throwForInvalidTag(depth, tag); 228441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 228541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return ret; 228641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 228741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 228841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName) 228941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki throws IOException, XmlPullParserException { 229041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki String id; 229141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ComponentName activityComponent; 229241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki // Icon icon; 229341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki String title; 229441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki Intent intent; 229541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki PersistableBundle intentPersistableExtras = null; 229641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki int weight; 229741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki PersistableBundle extras = null; 229841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki long lastChangedTimestamp; 229941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki int flags; 230041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki int iconRes; 230141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki String bitmapPath; 230241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 230341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki id = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_ID); 230441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki activityComponent = ShortcutService.parseComponentNameAttribute(parser, 230541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.ATTR_ACTIVITY); 230641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki title = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_TITLE); 230741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki intent = ShortcutService.parseIntentAttribute(parser, ShortcutService.ATTR_INTENT); 230841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki weight = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_WEIGHT); 230941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser, 231041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki ShortcutService.ATTR_TIMESTAMP); 231141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki flags = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_FLAGS); 231241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki iconRes = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_ICON_RES); 231341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki bitmapPath = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_BITMAP_PATH); 231441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki 231541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final int outerDepth = parser.getDepth(); 231641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki int type; 231741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 231841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 231941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (type != XmlPullParser.START_TAG) { 232041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki continue; 232141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 232241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final int depth = parser.getDepth(); 232341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki final String tag = parser.getName(); 232441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki if (ShortcutService.DEBUG_LOAD) { 232541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki Slog.d(TAG, String.format(" depth=%d type=%d name=%s", 232641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki depth, type, tag)); 232741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 232841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki switch (tag) { 232941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki case ShortcutService.TAG_INTENT_EXTRAS: 233041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki intentPersistableExtras = PersistableBundle.restoreFromXml(parser); 233141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki continue; 233241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki case ShortcutService.TAG_EXTRAS: 233341066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki extras = PersistableBundle.restoreFromXml(parser); 233441066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki continue; 233541066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 233641066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki throw ShortcutService.throwForInvalidTag(depth, tag); 233741066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 233841066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki return new ShortcutInfo( 233941066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki id, packageName, activityComponent, /* icon =*/ null, title, intent, 234041066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki intentPersistableExtras, weight, extras, lastChangedTimestamp, flags, 234141066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki iconRes, bitmapPath); 234241066a61b912f22dd0342a002b4b5e060719cec9Makoto Onuki } 23436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki} 2344