blob: 6d48a05273cbd7b0040860dbe6cf584980c212d5 [file] [log] [blame]
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.pm;
17
Makoto Onuki7001a612016-05-27 13:24:28 -070018import android.annotation.IntDef;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080019import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.UserIdInt;
Makoto Onuki55046222016-03-08 10:49:47 -080022import android.app.ActivityManager;
Makoto Onuki33525d22016-08-03 15:45:24 -070023import android.app.ActivityManagerInternal;
Makoto Onuki0acbb142016-03-22 17:02:57 -070024import android.app.AppGlobals;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070025import android.app.IUidObserver;
Makoto Onukiac042502016-05-20 16:39:42 -070026import android.app.usage.UsageStatsManagerInternal;
Sunny Goyal87a563e2017-01-01 19:42:45 -080027import android.appwidget.AppWidgetProviderInfo;
Makoto Onuki4e6cef42016-07-13 16:14:01 -070028import android.content.BroadcastReceiver;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080029import android.content.ComponentName;
30import android.content.Context;
31import android.content.Intent;
Makoto Onuki4e6cef42016-07-13 16:14:01 -070032import android.content.IntentFilter;
Makoto Onuki2d895c32016-12-02 15:48:40 -080033import android.content.IntentSender;
34import android.content.IntentSender.SendIntentException;
Makoto Onuki22fcc682016-05-17 14:52:19 -070035import android.content.pm.ActivityInfo;
Makoto Onuki0acbb142016-03-22 17:02:57 -070036import android.content.pm.ApplicationInfo;
37import android.content.pm.IPackageManager;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080038import android.content.pm.IShortcutService;
39import android.content.pm.LauncherApps;
40import android.content.pm.LauncherApps.ShortcutQuery;
Makoto Onuki0acbb142016-03-22 17:02:57 -070041import android.content.pm.PackageInfo;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080042import android.content.pm.PackageManager;
Makoto Onuki20c95f82016-05-11 16:51:01 -070043import android.content.pm.PackageManager.NameNotFoundException;
Makoto Onuki2d5b4652016-03-11 16:09:54 -080044import android.content.pm.PackageManagerInternal;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080045import android.content.pm.ParceledListSlice;
Makoto Onuki2d5b4652016-03-11 16:09:54 -080046import android.content.pm.ResolveInfo;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080047import android.content.pm.ShortcutInfo;
48import android.content.pm.ShortcutServiceInternal;
49import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
Makoto Onuki2d895c32016-12-02 15:48:40 -080050import android.content.pm.UserInfo;
Makoto Onuki157b1622016-06-02 16:13:10 -070051import android.content.res.Resources;
Makoto Onuki22fcc682016-05-17 14:52:19 -070052import android.content.res.XmlResourceParser;
Makoto Onuki55046222016-03-08 10:49:47 -080053import android.graphics.Bitmap;
54import android.graphics.Bitmap.CompressFormat;
Makoto Onuki55046222016-03-08 10:49:47 -080055import android.graphics.Canvas;
56import android.graphics.RectF;
Hyunyoung Song47037462017-05-08 16:51:43 -070057import android.graphics.drawable.AdaptiveIconDrawable;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080058import android.graphics.drawable.Icon;
Makoto Onuki4e6cef42016-07-13 16:14:01 -070059import android.net.Uri;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080060import android.os.Binder;
Makoto Onuki33663282016-08-22 16:19:04 -070061import android.os.Build;
Sunny Goyal4ad6b572017-02-28 11:11:51 -080062import android.os.Bundle;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080063import android.os.Environment;
Makoto Onuki2e210c42016-03-30 08:30:36 -070064import android.os.FileUtils;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080065import android.os.Handler;
Makoto Onuki4e6cef42016-07-13 16:14:01 -070066import android.os.LocaleList;
Makoto Onukiaa8b94a2016-03-17 13:14:05 -070067import android.os.Looper;
Makoto Onuki55046222016-03-08 10:49:47 -080068import android.os.ParcelFileDescriptor;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080069import android.os.PersistableBundle;
70import android.os.Process;
71import android.os.RemoteException;
72import android.os.ResultReceiver;
Makoto Onuki55046222016-03-08 10:49:47 -080073import android.os.SELinux;
Makoto Onukib08790c2016-06-23 14:05:46 -070074import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070075import android.os.ShellCallback;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080076import android.os.ShellCommand;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070077import android.os.SystemClock;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080078import android.os.UserHandle;
Makoto Onukicdc78f72016-03-21 15:47:52 -070079import android.os.UserManager;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080080import android.text.TextUtils;
81import android.text.format.Time;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080082import android.util.ArraySet;
83import android.util.AtomicFile;
Makoto Onuki4362a662016-03-08 18:59:09 -080084import android.util.KeyValueListParser;
Makoto Onukiac042502016-05-20 16:39:42 -070085import android.util.Log;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080086import android.util.Slog;
87import android.util.SparseArray;
Makoto Onuki02f338e2016-07-29 09:40:40 -070088import android.util.SparseBooleanArray;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070089import android.util.SparseIntArray;
90import android.util.SparseLongArray;
Makoto Onuki55046222016-03-08 10:49:47 -080091import android.util.TypedValue;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080092import android.util.Xml;
Makoto Onukib08790c2016-06-23 14:05:46 -070093import android.view.IWindowManager;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080094
95import com.android.internal.annotations.GuardedBy;
96import com.android.internal.annotations.VisibleForTesting;
97import com.android.internal.os.BackgroundThread;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060098import com.android.internal.util.DumpUtils;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080099import com.android.internal.util.FastXmlSerializer;
100import com.android.internal.util.Preconditions;
101import com.android.server.LocalServices;
102import com.android.server.SystemService;
Makoto Onukid99c6f02016-03-28 11:02:54 -0700103import com.android.server.pm.ShortcutUser.PackageWithUser;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800104
105import libcore.io.IoUtils;
106
Makoto Onuki76269922016-07-15 14:58:54 -0700107import org.json.JSONArray;
108import org.json.JSONException;
109import org.json.JSONObject;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800110import org.xmlpull.v1.XmlPullParser;
111import org.xmlpull.v1.XmlPullParserException;
112import org.xmlpull.v1.XmlSerializer;
113
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700114import java.io.BufferedInputStream;
115import java.io.BufferedOutputStream;
116import java.io.ByteArrayInputStream;
117import java.io.ByteArrayOutputStream;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800118import java.io.File;
119import java.io.FileDescriptor;
120import java.io.FileInputStream;
121import java.io.FileNotFoundException;
122import java.io.FileOutputStream;
123import java.io.IOException;
Makoto Onuki55046222016-03-08 10:49:47 -0800124import java.io.InputStream;
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700125import java.io.OutputStream;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800126import java.io.PrintWriter;
Makoto Onuki7001a612016-05-27 13:24:28 -0700127import java.lang.annotation.Retention;
128import java.lang.annotation.RetentionPolicy;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800129import java.net.URISyntaxException;
130import java.nio.charset.StandardCharsets;
131import java.util.ArrayList;
Makoto Onuki22fcc682016-05-17 14:52:19 -0700132import java.util.Collections;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800133import java.util.List;
Makoto Onukic51b2872016-05-04 15:24:50 -0700134import java.util.concurrent.atomic.AtomicBoolean;
Makoto Onuki2e210c42016-03-30 08:30:36 -0700135import java.util.function.Consumer;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800136import java.util.function.Predicate;
137
138/**
139 * TODO:
Makoto Onuki22fcc682016-05-17 14:52:19 -0700140 * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
Makoto Onukib5a012f2016-06-21 11:13:53 -0700141 * -> But TypedValue.applyDimension() doesn't differentiate x and y..?
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800142 *
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700143 * - Detect when already registered instances are passed to APIs again, which might break
Makoto Onukib08790c2016-06-23 14:05:46 -0700144 * internal bitmap handling.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800145 */
146public class ShortcutService extends IShortcutService.Stub {
Makoto Onuki55046222016-03-08 10:49:47 -0800147 static final String TAG = "ShortcutService";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800148
Makoto Onuki7001a612016-05-27 13:24:28 -0700149 static final boolean DEBUG = false; // STOPSHIP if true
Makoto Onuki41066a62016-03-09 16:18:44 -0800150 static final boolean DEBUG_LOAD = false; // STOPSHIP if true
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700151 static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800152
Makoto Onuki4362a662016-03-08 18:59:09 -0800153 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700154 static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
Makoto Onuki4362a662016-03-08 18:59:09 -0800155
156 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700157 static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10;
Makoto Onuki4362a662016-03-08 18:59:09 -0800158
159 @VisibleForTesting
160 static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
161
162 @VisibleForTesting
163 static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
164
165 @VisibleForTesting
166 static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
167
168 @VisibleForTesting
169 static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
170
171 @VisibleForTesting
172 static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800173
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700174 @VisibleForTesting
175 static final int DEFAULT_SAVE_DELAY_MS = 3000;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800176
177 @VisibleForTesting
178 static final String FILENAME_BASE_STATE = "shortcut_service.xml";
179
180 @VisibleForTesting
181 static final String DIRECTORY_PER_USER = "shortcut_service";
182
183 @VisibleForTesting
184 static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
185
Makoto Onuki55046222016-03-08 10:49:47 -0800186 static final String DIRECTORY_BITMAPS = "bitmaps";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800187
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700188 private static final String TAG_ROOT = "root";
189 private static final String TAG_LAST_RESET_TIME = "last_reset_time";
Makoto Onuki55046222016-03-08 10:49:47 -0800190
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700191 private static final String ATTR_VALUE = "value";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800192
Makoto Onukib08790c2016-06-23 14:05:46 -0700193 private static final String LAUNCHER_INTENT_CATEGORY = Intent.CATEGORY_LAUNCHER;
194
Makoto Onuki76269922016-07-15 14:58:54 -0700195 private static final String KEY_SHORTCUT = "shortcut";
196 private static final String KEY_LOW_RAM = "lowRam";
197 private static final String KEY_ICON_SIZE = "iconSize";
198
Makoto Onuki2d895c32016-12-02 15:48:40 -0800199 private static final String DUMMY_MAIN_ACTIVITY = "android.__dummy__";
200
Makoto Onuki4362a662016-03-08 18:59:09 -0800201 @VisibleForTesting
202 interface ConfigConstants {
203 /**
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700204 * Key name for the save delay, in milliseconds. (int)
205 */
206 String KEY_SAVE_DELAY_MILLIS = "save_delay_ms";
207
208 /**
Makoto Onuki4362a662016-03-08 18:59:09 -0800209 * Key name for the throttling reset interval, in seconds. (long)
210 */
211 String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
212
213 /**
214 * Key name for the max number of modifying API calls per app for every interval. (int)
215 */
Makoto Onukib6d35232016-04-04 15:57:17 -0700216 String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval";
Makoto Onuki4362a662016-03-08 18:59:09 -0800217
218 /**
219 * Key name for the max icon dimensions in DP, for non-low-memory devices.
220 */
221 String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
222
223 /**
224 * Key name for the max icon dimensions in DP, for low-memory devices.
225 */
226 String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
227
228 /**
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700229 * Key name for the max dynamic shortcuts per activity. (int)
Makoto Onuki4362a662016-03-08 18:59:09 -0800230 */
231 String KEY_MAX_SHORTCUTS = "max_shortcuts";
232
233 /**
Makoto Onuki41066a62016-03-09 16:18:44 -0800234 * Key name for icon compression quality, 0-100.
Makoto Onuki4362a662016-03-08 18:59:09 -0800235 */
236 String KEY_ICON_QUALITY = "icon_quality";
237
238 /**
239 * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
240 */
241 String KEY_ICON_FORMAT = "icon_format";
242 }
243
Makoto Onuki41066a62016-03-09 16:18:44 -0800244 final Context mContext;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800245
246 private final Object mLock = new Object();
247
Makoto Onukiee6b6e42016-06-29 17:34:02 -0700248 private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
249
Mark Renoufe065f7c2016-11-01 11:48:24 -0400250 // Temporarily reverted to anonymous inner class form due to: b/32554459
251 private static Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED = new Predicate<ResolveInfo>() {
252 public boolean test(ResolveInfo ri) {
253 return !ri.activityInfo.exported;
254 }
255 };
Makoto Onukiee6b6e42016-06-29 17:34:02 -0700256
Mark Renoufe065f7c2016-11-01 11:48:24 -0400257 // Temporarily reverted to anonymous inner class form due to: b/32554459
258 private static Predicate<PackageInfo> PACKAGE_NOT_INSTALLED = new Predicate<PackageInfo>() {
259 public boolean test(PackageInfo pi) {
260 return !isInstalled(pi);
261 }
262 };
Makoto Onukiee6b6e42016-06-29 17:34:02 -0700263
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800264 private final Handler mHandler;
265
266 @GuardedBy("mLock")
267 private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
268
269 @GuardedBy("mLock")
270 private long mRawLastResetTime;
271
272 /**
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -0800273 * User ID -> UserShortcuts
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800274 */
275 @GuardedBy("mLock")
Makoto Onuki31459242016-03-22 11:12:18 -0700276 private final SparseArray<ShortcutUser> mUsers = new SparseArray<>();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800277
278 /**
Makoto Onukib5a012f2016-06-21 11:13:53 -0700279 * Max number of dynamic + manifest shortcuts that each application can have at a time.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800280 */
Makoto Onukib5a012f2016-06-21 11:13:53 -0700281 private int mMaxShortcuts;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800282
283 /**
Makoto Onukib6d35232016-04-04 15:57:17 -0700284 * Max number of updating API calls that each application can make during the interval.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800285 */
Makoto Onukib6d35232016-04-04 15:57:17 -0700286 int mMaxUpdatesPerInterval;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800287
288 /**
289 * Actual throttling-reset interval. By default it's a day.
290 */
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800291 private long mResetInterval;
292
Makoto Onuki55046222016-03-08 10:49:47 -0800293 /**
294 * Icon max width/height in pixels.
295 */
296 private int mMaxIconDimension;
297
Makoto Onuki4362a662016-03-08 18:59:09 -0800298 private CompressFormat mIconPersistFormat;
299 private int mIconPersistQuality;
Makoto Onuki55046222016-03-08 10:49:47 -0800300
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700301 private int mSaveDelayMillis;
302
Makoto Onuki0acbb142016-03-22 17:02:57 -0700303 private final IPackageManager mIPackageManager;
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800304 private final PackageManagerInternal mPackageManagerInternal;
Makoto Onukicdc78f72016-03-21 15:47:52 -0700305 private final UserManager mUserManager;
Makoto Onukiac042502016-05-20 16:39:42 -0700306 private final UsageStatsManagerInternal mUsageStatsManagerInternal;
Makoto Onuki33525d22016-08-03 15:45:24 -0700307 private final ActivityManagerInternal mActivityManagerInternal;
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800308
Makoto Onuki2d895c32016-12-02 15:48:40 -0800309 private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor;
Makoto Onuki475c3652017-05-08 14:29:03 -0700310 private final ShortcutBitmapSaver mShortcutBitmapSaver;
Makoto Onuki2d895c32016-12-02 15:48:40 -0800311
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700312 @GuardedBy("mLock")
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700313 final SparseIntArray mUidState = new SparseIntArray();
314
315 @GuardedBy("mLock")
316 final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray();
317
318 @GuardedBy("mLock")
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700319 private List<Integer> mDirtyUserIds = new ArrayList<>();
320
Makoto Onukic51b2872016-05-04 15:24:50 -0700321 private final AtomicBoolean mBootCompleted = new AtomicBoolean();
322
Makoto Onuki905e8852016-03-28 10:40:58 -0700323 private static final int PACKAGE_MATCH_FLAGS =
324 PackageManager.MATCH_DIRECT_BOOT_AWARE
Makoto Onukib08790c2016-06-23 14:05:46 -0700325 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
326 | PackageManager.MATCH_UNINSTALLED_PACKAGES;
Makoto Onuki905e8852016-03-28 10:40:58 -0700327
Makoto Onuki02f338e2016-07-29 09:40:40 -0700328 @GuardedBy("mLock")
329 final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
330
Makoto Onuki2e210c42016-03-30 08:30:36 -0700331 // Stats
332 @VisibleForTesting
333 interface Stats {
334 int GET_DEFAULT_HOME = 0;
335 int GET_PACKAGE_INFO = 1;
336 int GET_PACKAGE_INFO_WITH_SIG = 2;
337 int GET_APPLICATION_INFO = 3;
338 int LAUNCHER_PERMISSION_CHECK = 4;
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700339 int CLEANUP_DANGLING_BITMAPS = 5;
Makoto Onukib08790c2016-06-23 14:05:46 -0700340 int GET_ACTIVITY_WITH_METADATA = 6;
Makoto Onuki6dd9fb72016-06-01 13:55:54 -0700341 int GET_INSTALLED_PACKAGES = 7;
Makoto Onuki22fcc682016-05-17 14:52:19 -0700342 int CHECK_PACKAGE_CHANGES = 8;
Makoto Onuki157b1622016-06-02 16:13:10 -0700343 int GET_APPLICATION_RESOURCES = 9;
344 int RESOURCE_NAME_LOOKUP = 10;
Makoto Onukib08790c2016-06-23 14:05:46 -0700345 int GET_LAUNCHER_ACTIVITY = 11;
346 int CHECK_LAUNCHER_ACTIVITY = 12;
Makoto Onukiee6b6e42016-06-29 17:34:02 -0700347 int IS_ACTIVITY_ENABLED = 13;
Makoto Onuki4e6cef42016-07-13 16:14:01 -0700348 int PACKAGE_UPDATE_CHECK = 14;
Makoto Onuki085a05c2016-08-19 11:39:29 -0700349 int ASYNC_PRELOAD_USER_DELAY = 15;
Makoto Onuki2d895c32016-12-02 15:48:40 -0800350 int GET_DEFAULT_LAUNCHER = 16;
Makoto Onuki2e210c42016-03-30 08:30:36 -0700351
Makoto Onuki2d895c32016-12-02 15:48:40 -0800352 int COUNT = GET_DEFAULT_LAUNCHER + 1;
Makoto Onuki2e210c42016-03-30 08:30:36 -0700353 }
354
Makoto Onuki085a05c2016-08-19 11:39:29 -0700355 private static final String[] STAT_LABELS = {
356 "getHomeActivities()",
357 "Launcher permission check",
358 "getPackageInfo()",
359 "getPackageInfo(SIG)",
360 "getApplicationInfo",
361 "cleanupDanglingBitmaps",
362 "getActivity+metadata",
363 "getInstalledPackages",
364 "checkPackageChanges",
365 "getApplicationResources",
366 "resourceNameLookup",
367 "getLauncherActivity",
368 "checkLauncherActivity",
369 "isActivityEnabled",
370 "packageUpdateCheck",
Makoto Onuki2d895c32016-12-02 15:48:40 -0800371 "asyncPreloadUserDelay",
372 "getDefaultLauncher()"
Makoto Onuki085a05c2016-08-19 11:39:29 -0700373 };
374
Makoto Onuki2e210c42016-03-30 08:30:36 -0700375 final Object mStatLock = new Object();
376
377 @GuardedBy("mStatLock")
378 private final int[] mCountStats = new int[Stats.COUNT];
379
380 @GuardedBy("mStatLock")
381 private final long[] mDurationStats = new long[Stats.COUNT];
382
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700383 private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
384 ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
385
Makoto Onuki7001a612016-05-27 13:24:28 -0700386 static final int OPERATION_SET = 0;
387 static final int OPERATION_ADD = 1;
388 static final int OPERATION_UPDATE = 2;
389
390 /** @hide */
391 @IntDef(value = {
392 OPERATION_SET,
393 OPERATION_ADD,
394 OPERATION_UPDATE
Makoto Onukib08790c2016-06-23 14:05:46 -0700395 })
Makoto Onuki7001a612016-05-27 13:24:28 -0700396 @Retention(RetentionPolicy.SOURCE)
Makoto Onukib08790c2016-06-23 14:05:46 -0700397 @interface ShortcutOperation {
398 }
Makoto Onuki7001a612016-05-27 13:24:28 -0700399
Makoto Onukia2241832016-07-06 13:28:37 -0700400 @GuardedBy("mLock")
401 private int mWtfCount = 0;
402
403 @GuardedBy("mLock")
404 private Exception mLastWtfStacktrace;
405
Makoto Onukifc4cf2d2016-08-24 11:10:26 -0700406 static class InvalidFileFormatException extends Exception {
407 public InvalidFileFormatException(String message, Throwable cause) {
408 super(message, cause);
409 }
410 }
411
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800412 public ShortcutService(Context context) {
Makoto Onukiee6b6e42016-06-29 17:34:02 -0700413 this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false);
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700414 }
415
416 @VisibleForTesting
Makoto Onukiee6b6e42016-06-29 17:34:02 -0700417 ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800418 mContext = Preconditions.checkNotNull(context);
419 LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700420 mHandler = new Handler(looper);
Makoto Onuki0acbb142016-03-22 17:02:57 -0700421 mIPackageManager = AppGlobals.getPackageManager();
Makoto Onukiac042502016-05-20 16:39:42 -0700422 mPackageManagerInternal = Preconditions.checkNotNull(
423 LocalServices.getService(PackageManagerInternal.class));
424 mUserManager = Preconditions.checkNotNull(context.getSystemService(UserManager.class));
425 mUsageStatsManagerInternal = Preconditions.checkNotNull(
426 LocalServices.getService(UsageStatsManagerInternal.class));
Makoto Onuki33525d22016-08-03 15:45:24 -0700427 mActivityManagerInternal = Preconditions.checkNotNull(
428 LocalServices.getService(ActivityManagerInternal.class));
Makoto Onukicdc78f72016-03-21 15:47:52 -0700429
Makoto Onuki2d895c32016-12-02 15:48:40 -0800430 mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
Makoto Onuki475c3652017-05-08 14:29:03 -0700431 mShortcutBitmapSaver = new ShortcutBitmapSaver(this);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800432
Makoto Onukiee6b6e42016-06-29 17:34:02 -0700433 if (onlyForPackageManagerApis) {
434 return; // Don't do anything further. For unit tests only.
435 }
436
Makoto Onuki4e6cef42016-07-13 16:14:01 -0700437 // Register receivers.
438
439 // We need to set a priority, so let's just not use PackageMonitor for now.
440 // TODO Refactor PackageMonitor to support priorities.
441 final IntentFilter packageFilter = new IntentFilter();
442 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
443 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
444 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
445 packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
446 packageFilter.addDataScheme("package");
447 packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
448 mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL,
449 packageFilter, null, mHandler);
450
Makoto Onuki10305202016-07-14 18:14:08 -0700451 final IntentFilter preferedActivityFilter = new IntentFilter();
452 preferedActivityFilter.addAction(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
453 preferedActivityFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
454 mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL,
455 preferedActivityFilter, null, mHandler);
456
Makoto Onuki4e6cef42016-07-13 16:14:01 -0700457 final IntentFilter localeFilter = new IntentFilter();
458 localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
459 localeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
460 mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL,
461 localeFilter, null, mHandler);
Makoto Onukif34c3082016-07-13 10:25:25 -0700462
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700463 injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
464 | ActivityManager.UID_OBSERVER_GONE);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800465 }
466
Makoto Onuki2e210c42016-03-30 08:30:36 -0700467 void logDurationStat(int statId, long start) {
468 synchronized (mStatLock) {
469 mCountStats[statId]++;
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700470 mDurationStats[statId] += (injectElapsedRealtime() - start);
Makoto Onuki2e210c42016-03-30 08:30:36 -0700471 }
472 }
473
Makoto Onuki4e6cef42016-07-13 16:14:01 -0700474 public String injectGetLocaleTagsForUser(@UserIdInt int userId) {
475 // TODO This should get the per-user locale. b/30123329 b/30119489
476 return LocaleList.getDefault().toLanguageTags();
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700477 }
478
479 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Makoto Onukib08790c2016-06-23 14:05:46 -0700480 @Override
Sudheer Shanka80255802017-03-04 14:48:53 -0800481 public void onUidStateChanged(int uid, int procState, long procStateSeq)
482 throws RemoteException {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700483 handleOnUidStateChanged(uid, procState);
484 }
485
Makoto Onukib08790c2016-06-23 14:05:46 -0700486 @Override
Dianne Hackborne07641d2016-11-09 15:07:23 -0800487 public void onUidGone(int uid, boolean disabled) throws RemoteException {
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800488 handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700489 }
490
Makoto Onukib08790c2016-06-23 14:05:46 -0700491 @Override
492 public void onUidActive(int uid) throws RemoteException {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700493 }
494
Makoto Onukib08790c2016-06-23 14:05:46 -0700495 @Override
Dianne Hackborne07641d2016-11-09 15:07:23 -0800496 public void onUidIdle(int uid, boolean disabled) throws RemoteException {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700497 }
498 };
499
500 void handleOnUidStateChanged(int uid, int procState) {
501 if (DEBUG_PROCSTATE) {
502 Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
503 }
504 synchronized (mLock) {
505 mUidState.put(uid, procState);
506
507 // We need to keep track of last time an app comes to foreground.
508 // See ShortcutPackage.getApiCallCount() for how it's used.
509 // It doesn't have to be persisted, but it needs to be the elapsed time.
510 if (isProcessStateForeground(procState)) {
511 mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
512 }
513 }
514 }
515
516 private boolean isProcessStateForeground(int processState) {
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800517 return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700518 }
519
520 boolean isUidForegroundLocked(int uid) {
521 if (uid == Process.SYSTEM_UID) {
522 // IUidObserver doesn't report the state of SYSTEM, but it always has bound services,
523 // so it's foreground anyway.
524 return true;
525 }
Makoto Onuki33525d22016-08-03 15:45:24 -0700526 // First, check with the local cache.
527 if (isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE))) {
528 return true;
529 }
530 // If the cache says background, reach out to AM. Since it'll internally need to hold
531 // the AM lock, we use it as a last resort.
532 return isProcessStateForeground(mActivityManagerInternal.getUidProcessState(uid));
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700533 }
534
535 long getUidLastForegroundElapsedTimeLocked(int uid) {
536 return mUidLastForegroundElapsedTime.get(uid);
537 }
538
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800539 /**
540 * System service lifecycle.
541 */
542 public static final class Lifecycle extends SystemService {
543 final ShortcutService mService;
544
545 public Lifecycle(Context context) {
546 super(context);
547 mService = new ShortcutService(context);
548 }
549
550 @Override
551 public void onStart() {
552 publishBinderService(Context.SHORTCUT_SERVICE, mService);
553 }
554
555 @Override
556 public void onBootPhase(int phase) {
557 mService.onBootPhase(phase);
558 }
559
560 @Override
Makoto Onuki01ce92b2017-04-28 12:24:16 -0700561 public void onStopUser(int userHandle) {
562 mService.handleStopUser(userHandle);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800563 }
564
565 @Override
Makoto Onukif3a572b2016-03-10 12:28:38 -0800566 public void onUnlockUser(int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -0700567 mService.handleUnlockUser(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800568 }
569 }
570
571 /** lifecycle event */
572 void onBootPhase(int phase) {
573 if (DEBUG) {
574 Slog.d(TAG, "onBootPhase: " + phase);
575 }
576 switch (phase) {
577 case SystemService.PHASE_LOCK_SETTINGS_READY:
578 initialize();
579 break;
Makoto Onukic51b2872016-05-04 15:24:50 -0700580 case SystemService.PHASE_BOOT_COMPLETED:
581 mBootCompleted.set(true);
582 break;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800583 }
584 }
585
586 /** lifecycle event */
Makoto Onukicdc78f72016-03-21 15:47:52 -0700587 void handleUnlockUser(int userId) {
Makoto Onuki22fcc682016-05-17 14:52:19 -0700588 if (DEBUG) {
Makoto Onuki085a05c2016-08-19 11:39:29 -0700589 Slog.d(TAG, "handleUnlockUser: user=" + userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -0700590 }
Makoto Onukicdc78f72016-03-21 15:47:52 -0700591 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -0700592 mUnlockedUsers.put(userId, true);
Makoto Onukicdc78f72016-03-21 15:47:52 -0700593 }
Makoto Onuki085a05c2016-08-19 11:39:29 -0700594
595 // Preload the user data.
596 // Note, we don't use mHandler here but instead just start a new thread.
597 // This is because mHandler (which uses com.android.internal.os.BackgroundThread) is very
598 // busy at this point and this could take hundreds of milliseconds, which would be too
599 // late since the launcher would already have started.
600 // So we just create a new thread. This code runs rarely, so we don't use a thread pool
601 // or anything.
602 final long start = injectElapsedRealtime();
603 injectRunOnNewThread(() -> {
604 synchronized (mLock) {
605 logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
606 getUserShortcutsLocked(userId);
607 }
608 });
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800609 }
610
611 /** lifecycle event */
Makoto Onuki01ce92b2017-04-28 12:24:16 -0700612 void handleStopUser(int userId) {
Makoto Onuki02f338e2016-07-29 09:40:40 -0700613 if (DEBUG) {
Makoto Onuki01ce92b2017-04-28 12:24:16 -0700614 Slog.d(TAG, "handleStopUser: user=" + userId);
Makoto Onuki02f338e2016-07-29 09:40:40 -0700615 }
Makoto Onukicdc78f72016-03-21 15:47:52 -0700616 synchronized (mLock) {
617 unloadUserLocked(userId);
Makoto Onuki02f338e2016-07-29 09:40:40 -0700618
619 mUnlockedUsers.put(userId, false);
Makoto Onukicdc78f72016-03-21 15:47:52 -0700620 }
621 }
622
623 private void unloadUserLocked(int userId) {
624 if (DEBUG) {
625 Slog.d(TAG, "unloadUserLocked: user=" + userId);
626 }
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700627 // Save all dirty information.
628 saveDirtyInfo();
629
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800630 // Unload
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -0800631 mUsers.delete(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800632 }
633
634 /** Return the base state file name */
635 private AtomicFile getBaseStateFile() {
636 final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
637 path.mkdirs();
638 return new AtomicFile(path);
639 }
640
641 /**
642 * Init the instance. (load the state file, etc)
643 */
644 private void initialize() {
645 synchronized (mLock) {
Makoto Onuki4362a662016-03-08 18:59:09 -0800646 loadConfigurationLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800647 loadBaseStateLocked();
648 }
649 }
650
Makoto Onuki4362a662016-03-08 18:59:09 -0800651 /**
652 * Load the configuration from Settings.
653 */
654 private void loadConfigurationLocked() {
655 updateConfigurationLocked(injectShortcutManagerConstants());
656 }
Makoto Onuki55046222016-03-08 10:49:47 -0800657
Makoto Onuki4362a662016-03-08 18:59:09 -0800658 /**
659 * Load the configuration from Settings.
660 */
661 @VisibleForTesting
662 boolean updateConfigurationLocked(String config) {
663 boolean result = true;
664
665 final KeyValueListParser parser = new KeyValueListParser(',');
666 try {
667 parser.setString(config);
668 } catch (IllegalArgumentException e) {
669 // Failed to parse the settings string, log this and move on
670 // with defaults.
671 Slog.e(TAG, "Bad shortcut manager settings", e);
672 result = false;
673 }
674
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700675 mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
676 DEFAULT_SAVE_DELAY_MS));
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700677
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700678 mResetInterval = Math.max(1, parser.getLong(
Makoto Onuki4362a662016-03-08 18:59:09 -0800679 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700680 * 1000L);
Makoto Onuki4362a662016-03-08 18:59:09 -0800681
Makoto Onukib6d35232016-04-04 15:57:17 -0700682 mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong(
683 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL));
Makoto Onuki4362a662016-03-08 18:59:09 -0800684
Makoto Onukib5a012f2016-06-21 11:13:53 -0700685 mMaxShortcuts = Math.max(0, (int) parser.getLong(
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700686 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP));
Makoto Onuki4362a662016-03-08 18:59:09 -0800687
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700688 final int iconDimensionDp = Math.max(1, injectIsLowRamDevice()
Makoto Onuki4362a662016-03-08 18:59:09 -0800689 ? (int) parser.getLong(
Makoto Onukib08790c2016-06-23 14:05:46 -0700690 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
691 DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
Makoto Onuki4362a662016-03-08 18:59:09 -0800692 : (int) parser.getLong(
Makoto Onukib08790c2016-06-23 14:05:46 -0700693 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
694 DEFAULT_MAX_ICON_DIMENSION_DP));
Makoto Onuki4362a662016-03-08 18:59:09 -0800695
696 mMaxIconDimension = injectDipToPixel(iconDimensionDp);
697
698 mIconPersistFormat = CompressFormat.valueOf(
699 parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
700
701 mIconPersistQuality = (int) parser.getLong(
702 ConfigConstants.KEY_ICON_QUALITY,
703 DEFAULT_ICON_PERSIST_QUALITY);
704
705 return result;
706 }
707
708 @VisibleForTesting
709 String injectShortcutManagerConstants() {
710 return android.provider.Settings.Global.getString(
711 mContext.getContentResolver(),
712 android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
713 }
714
715 @VisibleForTesting
716 int injectDipToPixel(int dip) {
717 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
718 mContext.getResources().getDisplayMetrics());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800719 }
720
Makoto Onuki55046222016-03-08 10:49:47 -0800721 // === Persisting ===
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800722
723 @Nullable
Makoto Onuki41066a62016-03-09 16:18:44 -0800724 static String parseStringAttribute(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800725 return parser.getAttributeValue(null, attribute);
726 }
727
Makoto Onuki0acbb142016-03-22 17:02:57 -0700728 static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) {
729 return parseLongAttribute(parser, attribute) == 1;
730 }
731
Makoto Onuki41066a62016-03-09 16:18:44 -0800732 static int parseIntAttribute(XmlPullParser parser, String attribute) {
733 return (int) parseLongAttribute(parser, attribute);
734 }
735
Makoto Onukid99c6f02016-03-28 11:02:54 -0700736 static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
737 return (int) parseLongAttribute(parser, attribute, def);
738 }
739
Makoto Onuki41066a62016-03-09 16:18:44 -0800740 static long parseLongAttribute(XmlPullParser parser, String attribute) {
Makoto Onukid99c6f02016-03-28 11:02:54 -0700741 return parseLongAttribute(parser, attribute, 0);
742 }
743
744 static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800745 final String value = parseStringAttribute(parser, attribute);
746 if (TextUtils.isEmpty(value)) {
Makoto Onukid99c6f02016-03-28 11:02:54 -0700747 return def;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800748 }
749 try {
750 return Long.parseLong(value);
751 } catch (NumberFormatException e) {
752 Slog.e(TAG, "Error parsing long " + value);
Makoto Onukid99c6f02016-03-28 11:02:54 -0700753 return def;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800754 }
755 }
756
757 @Nullable
Makoto Onuki41066a62016-03-09 16:18:44 -0800758 static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800759 final String value = parseStringAttribute(parser, attribute);
760 if (TextUtils.isEmpty(value)) {
761 return null;
762 }
763 return ComponentName.unflattenFromString(value);
764 }
765
766 @Nullable
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700767 static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800768 final String value = parseStringAttribute(parser, attribute);
Makoto Onukib5a012f2016-06-21 11:13:53 -0700769 Intent parsed = null;
770 if (!TextUtils.isEmpty(value)) {
771 try {
772 parsed = Intent.parseUri(value, /* flags =*/ 0);
773 } catch (URISyntaxException e) {
774 Slog.e(TAG, "Error parsing intent", e);
775 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800776 }
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700777 return parsed;
778 }
779
780 @Nullable
781 static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
782 Intent parsed = parseIntentAttributeNoDefault(parser, attribute);
Makoto Onukib5a012f2016-06-21 11:13:53 -0700783 if (parsed == null) {
784 // Default intent.
785 parsed = new Intent(Intent.ACTION_VIEW);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800786 }
Makoto Onukib5a012f2016-06-21 11:13:53 -0700787 return parsed;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800788 }
789
Makoto Onuki41066a62016-03-09 16:18:44 -0800790 static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800791 if (TextUtils.isEmpty(value)) return;
792
793 out.startTag(null, tag);
794 out.attribute(null, ATTR_VALUE, value);
795 out.endTag(null, tag);
796 }
797
Makoto Onuki41066a62016-03-09 16:18:44 -0800798 static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800799 writeTagValue(out, tag, Long.toString(value));
800 }
801
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800802 static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
803 if (name == null) return;
804 writeTagValue(out, tag, name.flattenToString());
805 }
806
Makoto Onuki41066a62016-03-09 16:18:44 -0800807 static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800808 throws IOException, XmlPullParserException {
809 if (bundle == null) return;
810
811 out.startTag(null, tag);
812 bundle.saveToXml(out);
813 out.endTag(null, tag);
814 }
815
Makoto Onuki22fcc682016-05-17 14:52:19 -0700816 static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800817 if (TextUtils.isEmpty(value)) return;
818
Makoto Onuki22fcc682016-05-17 14:52:19 -0700819 out.attribute(null, name, value.toString());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800820 }
821
Makoto Onuki41066a62016-03-09 16:18:44 -0800822 static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800823 writeAttr(out, name, String.valueOf(value));
824 }
825
Makoto Onuki0acbb142016-03-22 17:02:57 -0700826 static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
827 if (value) {
828 writeAttr(out, name, "1");
829 }
830 }
831
Makoto Onuki41066a62016-03-09 16:18:44 -0800832 static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800833 if (comp == null) return;
834 writeAttr(out, name, comp.flattenToString());
835 }
836
Makoto Onuki41066a62016-03-09 16:18:44 -0800837 static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800838 if (intent == null) return;
839
840 writeAttr(out, name, intent.toUri(/* flags =*/ 0));
841 }
842
843 @VisibleForTesting
844 void saveBaseStateLocked() {
845 final AtomicFile file = getBaseStateFile();
846 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700847 Slog.d(TAG, "Saving to " + file.getBaseFile());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800848 }
849
850 FileOutputStream outs = null;
851 try {
852 outs = file.startWrite();
853
854 // Write to XML
855 XmlSerializer out = new FastXmlSerializer();
856 out.setOutput(outs, StandardCharsets.UTF_8.name());
857 out.startDocument(null, true);
858 out.startTag(null, TAG_ROOT);
859
860 // Body.
861 writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
862
863 // Epilogue.
864 out.endTag(null, TAG_ROOT);
865 out.endDocument();
866
867 // Close.
868 file.finishWrite(outs);
869 } catch (IOException e) {
870 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
871 file.failWrite(outs);
872 }
873 }
874
875 private void loadBaseStateLocked() {
876 mRawLastResetTime = 0;
877
878 final AtomicFile file = getBaseStateFile();
879 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700880 Slog.d(TAG, "Loading from " + file.getBaseFile());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800881 }
882 try (FileInputStream in = file.openRead()) {
883 XmlPullParser parser = Xml.newPullParser();
884 parser.setInput(in, StandardCharsets.UTF_8.name());
885
886 int type;
887 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
888 if (type != XmlPullParser.START_TAG) {
889 continue;
890 }
891 final int depth = parser.getDepth();
892 // Check the root tag
893 final String tag = parser.getName();
894 if (depth == 1) {
895 if (!TAG_ROOT.equals(tag)) {
896 Slog.e(TAG, "Invalid root tag: " + tag);
897 return;
898 }
899 continue;
900 }
901 // Assume depth == 2
902 switch (tag) {
903 case TAG_LAST_RESET_TIME:
904 mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
905 break;
906 default:
907 Slog.e(TAG, "Invalid tag: " + tag);
908 break;
909 }
910 }
911 } catch (FileNotFoundException e) {
912 // Use the default
Makoto Onukib08790c2016-06-23 14:05:46 -0700913 } catch (IOException | XmlPullParserException e) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800914 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
915
916 mRawLastResetTime = 0;
917 }
918 // Adjust the last reset time.
919 getLastResetTimeLocked();
920 }
921
Makoto Onuki0eed4412016-07-21 11:21:59 -0700922 @VisibleForTesting
923 final File getUserFile(@UserIdInt int userId) {
924 return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
925 }
926
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800927 private void saveUserLocked(@UserIdInt int userId) {
Makoto Onuki0eed4412016-07-21 11:21:59 -0700928 final File path = getUserFile(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800929 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700930 Slog.d(TAG, "Saving to " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800931 }
Makoto Onuki475c3652017-05-08 14:29:03 -0700932
933 mShortcutBitmapSaver.waitForAllSavesLocked();
934
Makoto Onuki0eed4412016-07-21 11:21:59 -0700935 path.getParentFile().mkdirs();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800936 final AtomicFile file = new AtomicFile(path);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700937 FileOutputStream os = null;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800938 try {
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700939 os = file.startWrite();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800940
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700941 saveUserInternalLocked(userId, os, /* forBackup= */ false);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800942
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700943 file.finishWrite(os);
Makoto Onuki1e173232016-08-10 10:47:13 -0700944
945 // Remove all dangling bitmap files.
946 cleanupDanglingBitmapDirectoriesLocked(userId);
Makoto Onukib08790c2016-06-23 14:05:46 -0700947 } catch (XmlPullParserException | IOException e) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800948 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700949 file.failWrite(os);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800950 }
951 }
952
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700953 private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
954 boolean forBackup) throws IOException, XmlPullParserException {
955
956 final BufferedOutputStream bos = new BufferedOutputStream(os);
957
958 // Write to XML
959 XmlSerializer out = new FastXmlSerializer();
960 out.setOutput(bos, StandardCharsets.UTF_8.name());
961 out.startDocument(null, true);
962
Makoto Onukic51b2872016-05-04 15:24:50 -0700963 getUserShortcutsLocked(userId).saveToXml(out, forBackup);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700964
965 out.endDocument();
966
967 bos.flush();
968 os.flush();
969 }
970
Makoto Onuki41066a62016-03-09 16:18:44 -0800971 static IOException throwForInvalidTag(int depth, String tag) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800972 throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
973 }
974
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700975 static void warnForInvalidTag(int depth, String tag) throws IOException {
976 Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
977 }
978
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800979 @Nullable
Makoto Onuki31459242016-03-22 11:12:18 -0700980 private ShortcutUser loadUserLocked(@UserIdInt int userId) {
Makoto Onuki0eed4412016-07-21 11:21:59 -0700981 final File path = getUserFile(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800982 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700983 Slog.d(TAG, "Loading from " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800984 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800985 final AtomicFile file = new AtomicFile(path);
986
987 final FileInputStream in;
988 try {
989 in = file.openRead();
990 } catch (FileNotFoundException e) {
991 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700992 Slog.d(TAG, "Not found " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800993 }
994 return null;
995 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800996 try {
Makoto Onukib08790c2016-06-23 14:05:46 -0700997 final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false);
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700998 return ret;
Makoto Onukifc4cf2d2016-08-24 11:10:26 -0700999 } catch (IOException | XmlPullParserException | InvalidFileFormatException e) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001000 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
1001 return null;
1002 } finally {
1003 IoUtils.closeQuietly(in);
1004 }
1005 }
1006
Makoto Onuki9da23fc2016-03-29 11:14:42 -07001007 private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
Makoto Onukifc4cf2d2016-08-24 11:10:26 -07001008 boolean fromBackup) throws XmlPullParserException, IOException,
1009 InvalidFileFormatException {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07001010
1011 final BufferedInputStream bis = new BufferedInputStream(is);
1012
1013 ShortcutUser ret = null;
1014 XmlPullParser parser = Xml.newPullParser();
1015 parser.setInput(bis, StandardCharsets.UTF_8.name());
1016
1017 int type;
1018 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
1019 if (type != XmlPullParser.START_TAG) {
1020 continue;
1021 }
1022 final int depth = parser.getDepth();
1023
1024 final String tag = parser.getName();
1025 if (DEBUG_LOAD) {
1026 Slog.d(TAG, String.format("depth=%d type=%d name=%s",
1027 depth, type, tag));
1028 }
1029 if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
1030 ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup);
1031 continue;
1032 }
1033 throwForInvalidTag(depth, tag);
1034 }
1035 return ret;
1036 }
1037
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001038 private void scheduleSaveBaseState() {
Makoto Onuki0acbb142016-03-22 17:02:57 -07001039 scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001040 }
1041
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001042 void scheduleSaveUser(@UserIdInt int userId) {
Makoto Onuki0acbb142016-03-22 17:02:57 -07001043 scheduleSaveInner(userId);
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001044 }
1045
1046 // In order to re-schedule, we need to reuse the same instance, so keep it in final.
1047 private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo;
1048
Makoto Onuki0acbb142016-03-22 17:02:57 -07001049 private void scheduleSaveInner(@UserIdInt int userId) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001050 if (DEBUG) {
1051 Slog.d(TAG, "Scheduling to save for " + userId);
1052 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001053 synchronized (mLock) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001054 if (!mDirtyUserIds.contains(userId)) {
1055 mDirtyUserIds.add(userId);
1056 }
1057 }
1058 // If already scheduled, remove that and re-schedule in N seconds.
1059 mHandler.removeCallbacks(mSaveDirtyInfoRunner);
1060 mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis);
1061 }
1062
1063 @VisibleForTesting
1064 void saveDirtyInfo() {
1065 if (DEBUG) {
1066 Slog.d(TAG, "saveDirtyInfo");
1067 }
Makoto Onuki02f338e2016-07-29 09:40:40 -07001068 try {
1069 synchronized (mLock) {
1070 for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
1071 final int userId = mDirtyUserIds.get(i);
1072 if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
1073 saveBaseStateLocked();
1074 } else {
1075 saveUserLocked(userId);
1076 }
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001077 }
Makoto Onuki02f338e2016-07-29 09:40:40 -07001078 mDirtyUserIds.clear();
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001079 }
Makoto Onuki02f338e2016-07-29 09:40:40 -07001080 } catch (Exception e) {
1081 wtf("Exception in saveDirtyInfo", e);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001082 }
1083 }
1084
1085 /** Return the last reset time. */
1086 long getLastResetTimeLocked() {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001087 updateTimesLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001088 return mRawLastResetTime;
1089 }
1090
1091 /** Return the next reset time. */
1092 long getNextResetTimeLocked() {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001093 updateTimesLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001094 return mRawLastResetTime + mResetInterval;
1095 }
1096
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001097 static boolean isClockValid(long time) {
1098 return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT
1099 }
1100
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001101 /**
1102 * Update the last reset time.
1103 */
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001104 private void updateTimesLocked() {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001105
1106 final long now = injectCurrentTimeMillis();
1107
1108 final long prevLastResetTime = mRawLastResetTime;
1109
1110 if (mRawLastResetTime == 0) { // first launch.
1111 // TODO Randomize??
1112 mRawLastResetTime = now;
1113 } else if (now < mRawLastResetTime) {
1114 // Clock rewound.
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001115 if (isClockValid(now)) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001116 Slog.w(TAG, "Clock rewound");
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001117 // TODO Randomize??
1118 mRawLastResetTime = now;
1119 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001120 } else {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -07001121 if ((mRawLastResetTime + mResetInterval) <= now) {
1122 final long offset = mRawLastResetTime % mResetInterval;
1123 mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001124 }
1125 }
1126 if (prevLastResetTime != mRawLastResetTime) {
1127 scheduleSaveBaseState();
1128 }
1129 }
1130
Makoto Onuki1e173232016-08-10 10:47:13 -07001131 // Requires mLock held, but "Locked" prefix would look weired so we just say "L".
Makoto Onuki02f338e2016-07-29 09:40:40 -07001132 protected boolean isUserUnlockedL(@UserIdInt int userId) {
Makoto Onuki1e173232016-08-10 10:47:13 -07001133 // First, check the local copy.
1134 if (mUnlockedUsers.get(userId)) {
1135 return true;
1136 }
1137 // If the local copy says the user is locked, check with AM for the actual state, since
1138 // the user might just have been unlocked.
1139 // Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
1140 // when the user is STOPPING, which we still want to consider as "unlocked".
1141 final long token = injectClearCallingIdentity();
1142 try {
1143 return mUserManager.isUserUnlockingOrUnlocked(userId);
1144 } finally {
1145 injectRestoreCallingIdentity(token);
1146 }
Makoto Onuki9c850012016-07-26 15:50:50 -07001147 }
1148
Makoto Onuki02f338e2016-07-29 09:40:40 -07001149 // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L".
1150 void throwIfUserLockedL(@UserIdInt int userId) {
1151 if (!isUserUnlockedL(userId)) {
Makoto Onuki9c850012016-07-26 15:50:50 -07001152 throw new IllegalStateException("User " + userId + " is locked or not running");
1153 }
1154 }
1155
Makoto Onukicdc78f72016-03-21 15:47:52 -07001156 @GuardedBy("mLock")
1157 @NonNull
Makoto Onuki2e210c42016-03-30 08:30:36 -07001158 private boolean isUserLoadedLocked(@UserIdInt int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -07001159 return mUsers.get(userId) != null;
1160 }
1161
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001162 /** Return the per-user state. */
1163 @GuardedBy("mLock")
1164 @NonNull
Makoto Onuki31459242016-03-22 11:12:18 -07001165 ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001166 if (!isUserUnlockedL(userId)) {
Makoto Onuki9c850012016-07-26 15:50:50 -07001167 wtf("User still locked");
Makoto Onuki9c850012016-07-26 15:50:50 -07001168 }
1169
Makoto Onuki31459242016-03-22 11:12:18 -07001170 ShortcutUser userPackages = mUsers.get(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001171 if (userPackages == null) {
1172 userPackages = loadUserLocked(userId);
1173 if (userPackages == null) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001174 userPackages = new ShortcutUser(this, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001175 }
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08001176 mUsers.put(userId, userPackages);
Makoto Onuki085a05c2016-08-19 11:39:29 -07001177
1178 // Also when a user's data is first accessed, scan all packages.
1179 checkPackageChanges(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001180 }
1181 return userPackages;
1182 }
1183
Makoto Onuki2e210c42016-03-30 08:30:36 -07001184 void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
1185 for (int i = mUsers.size() - 1; i >= 0; i--) {
1186 c.accept(mUsers.valueAt(i));
1187 }
1188 }
1189
Makoto Onukic8c33292016-09-12 16:36:59 -07001190 /**
1191 * Return the per-user per-package state. If the caller is a publisher, use
1192 * {@link #getPackageShortcutsForPublisherLocked} instead.
1193 */
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001194 @GuardedBy("mLock")
1195 @NonNull
Makoto Onuki31459242016-03-22 11:12:18 -07001196 ShortcutPackage getPackageShortcutsLocked(
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001197 @NonNull String packageName, @UserIdInt int userId) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001198 return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
Makoto Onukide667372016-03-15 14:29:20 -07001199 }
1200
Makoto Onukic8c33292016-09-12 16:36:59 -07001201 /** Return the per-user per-package state. Use this when the caller is a publisher. */
1202 @GuardedBy("mLock")
1203 @NonNull
1204 ShortcutPackage getPackageShortcutsForPublisherLocked(
1205 @NonNull String packageName, @UserIdInt int userId) {
1206 final ShortcutPackage ret = getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
1207 ret.getUser().onCalledByPublisher(packageName);
1208 return ret;
1209 }
1210
Makoto Onukide667372016-03-15 14:29:20 -07001211 @GuardedBy("mLock")
1212 @NonNull
Makoto Onuki2e210c42016-03-30 08:30:36 -07001213 ShortcutLauncher getLauncherShortcutsLocked(
1214 @NonNull String packageName, @UserIdInt int ownerUserId,
1215 @UserIdInt int launcherUserId) {
1216 return getUserShortcutsLocked(ownerUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07001217 .getLauncherShortcuts(packageName, launcherUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001218 }
1219
1220 // === Caller validation ===
1221
Makoto Onuki475c3652017-05-08 14:29:03 -07001222 void removeIconLocked(ShortcutInfo shortcut) {
1223 mShortcutBitmapSaver.removeIcon(shortcut);
Makoto Onuki55046222016-03-08 10:49:47 -08001224 }
1225
Makoto Onuki0033b2a2016-04-14 17:19:16 -07001226 public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) {
1227 final File packagePath = new File(getUserBitmapFilePath(userId), packageName);
1228 if (!packagePath.isDirectory()) {
1229 return;
1230 }
1231 if (!(FileUtils.deleteContents(packagePath) && packagePath.delete())) {
1232 Slog.w(TAG, "Unable to remove directory " + packagePath);
1233 }
1234 }
1235
Makoto Onuki475c3652017-05-08 14:29:03 -07001236 /**
1237 * Remove dangling bitmap files for a user.
1238 *
1239 * Note this method must be called with the lock held after calling
1240 * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap
1241 * saves are going on.
1242 */
Makoto Onuki1e173232016-08-10 10:47:13 -07001243 private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001244 if (DEBUG) {
1245 Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
1246 }
1247 final long start = injectElapsedRealtime();
1248
Makoto Onuki1e173232016-08-10 10:47:13 -07001249 final ShortcutUser user = getUserShortcutsLocked(userId);
1250
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001251 final File bitmapDir = getUserBitmapFilePath(userId);
1252 final File[] children = bitmapDir.listFiles();
1253 if (children == null) {
1254 return;
1255 }
1256 for (File child : children) {
1257 if (!child.isDirectory()) {
1258 continue;
1259 }
1260 final String packageName = child.getName();
1261 if (DEBUG) {
1262 Slog.d(TAG, "cleanupDanglingBitmaps: Found directory=" + packageName);
1263 }
1264 if (!user.hasPackage(packageName)) {
1265 if (DEBUG) {
1266 Slog.d(TAG, "Removing dangling bitmap directory: " + packageName);
1267 }
1268 cleanupBitmapsForPackage(userId, packageName);
1269 } else {
1270 cleanupDanglingBitmapFilesLocked(userId, user, packageName, child);
1271 }
1272 }
1273 logDurationStat(Stats.CLEANUP_DANGLING_BITMAPS, start);
1274 }
1275
Makoto Onuki475c3652017-05-08 14:29:03 -07001276 /**
1277 * Remove dangling bitmap files for a package.
1278 *
1279 * Note this method must be called with the lock held after calling
1280 * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap
1281 * saves are going on.
1282 */
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001283 private void cleanupDanglingBitmapFilesLocked(@UserIdInt int userId, @NonNull ShortcutUser user,
1284 @NonNull String packageName, @NonNull File path) {
1285 final ArraySet<String> usedFiles =
Makoto Onukic51b2872016-05-04 15:24:50 -07001286 user.getPackageShortcuts(packageName).getUsedBitmapFiles();
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001287
1288 for (File child : path.listFiles()) {
1289 if (!child.isFile()) {
1290 continue;
1291 }
1292 final String name = child.getName();
1293 if (!usedFiles.contains(name)) {
1294 if (DEBUG) {
1295 Slog.d(TAG, "Removing dangling bitmap file: " + child.getAbsolutePath());
1296 }
1297 child.delete();
1298 }
1299 }
1300 }
1301
Makoto Onuki55046222016-03-08 10:49:47 -08001302 @VisibleForTesting
1303 static class FileOutputStreamWithPath extends FileOutputStream {
1304 private final File mFile;
1305
1306 public FileOutputStreamWithPath(File file) throws FileNotFoundException {
1307 super(file);
1308 mFile = file;
1309 }
1310
1311 public File getFile() {
1312 return mFile;
1313 }
1314 }
1315
1316 /**
1317 * Build the cached bitmap filename for a shortcut icon.
1318 *
1319 * The filename will be based on the ID, except certain characters will be escaped.
1320 */
Makoto Onuki55046222016-03-08 10:49:47 -08001321 FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
1322 throws IOException {
1323 final File packagePath = new File(getUserBitmapFilePath(userId),
Makoto Onuki22fcc682016-05-17 14:52:19 -07001324 shortcut.getPackage());
Makoto Onuki55046222016-03-08 10:49:47 -08001325 if (!packagePath.isDirectory()) {
1326 packagePath.mkdirs();
1327 if (!packagePath.isDirectory()) {
1328 throw new IOException("Unable to create directory " + packagePath);
1329 }
1330 SELinux.restorecon(packagePath);
1331 }
1332
1333 final String baseName = String.valueOf(injectCurrentTimeMillis());
Makoto Onukib08790c2016-06-23 14:05:46 -07001334 for (int suffix = 0; ; suffix++) {
Makoto Onuki55046222016-03-08 10:49:47 -08001335 final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
1336 final File file = new File(packagePath, filename);
1337 if (!file.exists()) {
1338 if (DEBUG) {
1339 Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
1340 }
1341 return new FileOutputStreamWithPath(file);
1342 }
1343 }
1344 }
1345
Makoto Onuki475c3652017-05-08 14:29:03 -07001346 void saveIconAndFixUpShortcutLocked(ShortcutInfo shortcut) {
Makoto Onuki55046222016-03-08 10:49:47 -08001347 if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
1348 return;
1349 }
1350
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001351 final long token = injectClearCallingIdentity();
Makoto Onuki55046222016-03-08 10:49:47 -08001352 try {
1353 // Clear icon info on the shortcut.
Makoto Onuki475c3652017-05-08 14:29:03 -07001354 removeIconLocked(shortcut);
Makoto Onuki55046222016-03-08 10:49:47 -08001355
1356 final Icon icon = shortcut.getIcon();
1357 if (icon == null) {
1358 return; // has no icon
1359 }
Hyunyoung Song47037462017-05-08 16:51:43 -07001360 int maxIconDimension = mMaxIconDimension;
Makoto Onukiabe84422016-04-07 09:41:19 -07001361 Bitmap bitmap;
Makoto Onuki55046222016-03-08 10:49:47 -08001362 try {
1363 switch (icon.getType()) {
1364 case Icon.TYPE_RESOURCE: {
1365 injectValidateIconResPackage(shortcut, icon);
1366
1367 shortcut.setIconResourceId(icon.getResId());
1368 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
1369 return;
1370 }
Hyunyoung Songf281e7a2017-02-13 10:57:42 -08001371 case Icon.TYPE_BITMAP:
Makoto Onukiabe84422016-04-07 09:41:19 -07001372 bitmap = icon.getBitmap(); // Don't recycle in this case.
Makoto Onuki55046222016-03-08 10:49:47 -08001373 break;
Hyunyoung Song47037462017-05-08 16:51:43 -07001374 case Icon.TYPE_ADAPTIVE_BITMAP: {
1375 bitmap = icon.getBitmap(); // Don't recycle in this case.
1376 maxIconDimension *= (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
Hyunyoung Song8947f592017-05-15 10:12:11 -07001377 break;
Makoto Onuki55046222016-03-08 10:49:47 -08001378 }
Makoto Onuki55046222016-03-08 10:49:47 -08001379 default:
1380 // This shouldn't happen because we've already validated the icon, but
1381 // just in case.
1382 throw ShortcutInfo.getInvalidIconException();
1383 }
Makoto Onuki475c3652017-05-08 14:29:03 -07001384 mShortcutBitmapSaver.saveBitmapLocked(shortcut,
Hyunyoung Song47037462017-05-08 16:51:43 -07001385 maxIconDimension, mIconPersistFormat, mIconPersistQuality);
Makoto Onuki55046222016-03-08 10:49:47 -08001386 } finally {
Makoto Onuki55046222016-03-08 10:49:47 -08001387 // Once saved, we won't use the original icon information, so null it out.
1388 shortcut.clearIcon();
1389 }
1390 } finally {
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001391 injectRestoreCallingIdentity(token);
Makoto Onuki55046222016-03-08 10:49:47 -08001392 }
1393 }
1394
1395 // Unfortunately we can't do this check in unit tests because we fake creator package names,
1396 // so override in unit tests.
1397 // TODO CTS this case.
1398 void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001399 if (!shortcut.getPackage().equals(icon.getResPackage())) {
Makoto Onuki55046222016-03-08 10:49:47 -08001400 throw new IllegalArgumentException(
1401 "Icon resource must reside in shortcut owner package");
1402 }
1403 }
1404
Makoto Onuki55046222016-03-08 10:49:47 -08001405 static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
1406 // Original width/height.
1407 final int ow = in.getWidth();
1408 final int oh = in.getHeight();
1409 if ((ow <= maxSize) && (oh <= maxSize)) {
1410 if (DEBUG) {
1411 Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
1412 }
1413 return in;
1414 }
1415 final int longerDimension = Math.max(ow, oh);
1416
1417 // New width and height.
1418 final int nw = ow * maxSize / longerDimension;
1419 final int nh = oh * maxSize / longerDimension;
1420 if (DEBUG) {
1421 Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
1422 ow, oh, nw, nh));
1423 }
1424
1425 final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
1426 final Canvas c = new Canvas(scaledBitmap);
1427
1428 final RectF dst = new RectF(0, 0, nw, nh);
1429
1430 c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
1431
Makoto Onuki55046222016-03-08 10:49:47 -08001432 return scaledBitmap;
1433 }
1434
Makoto Onuki157b1622016-06-02 16:13:10 -07001435 /**
1436 * For a shortcut, update all resource names from resource IDs, and also update all
1437 * resource-based strings.
1438 */
1439 void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
1440 final Resources publisherRes = injectGetResourcesForApplicationAsUser(
1441 si.getPackage(), si.getUserId());
1442 if (publisherRes != null) {
1443 final long start = injectElapsedRealtime();
1444 try {
1445 si.lookupAndFillInResourceNames(publisherRes);
1446 } finally {
1447 logDurationStat(Stats.RESOURCE_NAME_LOOKUP, start);
1448 }
1449 si.resolveResourceStrings(publisherRes);
1450 }
1451 }
1452
Makoto Onuki55046222016-03-08 10:49:47 -08001453 // === Caller validation ===
1454
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001455 private boolean isCallerSystem() {
1456 final int callingUid = injectBinderCallingUid();
Makoto Onukib08790c2016-06-23 14:05:46 -07001457 return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001458 }
1459
1460 private boolean isCallerShell() {
1461 final int callingUid = injectBinderCallingUid();
1462 return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
1463 }
1464
1465 private void enforceSystemOrShell() {
Makoto Onuki0b9d1db2016-07-18 14:16:41 -07001466 if (!(isCallerSystem() || isCallerShell())) {
1467 throw new SecurityException("Caller must be system or shell");
1468 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001469 }
1470
1471 private void enforceShell() {
Makoto Onuki0b9d1db2016-07-18 14:16:41 -07001472 if (!isCallerShell()) {
1473 throw new SecurityException("Caller must be shell");
1474 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001475 }
1476
Makoto Onuki9da23fc2016-03-29 11:14:42 -07001477 private void enforceSystem() {
Makoto Onuki0b9d1db2016-07-18 14:16:41 -07001478 if (!isCallerSystem()) {
1479 throw new SecurityException("Caller must be system");
1480 }
Makoto Onuki9da23fc2016-03-29 11:14:42 -07001481 }
1482
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001483 private void enforceResetThrottlingPermission() {
1484 if (isCallerSystem()) {
1485 return;
1486 }
Makoto Onuki76269922016-07-15 14:58:54 -07001487 enforceCallingOrSelfPermission(
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001488 android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null);
1489 }
1490
Makoto Onuki76269922016-07-15 14:58:54 -07001491 private void enforceCallingOrSelfPermission(
1492 @NonNull String permission, @Nullable String message) {
1493 if (isCallerSystem()) {
1494 return;
1495 }
1496 injectEnforceCallingPermission(permission, message);
1497 }
1498
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001499 /**
1500 * Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse
1501 * mockito. So instead we extracted it here and override it in the tests.
1502 */
1503 @VisibleForTesting
1504 void injectEnforceCallingPermission(
1505 @NonNull String permission, @Nullable String message) {
1506 mContext.enforceCallingPermission(permission, message);
1507 }
1508
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001509 private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
1510 Preconditions.checkStringNotEmpty(packageName, "packageName");
1511
1512 if (isCallerSystem()) {
1513 return; // no check
1514 }
1515
1516 final int callingUid = injectBinderCallingUid();
1517
1518 // Otherwise, make sure the arguments are valid.
1519 if (UserHandle.getUserId(callingUid) != userId) {
1520 throw new SecurityException("Invalid user-ID");
1521 }
Makoto Onuki66e4a2b2017-01-23 11:37:45 -08001522 if (injectGetPackageUid(packageName, userId) != callingUid) {
1523 throw new SecurityException("Calling package name mismatch");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001524 }
Makoto Onuki66e4a2b2017-01-23 11:37:45 -08001525 Preconditions.checkState(!isEphemeralApp(packageName, userId),
1526 "Ephemeral apps can't use ShortcutManager");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001527 }
1528
Makoto Onuki157b1622016-06-02 16:13:10 -07001529 // Overridden in unit tests to execute r synchronously.
1530 void injectPostToHandler(Runnable r) {
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001531 mHandler.post(r);
1532 }
1533
Makoto Onuki085a05c2016-08-19 11:39:29 -07001534 void injectRunOnNewThread(Runnable r) {
1535 new Thread(r).start();
1536 }
1537
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001538 /**
Makoto Onuki7001a612016-05-27 13:24:28 -07001539 * @throws IllegalArgumentException if {@code numShortcuts} is bigger than
Makoto Onukib08790c2016-06-23 14:05:46 -07001540 * {@link #getMaxActivityShortcuts()}.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001541 */
Makoto Onuki7001a612016-05-27 13:24:28 -07001542 void enforceMaxActivityShortcuts(int numShortcuts) {
Makoto Onukib5a012f2016-06-21 11:13:53 -07001543 if (numShortcuts > mMaxShortcuts) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001544 throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
1545 }
1546 }
1547
1548 /**
Makoto Onuki7001a612016-05-27 13:24:28 -07001549 * Return the max number of dynamic + manifest shortcuts for each launcher icon.
1550 */
1551 int getMaxActivityShortcuts() {
Makoto Onukib5a012f2016-06-21 11:13:53 -07001552 return mMaxShortcuts;
Makoto Onuki7001a612016-05-27 13:24:28 -07001553 }
1554
1555 /**
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001556 * - Sends a notification to LauncherApps
1557 * - Write to file
1558 */
Makoto Onuki39686e82016-04-13 18:03:00 -07001559 void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001560 notifyListeners(packageName, userId);
1561 scheduleSaveUser(userId);
1562 }
1563
1564 private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
Makoto Onuki82fb2eb2017-03-31 16:58:26 -07001565 if (DEBUG) {
1566 Slog.d(TAG, String.format(
1567 "Shortcut changes: package=%s, user=%d", packageName, userId));
1568 }
Makoto Onuki157b1622016-06-02 16:13:10 -07001569 injectPostToHandler(() -> {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001570 try {
1571 final ArrayList<ShortcutChangeListener> copy;
1572 synchronized (mLock) {
1573 if (!isUserUnlockedL(userId)) {
1574 return;
1575 }
1576
1577 copy = new ArrayList<>(mListeners);
1578 }
1579 // Note onShortcutChanged() needs to be called with the system service permissions.
1580 for (int i = copy.size() - 1; i >= 0; i--) {
1581 copy.get(i).onShortcutChanged(packageName, userId);
1582 }
1583 } catch (Exception ignore) {
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001584 }
1585 });
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001586 }
1587
1588 /**
1589 * Clean up / validate an incoming shortcut.
1590 * - Make sure all mandatory fields are set.
1591 * - Make sure the intent's extras are persistable, and them to set
Makoto Onuki0eed4412016-07-21 11:21:59 -07001592 * {@link ShortcutInfo#mIntentPersistableExtrases}. Also clear its extras.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001593 * - Clear flags.
1594 */
Makoto Onuki2d895c32016-12-02 15:48:40 -08001595 private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate,
1596 boolean forPinRequest) {
Makoto Onukibf563b62017-05-04 10:25:30 -07001597 if (shortcut.isReturnedByServer()) {
1598 Log.w(TAG,
1599 "Re-publishing ShortcutInfo returned by server is not supported."
1600 + " Some information such as icon may lost from shortcut.");
1601 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001602 Preconditions.checkNotNull(shortcut, "Null shortcut detected");
Makoto Onuki255461f2017-01-10 11:47:25 -08001603 if (shortcut.getActivity() != null) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001604 Preconditions.checkState(
Makoto Onuki22fcc682016-05-17 14:52:19 -07001605 shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
Makoto Onukib08790c2016-06-23 14:05:46 -07001606 "Cannot publish shortcut: activity " + shortcut.getActivity() + " does not"
1607 + " belong to package " + shortcut.getPackage());
Makoto Onuki13260b6ff2016-07-13 18:03:13 -07001608 Preconditions.checkState(
1609 injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
1610 "Cannot publish shortcut: activity " + shortcut.getActivity() + " is not"
1611 + " main activity");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001612 }
1613
Makoto Onuki55046222016-03-08 10:49:47 -08001614 if (!forUpdate) {
Makoto Onuki2d895c32016-12-02 15:48:40 -08001615 shortcut.enforceMandatoryFields(/* forPinned= */ forPinRequest);
1616 if (!forPinRequest) {
Makoto Onuki255461f2017-01-10 11:47:25 -08001617 Preconditions.checkState(shortcut.getActivity() != null,
1618 "Cannot publish shortcut: target activity is not set");
Makoto Onuki2d895c32016-12-02 15:48:40 -08001619 }
Makoto Onuki55046222016-03-08 10:49:47 -08001620 }
1621 if (shortcut.getIcon() != null) {
1622 ShortcutInfo.validateIcon(shortcut.getIcon());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001623 }
1624
Makoto Onukide667372016-03-15 14:29:20 -07001625 shortcut.replaceFlags(0);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001626 }
1627
Makoto Onuki2d895c32016-12-02 15:48:40 -08001628 private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
1629 fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false);
1630 }
1631
1632 public void validateShortcutForPinRequest(@NonNull ShortcutInfo shortcut) {
1633 fixUpIncomingShortcutInfo(shortcut, /* forUpdate= */ false, /*forPinRequest=*/ true);
1634 }
1635
Makoto Onukib08790c2016-06-23 14:05:46 -07001636 /**
1637 * When a shortcut has no target activity, set the default one from the package.
1638 */
1639 private void fillInDefaultActivity(List<ShortcutInfo> shortcuts) {
Makoto Onukib08790c2016-06-23 14:05:46 -07001640 ComponentName defaultActivity = null;
1641 for (int i = shortcuts.size() - 1; i >= 0; i--) {
1642 final ShortcutInfo si = shortcuts.get(i);
1643 if (si.getActivity() == null) {
1644 if (defaultActivity == null) {
1645 defaultActivity = injectGetDefaultMainActivity(
1646 si.getPackage(), si.getUserId());
1647 Preconditions.checkState(defaultActivity != null,
1648 "Launcher activity not found for package " + si.getPackage());
1649 }
1650 si.setActivity(defaultActivity);
1651 }
1652 }
1653 }
1654
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001655 private void assignImplicitRanks(List<ShortcutInfo> shortcuts) {
1656 for (int i = shortcuts.size() - 1; i >= 0; i--) {
1657 shortcuts.get(i).setImplicitRank(i);
1658 }
1659 }
1660
Makoto Onukibf563b62017-05-04 10:25:30 -07001661 private List<ShortcutInfo> setReturnedByServer(List<ShortcutInfo> shortcuts) {
1662 for (int i = shortcuts.size() - 1; i >= 0; i--) {
1663 shortcuts.get(i).setReturnedByServer();
1664 }
1665 return shortcuts;
1666 }
1667
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001668 // === APIs ===
1669
1670 @Override
1671 public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
1672 @UserIdInt int userId) {
1673 verifyCaller(packageName, userId);
1674
1675 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
1676 final int size = newShortcuts.size();
1677
1678 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001679 throwIfUserLockedL(userId);
1680
Makoto Onukic8c33292016-09-12 16:36:59 -07001681 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001682
Makoto Onuki22fcc682016-05-17 14:52:19 -07001683 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1684
Makoto Onukib08790c2016-06-23 14:05:46 -07001685 fillInDefaultActivity(newShortcuts);
1686
Makoto Onuki7001a612016-05-27 13:24:28 -07001687 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
1688
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001689 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001690 if (!ps.tryApiCall()) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001691 return false;
1692 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001693
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001694 // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
1695 ps.clearAllImplicitRanks();
1696 assignImplicitRanks(newShortcuts);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001697
Makoto Onukidf6da042016-06-16 09:51:40 -07001698 for (int i = 0; i < size; i++) {
1699 fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
1700 }
1701
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001702 // First, remove all un-pinned; dynamic shortcuts
Makoto Onukic51b2872016-05-04 15:24:50 -07001703 ps.deleteAllDynamicShortcuts();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001704
1705 // Then, add/update all. We need to make sure to take over "pinned" flag.
1706 for (int i = 0; i < size; i++) {
1707 final ShortcutInfo newShortcut = newShortcuts.get(i);
Makoto Onuki22fcc682016-05-17 14:52:19 -07001708 ps.addOrUpdateDynamicShortcut(newShortcut);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001709 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001710
1711 // Lastly, adjust the ranks.
1712 ps.adjustRanks();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001713 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001714 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001715
1716 verifyStates();
1717
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001718 return true;
1719 }
1720
1721 @Override
1722 public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
1723 @UserIdInt int userId) {
1724 verifyCaller(packageName, userId);
1725
1726 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
Makoto Onuki55046222016-03-08 10:49:47 -08001727 final int size = newShortcuts.size();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001728
1729 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001730 throwIfUserLockedL(userId);
1731
Makoto Onukic8c33292016-09-12 16:36:59 -07001732 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001733
Makoto Onuki22fcc682016-05-17 14:52:19 -07001734 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1735
Makoto Onukib08790c2016-06-23 14:05:46 -07001736 // For update, don't fill in the default activity. Having null activity means
1737 // "don't update the activity" here.
1738
Makoto Onuki7001a612016-05-27 13:24:28 -07001739 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
1740
Makoto Onuki55046222016-03-08 10:49:47 -08001741 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001742 if (!ps.tryApiCall()) {
Makoto Onuki55046222016-03-08 10:49:47 -08001743 return false;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001744 }
1745
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001746 // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
1747 ps.clearAllImplicitRanks();
1748 assignImplicitRanks(newShortcuts);
1749
Makoto Onuki55046222016-03-08 10:49:47 -08001750 for (int i = 0; i < size; i++) {
1751 final ShortcutInfo source = newShortcuts.get(i);
1752 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
1753
1754 final ShortcutInfo target = ps.findShortcutById(source.getId());
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001755 if (target == null) {
1756 continue;
1757 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07001758
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001759 if (target.isEnabled() != source.isEnabled()) {
1760 Slog.w(TAG,
1761 "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
1762 }
Makoto Onuki55046222016-03-08 10:49:47 -08001763
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001764 // When updating the rank, we need to insert between existing ranks, so set
1765 // this setRankChanged, and also copy the implicit rank fo adjustRanks().
1766 if (source.hasRank()) {
1767 target.setRankChanged();
1768 target.setImplicitRank(source.getImplicitRank());
1769 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07001770
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001771 final boolean replacingIcon = (source.getIcon() != null);
1772 if (replacingIcon) {
Makoto Onuki475c3652017-05-08 14:29:03 -07001773 removeIconLocked(target);
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001774 }
Makoto Onuki55046222016-03-08 10:49:47 -08001775
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001776 // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
1777 target.copyNonNullFieldsFrom(source);
1778 target.setTimestamp(injectCurrentTimeMillis());
1779
1780 if (replacingIcon) {
Makoto Onuki475c3652017-05-08 14:29:03 -07001781 saveIconAndFixUpShortcutLocked(target);
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001782 }
1783
1784 // When we're updating any resource related fields, re-extract the res names and
1785 // the values.
1786 if (replacingIcon || source.hasStringResources()) {
1787 fixUpShortcutResourceNamesAndValues(target);
Makoto Onuki55046222016-03-08 10:49:47 -08001788 }
1789 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001790
1791 // Lastly, adjust the ranks.
1792 ps.adjustRanks();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001793 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001794 packageShortcutsChanged(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001795
Makoto Onuki7001a612016-05-27 13:24:28 -07001796 verifyStates();
1797
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001798 return true;
1799 }
1800
1801 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001802 public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001803 @UserIdInt int userId) {
1804 verifyCaller(packageName, userId);
1805
Makoto Onukib6d35232016-04-04 15:57:17 -07001806 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
1807 final int size = newShortcuts.size();
1808
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001809 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001810 throwIfUserLockedL(userId);
1811
Makoto Onukic8c33292016-09-12 16:36:59 -07001812 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001813
Makoto Onuki22fcc682016-05-17 14:52:19 -07001814 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1815
Makoto Onukib08790c2016-06-23 14:05:46 -07001816 fillInDefaultActivity(newShortcuts);
1817
Makoto Onuki7001a612016-05-27 13:24:28 -07001818 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
1819
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001820 // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
1821 ps.clearAllImplicitRanks();
1822 assignImplicitRanks(newShortcuts);
1823
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001824 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001825 if (!ps.tryApiCall()) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001826 return false;
1827 }
Makoto Onukib6d35232016-04-04 15:57:17 -07001828 for (int i = 0; i < size; i++) {
1829 final ShortcutInfo newShortcut = newShortcuts.get(i);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001830
Makoto Onukib6d35232016-04-04 15:57:17 -07001831 // Validate the shortcut.
1832 fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001833
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001834 // When ranks are changing, we need to insert between ranks, so set the
1835 // "rank changed" flag.
1836 newShortcut.setRankChanged();
1837
Makoto Onukib6d35232016-04-04 15:57:17 -07001838 // Add it.
Makoto Onuki22fcc682016-05-17 14:52:19 -07001839 ps.addOrUpdateDynamicShortcut(newShortcut);
Makoto Onukib6d35232016-04-04 15:57:17 -07001840 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001841
1842 // Lastly, adjust the ranks.
1843 ps.adjustRanks();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001844 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001845 packageShortcutsChanged(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001846
Makoto Onuki7001a612016-05-27 13:24:28 -07001847 verifyStates();
1848
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001849 return true;
1850 }
1851
1852 @Override
Makoto Onuki2d895c32016-12-02 15:48:40 -08001853 public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
1854 IntentSender resultIntent, int userId) {
Makoto Onuki2d895c32016-12-02 15:48:40 -08001855 Preconditions.checkNotNull(shortcut);
1856 Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
Sunny Goyal4ad6b572017-02-28 11:11:51 -08001857 return requestPinItem(packageName, userId, shortcut, null, null, resultIntent);
Sunny Goyal87a563e2017-01-01 19:42:45 -08001858 }
1859
Sunny Goyala6be88a2017-01-12 16:27:58 -08001860 @Override
1861 public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)
1862 throws RemoteException {
1863 Preconditions.checkNotNull(shortcut);
1864 Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
1865 verifyCaller(packageName, userId);
1866
1867 final Intent ret;
1868 synchronized (mLock) {
1869 throwIfUserLockedL(userId);
1870
1871 // Send request to the launcher, if supported.
1872 ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
1873 }
1874
1875 verifyStates();
1876 return ret;
1877 }
1878
Sunny Goyal87a563e2017-01-01 19:42:45 -08001879 /**
1880 * Handles {@link #requestPinShortcut} and {@link ShortcutServiceInternal#requestPinAppWidget}.
1881 * After validating the caller, it passes the request to {@link #mShortcutRequestPinProcessor}.
1882 * Either {@param shortcut} or {@param appWidget} should be non-null.
1883 */
Sunny Goyal4ad6b572017-02-28 11:11:51 -08001884 private boolean requestPinItem(String packageName, int userId, ShortcutInfo shortcut,
1885 AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent) {
Sunny Goyal87a563e2017-01-01 19:42:45 -08001886 verifyCaller(packageName, userId);
Makoto Onuki2d895c32016-12-02 15:48:40 -08001887
1888 final boolean ret;
1889 synchronized (mLock) {
1890 throwIfUserLockedL(userId);
1891
Makoto Onukia01f4f02016-12-15 15:58:41 -08001892 Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()),
Makoto Onuki255461f2017-01-10 11:47:25 -08001893 "Calling application must have a foreground activity or a foreground service");
Makoto Onuki2d895c32016-12-02 15:48:40 -08001894
1895 // Send request to the launcher, if supported.
Sunny Goyal4ad6b572017-02-28 11:11:51 -08001896 ret = mShortcutRequestPinProcessor.requestPinItemLocked(shortcut, appWidget, extras,
1897 userId, resultIntent);
Makoto Onuki2d895c32016-12-02 15:48:40 -08001898 }
1899
1900 verifyStates();
1901
1902 return ret;
1903 }
1904
1905 @Override
Makoto Onuki20c95f82016-05-11 16:51:01 -07001906 public void disableShortcuts(String packageName, List shortcutIds,
Makoto Onukid6880792016-06-29 13:37:43 -07001907 CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
Makoto Onuki20c95f82016-05-11 16:51:01 -07001908 verifyCaller(packageName, userId);
1909 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
1910
1911 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001912 throwIfUserLockedL(userId);
1913
Makoto Onukic8c33292016-09-12 16:36:59 -07001914 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07001915
1916 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1917
Makoto Onukid6880792016-06-29 13:37:43 -07001918 final String disabledMessageString =
1919 (disabledMessage == null) ? null : disabledMessage.toString();
1920
Makoto Onuki22fcc682016-05-17 14:52:19 -07001921 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
1922 ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)),
Makoto Onukid6880792016-06-29 13:37:43 -07001923 disabledMessageString, disabledMessageResId,
Makoto Onuki22fcc682016-05-17 14:52:19 -07001924 /* overrideImmutable=*/ false);
1925 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001926
1927 // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
1928 ps.adjustRanks();
Makoto Onuki22fcc682016-05-17 14:52:19 -07001929 }
1930 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001931
1932 verifyStates();
Makoto Onuki22fcc682016-05-17 14:52:19 -07001933 }
1934
1935 @Override
1936 public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
1937 verifyCaller(packageName, userId);
1938 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
1939
1940 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001941 throwIfUserLockedL(userId);
1942
Makoto Onukic8c33292016-09-12 16:36:59 -07001943 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07001944
1945 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1946
1947 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
1948 ps.enableWithId((String) shortcutIds.get(i));
1949 }
Makoto Onuki20c95f82016-05-11 16:51:01 -07001950 }
1951 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001952
1953 verifyStates();
Makoto Onuki20c95f82016-05-11 16:51:01 -07001954 }
1955
1956 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001957 public void removeDynamicShortcuts(String packageName, List shortcutIds,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001958 @UserIdInt int userId) {
1959 verifyCaller(packageName, userId);
Makoto Onukib6d35232016-04-04 15:57:17 -07001960 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001961
1962 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001963 throwIfUserLockedL(userId);
1964
Makoto Onukic8c33292016-09-12 16:36:59 -07001965 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07001966
1967 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1968
Makoto Onukib6d35232016-04-04 15:57:17 -07001969 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001970 ps.deleteDynamicWithId(
Makoto Onukib6d35232016-04-04 15:57:17 -07001971 Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
1972 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001973
1974 // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
1975 ps.adjustRanks();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001976 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001977 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001978
1979 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001980 }
1981
1982 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001983 public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001984 verifyCaller(packageName, userId);
1985
1986 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07001987 throwIfUserLockedL(userId);
1988
Makoto Onukic8c33292016-09-12 16:36:59 -07001989 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki4e6cef42016-07-13 16:14:01 -07001990 ps.deleteAllDynamicShortcuts();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001991 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001992 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001993
1994 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001995 }
1996
1997 @Override
1998 public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
1999 @UserIdInt int userId) {
2000 verifyCaller(packageName, userId);
Makoto Onuki9c850012016-07-26 15:50:50 -07002001
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002002 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002003 throwIfUserLockedL(userId);
2004
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002005 return getShortcutsWithQueryLocked(
2006 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
Makoto Onuki99302b52017-03-29 12:42:26 -07002007 ShortcutInfo::isDynamic);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002008 }
2009 }
2010
2011 @Override
Makoto Onuki22fcc682016-05-17 14:52:19 -07002012 public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
2013 @UserIdInt int userId) {
2014 verifyCaller(packageName, userId);
Makoto Onuki9c850012016-07-26 15:50:50 -07002015
Makoto Onuki22fcc682016-05-17 14:52:19 -07002016 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002017 throwIfUserLockedL(userId);
2018
Makoto Onuki22fcc682016-05-17 14:52:19 -07002019 return getShortcutsWithQueryLocked(
2020 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
2021 ShortcutInfo::isManifestShortcut);
2022 }
2023 }
2024
2025 @Override
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002026 public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
2027 @UserIdInt int userId) {
2028 verifyCaller(packageName, userId);
Makoto Onuki9c850012016-07-26 15:50:50 -07002029
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002030 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002031 throwIfUserLockedL(userId);
2032
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002033 return getShortcutsWithQueryLocked(
2034 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
2035 ShortcutInfo::isPinned);
2036 }
2037 }
2038
2039 private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
2040 @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
2041
2042 final ArrayList<ShortcutInfo> ret = new ArrayList<>();
2043
Makoto Onukic8c33292016-09-12 16:36:59 -07002044 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002045 ps.findAll(ret, query, cloneFlags);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002046
Makoto Onukibf563b62017-05-04 10:25:30 -07002047 return new ParceledListSlice<>(setReturnedByServer(ret));
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002048 }
2049
2050 @Override
Makoto Onukid6880792016-06-29 13:37:43 -07002051 public int getMaxShortcutCountPerActivity(String packageName, @UserIdInt int userId)
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002052 throws RemoteException {
2053 verifyCaller(packageName, userId);
2054
Makoto Onukib5a012f2016-06-21 11:13:53 -07002055 return mMaxShortcuts;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002056 }
2057
2058 @Override
2059 public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
2060 verifyCaller(packageName, userId);
2061
2062 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002063 throwIfUserLockedL(userId);
2064
Makoto Onukic8c33292016-09-12 16:36:59 -07002065 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002066 return mMaxUpdatesPerInterval - ps.getApiCallCount();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002067 }
2068 }
2069
2070 @Override
2071 public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
2072 verifyCaller(packageName, userId);
2073
2074 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002075 throwIfUserLockedL(userId);
2076
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002077 return getNextResetTimeLocked();
2078 }
2079 }
2080
Makoto Onuki55046222016-03-08 10:49:47 -08002081 @Override
Makoto Onuki20c95f82016-05-11 16:51:01 -07002082 public int getIconMaxDimensions(String packageName, int userId) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002083 verifyCaller(packageName, userId);
2084
Makoto Onuki55046222016-03-08 10:49:47 -08002085 synchronized (mLock) {
2086 return mMaxIconDimension;
2087 }
2088 }
2089
Makoto Onuki20c95f82016-05-11 16:51:01 -07002090 @Override
2091 public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
2092 verifyCaller(packageName, userId);
2093
Makoto Onukiac042502016-05-20 16:39:42 -07002094 Preconditions.checkNotNull(shortcutId);
2095
2096 if (DEBUG) {
2097 Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
2098 shortcutId, packageName, userId));
2099 }
2100
2101 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002102 throwIfUserLockedL(userId);
2103
Makoto Onukic8c33292016-09-12 16:36:59 -07002104 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002105
Makoto Onukiac042502016-05-20 16:39:42 -07002106 if (ps.findShortcutById(shortcutId) == null) {
2107 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s",
2108 packageName, shortcutId));
2109 return;
2110 }
2111 }
2112
2113 final long token = injectClearCallingIdentity();
2114 try {
2115 mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
2116 } finally {
2117 injectRestoreCallingIdentity(token);
2118 }
Makoto Onuki20c95f82016-05-11 16:51:01 -07002119 }
2120
Makoto Onuki2d895c32016-12-02 15:48:40 -08002121 @Override
Sunny Goyal7f7372a2017-01-24 11:53:54 -08002122 public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
Makoto Onuki2d895c32016-12-02 15:48:40 -08002123 final long token = injectClearCallingIdentity();
2124 try {
Sunny Goyal7f7372a2017-01-24 11:53:54 -08002125 return mShortcutRequestPinProcessor
2126 .isRequestPinItemSupported(callingUserId, requestType);
Makoto Onuki2d895c32016-12-02 15:48:40 -08002127 } finally {
2128 injectRestoreCallingIdentity(token);
2129 }
2130 }
2131
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002132 /**
Makoto Onukib08790c2016-06-23 14:05:46 -07002133 * Reset all throttling, for developer options and command line. Only system/shell can call
2134 * it.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002135 */
2136 @Override
2137 public void resetThrottling() {
2138 enforceSystemOrShell();
2139
Makoto Onuki4554d0e2016-03-14 15:51:41 -07002140 resetThrottlingInner(getCallingUserId());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002141 }
2142
Makoto Onuki4554d0e2016-03-14 15:51:41 -07002143 void resetThrottlingInner(@UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002144 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002145 if (!isUserUnlockedL(userId)) {
2146 Log.w(TAG, "User " + userId + " is locked or not running");
2147 return;
2148 }
2149
Makoto Onuki4554d0e2016-03-14 15:51:41 -07002150 getUserShortcutsLocked(userId).resetThrottling();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002151 }
Makoto Onuki4554d0e2016-03-14 15:51:41 -07002152 scheduleSaveUser(userId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002153 Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
2154 }
2155
2156 void resetAllThrottlingInner() {
2157 synchronized (mLock) {
2158 mRawLastResetTime = injectCurrentTimeMillis();
2159 }
2160 scheduleSaveBaseState();
2161 Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002162 }
2163
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002164 @Override
2165 public void onApplicationActive(String packageName, int userId) {
2166 if (DEBUG) {
2167 Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
2168 }
2169 enforceResetThrottlingPermission();
Makoto Onuki02f338e2016-07-29 09:40:40 -07002170
2171 synchronized (mLock) {
2172 if (!isUserUnlockedL(userId)) {
2173 // This is called by system UI, so no need to throw. Just ignore.
2174 return;
2175 }
2176
2177 getPackageShortcutsLocked(packageName, userId)
2178 .resetRateLimitingForCommandLineNoSaving();
2179 saveUserLocked(userId);
Makoto Onuki9c850012016-07-26 15:50:50 -07002180 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002181 }
2182
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002183 // We override this method in unit tests to do a simpler check.
2184 boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
Makoto Onuki10305202016-07-14 18:14:08 -07002185 final long start = injectElapsedRealtime();
2186 try {
2187 return hasShortcutHostPermissionInner(callingPackage, userId);
2188 } finally {
2189 logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
2190 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002191 }
2192
2193 // This method is extracted so we can directly call this method from unit tests,
2194 // even when hasShortcutPermission() is overridden.
2195 @VisibleForTesting
Makoto Onuki2d895c32016-12-02 15:48:40 -08002196 boolean hasShortcutHostPermissionInner(@NonNull String packageName, int userId) {
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002197 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002198 throwIfUserLockedL(userId);
2199
Makoto Onuki31459242016-03-22 11:12:18 -07002200 final ShortcutUser user = getUserShortcutsLocked(userId);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002201
Makoto Onuki2d895c32016-12-02 15:48:40 -08002202 // Always trust the cached component.
Makoto Onuki10305202016-07-14 18:14:08 -07002203 final ComponentName cached = user.getCachedLauncher();
2204 if (cached != null) {
Makoto Onuki2d895c32016-12-02 15:48:40 -08002205 if (cached.getPackageName().equals(packageName)) {
Makoto Onuki10305202016-07-14 18:14:08 -07002206 return true;
2207 }
2208 }
2209 // If the cached one doesn't match, then go ahead
2210
Makoto Onuki2d895c32016-12-02 15:48:40 -08002211 final ComponentName detected = getDefaultLauncher(userId);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002212
Makoto Onuki10305202016-07-14 18:14:08 -07002213 // Update the cache.
2214 user.setLauncher(detected);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002215 if (detected != null) {
2216 if (DEBUG) {
2217 Slog.v(TAG, "Detected launcher: " + detected);
2218 }
Makoto Onuki2d895c32016-12-02 15:48:40 -08002219 return detected.getPackageName().equals(packageName);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002220 } else {
2221 // Default launcher not found.
2222 return false;
2223 }
2224 }
2225 }
2226
Makoto Onuki2d895c32016-12-02 15:48:40 -08002227 @Nullable
2228 ComponentName getDefaultLauncher(@UserIdInt int userId) {
2229 final long start = injectElapsedRealtime();
2230 final long token = injectClearCallingIdentity();
2231 try {
2232 synchronized (mLock) {
2233 throwIfUserLockedL(userId);
2234
2235 final ShortcutUser user = getUserShortcutsLocked(userId);
2236
2237 final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
2238
2239 // Default launcher from package manager.
2240 final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
2241 final ComponentName defaultLauncher = mPackageManagerInternal
2242 .getHomeActivitiesAsUser(allHomeCandidates, userId);
2243 logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
2244
2245 ComponentName detected = null;
2246 if (defaultLauncher != null) {
2247 detected = defaultLauncher;
2248 if (DEBUG) {
2249 Slog.v(TAG, "Default launcher from PM: " + detected);
2250 }
2251 } else {
2252 detected = user.getLastKnownLauncher();
2253
2254 if (detected != null) {
2255 if (injectIsActivityEnabledAndExported(detected, userId)) {
2256 if (DEBUG) {
2257 Slog.v(TAG, "Cached launcher: " + detected);
2258 }
2259 } else {
2260 Slog.w(TAG, "Cached launcher " + detected + " no longer exists");
2261 detected = null;
2262 user.clearLauncher();
2263 }
2264 }
2265 }
2266
2267 if (detected == null) {
2268 // If we reach here, that means it's the first check since the user was created,
2269 // and there's already multiple launchers and there's no default set.
2270 // Find the system one with the highest priority.
2271 // (We need to check the priority too because of FallbackHome in Settings.)
2272 // If there's no system launcher yet, then no one can access shortcuts, until
2273 // the user explicitly
2274 final int size = allHomeCandidates.size();
2275
2276 int lastPriority = Integer.MIN_VALUE;
2277 for (int i = 0; i < size; i++) {
2278 final ResolveInfo ri = allHomeCandidates.get(i);
2279 if (!ri.activityInfo.applicationInfo.isSystemApp()) {
2280 continue;
2281 }
2282 if (DEBUG) {
2283 Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
2284 ri.activityInfo.getComponentName(), ri.priority));
2285 }
2286 if (ri.priority < lastPriority) {
2287 continue;
2288 }
2289 detected = ri.activityInfo.getComponentName();
2290 lastPriority = ri.priority;
2291 }
2292 }
2293 return detected;
2294 }
2295 } finally {
2296 injectRestoreCallingIdentity(token);
2297 logDurationStat(Stats.GET_DEFAULT_LAUNCHER, start);
2298 }
2299 }
2300
Makoto Onukicdc78f72016-03-21 15:47:52 -07002301 // === House keeping ===
2302
Makoto Onukib08790c2016-06-23 14:05:46 -07002303 private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId,
2304 boolean appStillExists) {
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002305 synchronized (mLock) {
2306 forEachLoadedUserLocked(user ->
Makoto Onukib08790c2016-06-23 14:05:46 -07002307 cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
2308 appStillExists));
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002309 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002310 }
2311
Makoto Onuki2e210c42016-03-30 08:30:36 -07002312 /**
2313 * Remove all the information associated with a package. This will really remove all the
2314 * information, including the restore information (i.e. it'll remove packages even if they're
2315 * shadow).
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002316 *
2317 * This is called when an app is uninstalled, or an app gets "clear data"ed.
Makoto Onuki2e210c42016-03-30 08:30:36 -07002318 */
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002319 @VisibleForTesting
Makoto Onukib08790c2016-06-23 14:05:46 -07002320 void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
2321 boolean appStillExists) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002322 final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002323
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002324 final ShortcutUser user = getUserShortcutsLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002325 boolean doNotify = false;
2326
2327 // First, remove the package from the package list (if the package is a publisher).
Makoto Onukid99c6f02016-03-28 11:02:54 -07002328 if (packageUserId == owningUserId) {
Makoto Onukic51b2872016-05-04 15:24:50 -07002329 if (user.removePackage(packageName) != null) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002330 doNotify = true;
2331 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07002332 }
Makoto Onukid99c6f02016-03-28 11:02:54 -07002333
Makoto Onukicdc78f72016-03-21 15:47:52 -07002334 // Also remove from the launcher list (if the package is a launcher).
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002335 user.removeLauncher(packageUserId, packageName);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002336
2337 // Then remove pinned shortcuts from all launchers.
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002338 user.forAllLaunchers(l -> l.cleanUpPackage(packageName, packageUserId));
2339
2340 // Now there may be orphan shortcuts because we removed pinned shortcuts at the previous
Makoto Onukicdc78f72016-03-21 15:47:52 -07002341 // step. Remove them too.
Makoto Onukic51b2872016-05-04 15:24:50 -07002342 user.forAllPackages(p -> p.refreshPinnedFlags());
Makoto Onukicdc78f72016-03-21 15:47:52 -07002343
Makoto Onukid99c6f02016-03-28 11:02:54 -07002344 scheduleSaveUser(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002345
2346 if (doNotify) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002347 notifyListeners(packageName, owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002348 }
2349
Makoto Onukib08790c2016-06-23 14:05:46 -07002350 // If the app still exists (i.e. data cleared), we need to re-publish manifest shortcuts.
2351 if (appStillExists && (packageUserId == owningUserId)) {
2352 // This will do the notification and save when needed, so do it after the above
2353 // notifyListeners.
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002354 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
Makoto Onukib08790c2016-06-23 14:05:46 -07002355 }
2356
Makoto Onukicdc78f72016-03-21 15:47:52 -07002357 if (!wasUserLoaded) {
2358 // Note this will execute the scheduled save.
Makoto Onukid99c6f02016-03-28 11:02:54 -07002359 unloadUserLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002360 }
2361 }
2362
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002363 /**
2364 * Entry point from {@link LauncherApps}.
2365 */
2366 private class LocalService extends ShortcutServiceInternal {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002367
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002368 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002369 public List<ShortcutInfo> getShortcuts(int launcherUserId,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002370 @NonNull String callingPackage, long changedSince,
Makoto Onukiabe84422016-04-07 09:41:19 -07002371 @Nullable String packageName, @Nullable List<String> shortcutIds,
Makoto Onuki99302b52017-03-29 12:42:26 -07002372 @Nullable ComponentName componentName,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002373 int queryFlags, int userId) {
2374 final ArrayList<ShortcutInfo> ret = new ArrayList<>();
Makoto Onuki9c850012016-07-26 15:50:50 -07002375
Makoto Onuki20c95f82016-05-11 16:51:01 -07002376 final boolean cloneKeyFieldOnly =
2377 ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
2378 final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO
2379 : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
Makoto Onukiabe84422016-04-07 09:41:19 -07002380 if (packageName == null) {
2381 shortcutIds = null; // LauncherAppsService already threw for it though.
2382 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002383
2384 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002385 throwIfUserLockedL(userId);
2386 throwIfUserLockedL(launcherUserId);
2387
Makoto Onuki2e210c42016-03-30 08:30:36 -07002388 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002389 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002390
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002391 if (packageName != null) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002392 getShortcutsInnerLocked(launcherUserId,
Makoto Onukiabe84422016-04-07 09:41:19 -07002393 callingPackage, packageName, shortcutIds, changedSince,
Makoto Onuki99302b52017-03-29 12:42:26 -07002394 componentName, queryFlags, userId, ret, cloneFlag);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002395 } else {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002396 final List<String> shortcutIdsF = shortcutIds;
2397 getUserShortcutsLocked(userId).forAllPackages(p -> {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002398 getShortcutsInnerLocked(launcherUserId,
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002399 callingPackage, p.getPackageName(), shortcutIdsF, changedSince,
Makoto Onuki99302b52017-03-29 12:42:26 -07002400 componentName, queryFlags, userId, ret, cloneFlag);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002401 });
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002402 }
2403 }
Makoto Onukibf563b62017-05-04 10:25:30 -07002404 return setReturnedByServer(ret);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002405 }
2406
Makoto Onukid99c6f02016-03-28 11:02:54 -07002407 private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
Makoto Onukiabe84422016-04-07 09:41:19 -07002408 @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
Makoto Onuki99302b52017-03-29 12:42:26 -07002409 @Nullable ComponentName componentName, int queryFlags,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002410 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
Makoto Onukiabe84422016-04-07 09:41:19 -07002411 final ArraySet<String> ids = shortcutIds == null ? null
2412 : new ArraySet<>(shortcutIds);
2413
Makoto Onukic51b2872016-05-04 15:24:50 -07002414 final ShortcutPackage p = getUserShortcutsLocked(userId)
2415 .getPackageShortcutsIfExists(packageName);
2416 if (p == null) {
2417 return; // No need to instantiate ShortcutPackage.
2418 }
2419
2420 p.findAll(ret,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002421 (ShortcutInfo si) -> {
2422 if (si.getLastChangedTimestamp() < changedSince) {
2423 return false;
2424 }
Makoto Onukiabe84422016-04-07 09:41:19 -07002425 if (ids != null && !ids.contains(si.getId())) {
2426 return false;
2427 }
Makoto Onuki85694522016-05-04 12:53:37 -07002428 if (componentName != null) {
Makoto Onuki9fd90192017-01-06 18:31:03 +00002429 if (si.getActivity() != null
2430 && !si.getActivity().equals(componentName)) {
Makoto Onuki85694522016-05-04 12:53:37 -07002431 return false;
2432 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002433 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002434 if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
2435 && si.isDynamic()) {
2436 return true;
2437 }
2438 if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
2439 && si.isPinned()) {
2440 return true;
2441 }
2442 if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0)
2443 && si.isManifestShortcut()) {
2444 return true;
2445 }
2446 return false;
Makoto Onukid99c6f02016-03-28 11:02:54 -07002447 }, cloneFlag, callingPackage, launcherUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002448 }
2449
2450 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002451 public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
2452 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2453 Preconditions.checkStringNotEmpty(packageName, "packageName");
2454 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
2455
2456 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002457 throwIfUserLockedL(userId);
2458 throwIfUserLockedL(launcherUserId);
2459
Makoto Onuki2e210c42016-03-30 08:30:36 -07002460 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002461 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002462
Makoto Onukid99c6f02016-03-28 11:02:54 -07002463 final ShortcutInfo si = getShortcutInfoLocked(
2464 launcherUserId, callingPackage, packageName, shortcutId, userId);
2465 return si != null && si.isPinned();
2466 }
2467 }
2468
Makoto Onuki2e210c42016-03-30 08:30:36 -07002469 private ShortcutInfo getShortcutInfoLocked(
Makoto Onukid99c6f02016-03-28 11:02:54 -07002470 int launcherUserId, @NonNull String callingPackage,
2471 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2472 Preconditions.checkStringNotEmpty(packageName, "packageName");
2473 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
2474
Makoto Onuki02f338e2016-07-29 09:40:40 -07002475 throwIfUserLockedL(userId);
2476 throwIfUserLockedL(launcherUserId);
Makoto Onuki9c850012016-07-26 15:50:50 -07002477
Makoto Onukic51b2872016-05-04 15:24:50 -07002478 final ShortcutPackage p = getUserShortcutsLocked(userId)
2479 .getPackageShortcutsIfExists(packageName);
2480 if (p == null) {
2481 return null;
2482 }
2483
Makoto Onukid99c6f02016-03-28 11:02:54 -07002484 final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
Makoto Onukic51b2872016-05-04 15:24:50 -07002485 p.findAll(list,
Makoto Onukid99c6f02016-03-28 11:02:54 -07002486 (ShortcutInfo si) -> shortcutId.equals(si.getId()),
2487 /* clone flags=*/ 0, callingPackage, launcherUserId);
2488 return list.size() == 0 ? null : list.get(0);
2489 }
2490
2491 @Override
2492 public void pinShortcuts(int launcherUserId,
2493 @NonNull String callingPackage, @NonNull String packageName,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002494 @NonNull List<String> shortcutIds, int userId) {
2495 // Calling permission must be checked by LauncherAppsImpl.
2496 Preconditions.checkStringNotEmpty(packageName, "packageName");
2497 Preconditions.checkNotNull(shortcutIds, "shortcutIds");
2498
2499 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002500 throwIfUserLockedL(userId);
2501 throwIfUserLockedL(launcherUserId);
2502
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002503 final ShortcutLauncher launcher =
Makoto Onuki2e210c42016-03-30 08:30:36 -07002504 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
Makoto Onukic51b2872016-05-04 15:24:50 -07002505 launcher.attemptToRestoreIfNeededAndSave();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002506
Makoto Onukic51b2872016-05-04 15:24:50 -07002507 launcher.pinShortcuts(userId, packageName, shortcutIds);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002508 }
Makoto Onuki39686e82016-04-13 18:03:00 -07002509 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07002510
2511 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002512 }
2513
2514 @Override
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002515 public Intent[] createShortcutIntents(int launcherUserId,
Makoto Onukid99c6f02016-03-28 11:02:54 -07002516 @NonNull String callingPackage,
Makoto Onuki43204b82016-03-08 16:16:44 -08002517 @NonNull String packageName, @NonNull String shortcutId, int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002518 // Calling permission must be checked by LauncherAppsImpl.
Makoto Onuki43204b82016-03-08 16:16:44 -08002519 Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
2520 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002521
2522 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002523 throwIfUserLockedL(userId);
2524 throwIfUserLockedL(launcherUserId);
2525
Makoto Onuki2e210c42016-03-30 08:30:36 -07002526 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002527 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002528
Makoto Onukid99c6f02016-03-28 11:02:54 -07002529 // Make sure the shortcut is actually visible to the launcher.
2530 final ShortcutInfo si = getShortcutInfoLocked(
2531 launcherUserId, callingPackage, packageName, shortcutId, userId);
2532 // "si == null" should suffice here, but check the flags too just to make sure.
Makoto Onuki22fcc682016-05-17 14:52:19 -07002533 if (si == null || !si.isEnabled() || !si.isAlive()) {
Makoto Onuki83f6d2d2016-07-11 14:30:19 -07002534 Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
Makoto Onukid99c6f02016-03-28 11:02:54 -07002535 return null;
2536 }
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002537 return si.getIntents();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002538 }
2539 }
2540
2541 @Override
2542 public void addListener(@NonNull ShortcutChangeListener listener) {
2543 synchronized (mLock) {
2544 mListeners.add(Preconditions.checkNotNull(listener));
2545 }
2546 }
Makoto Onuki55046222016-03-08 10:49:47 -08002547
2548 @Override
Makoto Onukiabe84422016-04-07 09:41:19 -07002549 public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
2550 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2551 Preconditions.checkNotNull(callingPackage, "callingPackage");
2552 Preconditions.checkNotNull(packageName, "packageName");
2553 Preconditions.checkNotNull(shortcutId, "shortcutId");
Makoto Onuki55046222016-03-08 10:49:47 -08002554
2555 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002556 throwIfUserLockedL(userId);
2557 throwIfUserLockedL(launcherUserId);
2558
Makoto Onuki2e210c42016-03-30 08:30:36 -07002559 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002560 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002561
Makoto Onukic51b2872016-05-04 15:24:50 -07002562 final ShortcutPackage p = getUserShortcutsLocked(userId)
2563 .getPackageShortcutsIfExists(packageName);
2564 if (p == null) {
2565 return 0;
2566 }
2567
2568 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
Makoto Onuki55046222016-03-08 10:49:47 -08002569 return (shortcutInfo != null && shortcutInfo.hasIconResource())
2570 ? shortcutInfo.getIconResourceId() : 0;
2571 }
2572 }
2573
2574 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002575 public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
Makoto Onukiabe84422016-04-07 09:41:19 -07002576 @NonNull String callingPackage, @NonNull String packageName,
2577 @NonNull String shortcutId, int userId) {
2578 Preconditions.checkNotNull(callingPackage, "callingPackage");
2579 Preconditions.checkNotNull(packageName, "packageName");
2580 Preconditions.checkNotNull(shortcutId, "shortcutId");
Makoto Onuki55046222016-03-08 10:49:47 -08002581
2582 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002583 throwIfUserLockedL(userId);
2584 throwIfUserLockedL(launcherUserId);
2585
Makoto Onuki2e210c42016-03-30 08:30:36 -07002586 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002587 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002588
Makoto Onukic51b2872016-05-04 15:24:50 -07002589 final ShortcutPackage p = getUserShortcutsLocked(userId)
2590 .getPackageShortcutsIfExists(packageName);
2591 if (p == null) {
2592 return null;
2593 }
2594
2595 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
Makoto Onuki55046222016-03-08 10:49:47 -08002596 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
2597 return null;
2598 }
Makoto Onuki475c3652017-05-08 14:29:03 -07002599 final String path = mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcutInfo);
2600 if (path == null) {
2601 Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
2602 return null;
2603 }
Makoto Onuki55046222016-03-08 10:49:47 -08002604 try {
2605 return ParcelFileDescriptor.open(
Makoto Onuki475c3652017-05-08 14:29:03 -07002606 new File(path),
Makoto Onuki55046222016-03-08 10:49:47 -08002607 ParcelFileDescriptor.MODE_READ_ONLY);
2608 } catch (FileNotFoundException e) {
Makoto Onuki475c3652017-05-08 14:29:03 -07002609 Slog.e(TAG, "Icon file not found: " + path);
Makoto Onuki55046222016-03-08 10:49:47 -08002610 return null;
2611 }
2612 }
2613 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002614
2615 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002616 public boolean hasShortcutHostPermission(int launcherUserId,
2617 @NonNull String callingPackage) {
2618 return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002619 }
Sunny Goyal87a563e2017-01-01 19:42:45 -08002620
2621 @Override
2622 public boolean requestPinAppWidget(@NonNull String callingPackage,
Sunny Goyal4ad6b572017-02-28 11:11:51 -08002623 @NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras,
2624 @Nullable IntentSender resultIntent, int userId) {
Sunny Goyal87a563e2017-01-01 19:42:45 -08002625 Preconditions.checkNotNull(appWidget);
Sunny Goyal4ad6b572017-02-28 11:11:51 -08002626 return requestPinItem(callingPackage, userId, null, appWidget, extras, resultIntent);
Sunny Goyal87a563e2017-01-01 19:42:45 -08002627 }
Sunny Goyal7f7372a2017-01-24 11:53:54 -08002628
2629 @Override
2630 public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
2631 return ShortcutService.this.isRequestPinItemSupported(callingUserId, requestType);
2632 }
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002633 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002634
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002635 final BroadcastReceiver mReceiver = new BroadcastReceiver() {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002636 @Override
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002637 public void onReceive(Context context, Intent intent) {
2638 if (!mBootCompleted.get()) {
2639 return; // Boot not completed, ignore the broadcast.
2640 }
Makoto Onuki02f338e2016-07-29 09:40:40 -07002641 try {
2642 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
2643 handleLocaleChanged();
2644 }
2645 } catch (Exception e) {
2646 wtf("Exception in mReceiver.onReceive", e);
Makoto Onukic51b2872016-05-04 15:24:50 -07002647 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002648 }
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002649 };
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002650
Makoto Onuki157b1622016-06-02 16:13:10 -07002651 void handleLocaleChanged() {
2652 if (DEBUG) {
2653 Slog.d(TAG, "handleLocaleChanged");
2654 }
2655 scheduleSaveBaseState();
2656
Makoto Onuki02f338e2016-07-29 09:40:40 -07002657 synchronized (mLock) {
2658 final long token = injectClearCallingIdentity();
2659 try {
2660 forEachLoadedUserLocked(user -> user.detectLocaleChange());
2661 } finally {
2662 injectRestoreCallingIdentity(token);
2663 }
Makoto Onuki157b1622016-06-02 16:13:10 -07002664 }
2665 }
2666
Makoto Onukif34c3082016-07-13 10:25:25 -07002667 /**
2668 * Package event callbacks.
2669 */
2670 @VisibleForTesting
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002671 final BroadcastReceiver mPackageMonitor = new BroadcastReceiver() {
Makoto Onukif34c3082016-07-13 10:25:25 -07002672 @Override
2673 public void onReceive(Context context, Intent intent) {
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002674 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
2675 if (userId == UserHandle.USER_NULL) {
2676 Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
2677 return;
2678 }
2679
2680 final String action = intent.getAction();
2681
2682 // This is normally called on Handler, so clearCallingIdentity() isn't needed,
2683 // but we still check it in unit tests.
Makoto Onukif34c3082016-07-13 10:25:25 -07002684 final long token = injectClearCallingIdentity();
2685 try {
Makoto Onuki10305202016-07-14 18:14:08 -07002686 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07002687 if (!isUserUnlockedL(userId)) {
2688 if (DEBUG) {
2689 Slog.d(TAG, "Ignoring package broadcast " + action
2690 + " for locked/stopped user " + userId);
2691 }
2692 return;
2693 }
2694
2695 // Whenever we get one of those package broadcasts, or get
2696 // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache.
Makoto Onuki10305202016-07-14 18:14:08 -07002697 final ShortcutUser user = getUserShortcutsLocked(userId);
2698 user.clearLauncher();
2699 }
2700 if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) {
2701 // Nothing farther to do.
2702 return;
2703 }
2704
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002705 final Uri intentUri = intent.getData();
2706 final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart()
2707 : null;
2708 if (packageName == null) {
2709 Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
2710 return;
2711 }
2712
2713 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
2714
2715 switch (action) {
2716 case Intent.ACTION_PACKAGE_ADDED:
2717 if (replacing) {
2718 handlePackageUpdateFinished(packageName, userId);
2719 } else {
2720 handlePackageAdded(packageName, userId);
2721 }
2722 break;
2723 case Intent.ACTION_PACKAGE_REMOVED:
2724 if (!replacing) {
2725 handlePackageRemoved(packageName, userId);
2726 }
2727 break;
2728 case Intent.ACTION_PACKAGE_CHANGED:
2729 handlePackageChanged(packageName, userId);
2730
2731 break;
2732 case Intent.ACTION_PACKAGE_DATA_CLEARED:
2733 handlePackageDataCleared(packageName, userId);
2734 break;
2735 }
Makoto Onuki02f338e2016-07-29 09:40:40 -07002736 } catch (Exception e) {
2737 wtf("Exception in mPackageMonitor.onReceive", e);
Makoto Onukif34c3082016-07-13 10:25:25 -07002738 } finally {
2739 injectRestoreCallingIdentity(token);
Makoto Onukia2241832016-07-06 13:28:37 -07002740 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07002741 }
Makoto Onukif34c3082016-07-13 10:25:25 -07002742 };
Makoto Onukicdc78f72016-03-21 15:47:52 -07002743
Makoto Onuki0acbb142016-03-22 17:02:57 -07002744 /**
Makoto Onuki39686e82016-04-13 18:03:00 -07002745 * Called when a user is unlocked.
2746 * - Check all known packages still exist, and otherwise perform cleanup.
2747 * - If a package still exists, check the version code. If it's been updated, may need to
Makoto Onukib08790c2016-06-23 14:05:46 -07002748 * update timestamps of its shortcuts.
Makoto Onuki0acbb142016-03-22 17:02:57 -07002749 */
Makoto Onukid99c6f02016-03-28 11:02:54 -07002750 @VisibleForTesting
Makoto Onuki39686e82016-04-13 18:03:00 -07002751 void checkPackageChanges(@UserIdInt int ownerUserId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -07002752 if (DEBUG) {
Makoto Onuki39686e82016-04-13 18:03:00 -07002753 Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002754 }
Makoto Onukib08790c2016-06-23 14:05:46 -07002755 if (injectIsSafeModeEnabled()) {
2756 Slog.i(TAG, "Safe mode, skipping checkPackageChanges()");
2757 return;
2758 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002759
Makoto Onuki22fcc682016-05-17 14:52:19 -07002760 final long start = injectElapsedRealtime();
2761 try {
2762 final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002763
Makoto Onuki22fcc682016-05-17 14:52:19 -07002764 synchronized (mLock) {
2765 final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
2766
2767 // Find packages that have been uninstalled.
2768 user.forAllPackageItems(spi -> {
2769 if (spi.getPackageInfo().isShadow()) {
2770 return; // Don't delete shadow information.
2771 }
2772 if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002773 if (DEBUG) {
2774 Slog.d(TAG, "Uninstalled: " + spi.getPackageName()
2775 + " user " + spi.getPackageUserId());
2776 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002777 gonePackages.add(PackageWithUser.of(spi));
2778 }
2779 });
2780 if (gonePackages.size() > 0) {
2781 for (int i = gonePackages.size() - 1; i >= 0; i--) {
2782 final PackageWithUser pu = gonePackages.get(i);
Makoto Onukib08790c2016-06-23 14:05:46 -07002783 cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId,
2784 /* appStillExists = */ false);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002785 }
Makoto Onukid99c6f02016-03-28 11:02:54 -07002786 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002787
Makoto Onuki248a0ef2016-11-03 15:59:01 -07002788 rescanUpdatedPackagesLocked(ownerUserId, user.getLastAppScanTime());
Makoto Onuki0acbb142016-03-22 17:02:57 -07002789 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002790 } finally {
2791 logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002792 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002793 verifyStates();
Makoto Onukicdc78f72016-03-21 15:47:52 -07002794 }
2795
Makoto Onuki248a0ef2016-11-03 15:59:01 -07002796 private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
Makoto Onuki377b7972016-08-09 14:43:55 -07002797 final ShortcutUser user = getUserShortcutsLocked(userId);
2798
Makoto Onuki33663282016-08-22 16:19:04 -07002799 // Note after each OTA, we'll need to rescan all system apps, as their lastUpdateTime
2800 // is not reliable.
Makoto Onuki377b7972016-08-09 14:43:55 -07002801 final long now = injectCurrentTimeMillis();
Makoto Onuki33663282016-08-22 16:19:04 -07002802 final boolean afterOta =
2803 !injectBuildFingerprint().equals(user.getLastAppScanOsFingerprint());
Makoto Onuki377b7972016-08-09 14:43:55 -07002804
2805 // Then for each installed app, publish manifest shortcuts when needed.
Makoto Onuki33663282016-08-22 16:19:04 -07002806 forUpdatedPackages(userId, lastScanTime, afterOta, ai -> {
Makoto Onuki377b7972016-08-09 14:43:55 -07002807 user.attemptToRestoreIfNeededAndSave(this, ai.packageName, userId);
Makoto Onuki248a0ef2016-11-03 15:59:01 -07002808
2809 user.rescanPackageIfNeeded(ai.packageName, /* forceRescan= */ true);
Makoto Onuki377b7972016-08-09 14:43:55 -07002810 });
2811
2812 // Write the time just before the scan, because there may be apps that have just
2813 // been updated, and we want to catch them in the next time.
2814 user.setLastAppScanTime(now);
Makoto Onuki33663282016-08-22 16:19:04 -07002815 user.setLastAppScanOsFingerprint(injectBuildFingerprint());
Makoto Onuki377b7972016-08-09 14:43:55 -07002816 scheduleSaveUser(userId);
2817 }
2818
Makoto Onuki0acbb142016-03-22 17:02:57 -07002819 private void handlePackageAdded(String packageName, @UserIdInt int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -07002820 if (DEBUG) {
Makoto Onuki0acbb142016-03-22 17:02:57 -07002821 Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
2822 }
2823 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002824 final ShortcutUser user = getUserShortcutsLocked(userId);
2825 user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
Makoto Onuki64183d52016-08-08 14:11:34 -07002826 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002827 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002828 verifyStates();
Makoto Onuki0acbb142016-03-22 17:02:57 -07002829 }
2830
2831 private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
Makoto Onuki905e8852016-03-28 10:40:58 -07002832 if (DEBUG) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002833 Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
2834 packageName, userId));
Makoto Onuki0acbb142016-03-22 17:02:57 -07002835 }
2836 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002837 final ShortcutUser user = getUserShortcutsLocked(userId);
2838 user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
Makoto Onuki39686e82016-04-13 18:03:00 -07002839
Makoto Onuki22fcc682016-05-17 14:52:19 -07002840 if (isPackageInstalled(packageName, userId)) {
Makoto Onuki64183d52016-08-08 14:11:34 -07002841 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
Makoto Onuki39686e82016-04-13 18:03:00 -07002842 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002843 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002844 verifyStates();
Makoto Onuki0acbb142016-03-22 17:02:57 -07002845 }
2846
Makoto Onuki2e210c42016-03-30 08:30:36 -07002847 private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
Makoto Onuki0acbb142016-03-22 17:02:57 -07002848 if (DEBUG) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002849 Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
2850 packageUserId));
Makoto Onukicdc78f72016-03-21 15:47:52 -07002851 }
Makoto Onukib08790c2016-06-23 14:05:46 -07002852 cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false);
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002853
2854 verifyStates();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002855 }
2856
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002857 private void handlePackageDataCleared(String packageName, int packageUserId) {
2858 if (DEBUG) {
2859 Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
2860 packageUserId));
Makoto Onukicdc78f72016-03-21 15:47:52 -07002861 }
Makoto Onukib08790c2016-06-23 14:05:46 -07002862 cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true);
2863
2864 verifyStates();
2865 }
2866
2867 private void handlePackageChanged(String packageName, int packageUserId) {
Makoto Onuki82fb2eb2017-03-31 16:58:26 -07002868 if (!isPackageInstalled(packageName, packageUserId)) {
2869 // Probably disabled, which is the same thing as uninstalled.
2870 handlePackageRemoved(packageName, packageUserId);
2871 return;
2872 }
Makoto Onukib08790c2016-06-23 14:05:46 -07002873 if (DEBUG) {
2874 Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName,
2875 packageUserId));
2876 }
2877
2878 // Activities may be disabled or enabled. Just rescan the package.
2879 synchronized (mLock) {
2880 final ShortcutUser user = getUserShortcutsLocked(packageUserId);
2881
Makoto Onuki4e6cef42016-07-13 16:14:01 -07002882 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
Makoto Onukib08790c2016-06-23 14:05:46 -07002883 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002884
2885 verifyStates();
Makoto Onukicdc78f72016-03-21 15:47:52 -07002886 }
2887
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002888 // === PackageManager interaction ===
Makoto Onuki0acbb142016-03-22 17:02:57 -07002889
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002890 /**
2891 * Returns {@link PackageInfo} unless it's uninstalled or disabled.
2892 */
Makoto Onuki22fcc682016-05-17 14:52:19 -07002893 @Nullable
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002894 final PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
2895 return getPackageInfo(packageName, userId, true);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002896 }
2897
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002898 /**
2899 * Returns {@link PackageInfo} unless it's uninstalled or disabled.
2900 */
Makoto Onuki22fcc682016-05-17 14:52:19 -07002901 @Nullable
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002902 final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) {
2903 return getPackageInfo(packageName, userId, false);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002904 }
2905
Makoto Onuki905e8852016-03-28 10:40:58 -07002906 int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002907 final long token = injectClearCallingIdentity();
Makoto Onuki905e8852016-03-28 10:40:58 -07002908 try {
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002909 return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS, userId);
Makoto Onuki905e8852016-03-28 10:40:58 -07002910 } catch (RemoteException e) {
2911 // Shouldn't happen.
2912 Slog.wtf(TAG, "RemoteException", e);
2913 return -1;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002914 } finally {
2915 injectRestoreCallingIdentity(token);
Makoto Onuki905e8852016-03-28 10:40:58 -07002916 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002917 }
2918
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002919 /**
2920 * Returns {@link PackageInfo} unless it's uninstalled or disabled.
2921 */
Makoto Onuki22fcc682016-05-17 14:52:19 -07002922 @Nullable
Makoto Onuki0acbb142016-03-22 17:02:57 -07002923 @VisibleForTesting
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002924 final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId,
2925 boolean getSignatures) {
2926 return isInstalledOrNull(injectPackageInfoWithUninstalled(
2927 packageName, userId, getSignatures));
2928 }
2929
2930 /**
2931 * Do not use directly; this returns uninstalled packages too.
2932 */
2933 @Nullable
2934 @VisibleForTesting
2935 PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
Makoto Onuki0acbb142016-03-22 17:02:57 -07002936 boolean getSignatures) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07002937 final long start = injectElapsedRealtime();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002938 final long token = injectClearCallingIdentity();
Makoto Onuki0acbb142016-03-22 17:02:57 -07002939 try {
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002940 return mIPackageManager.getPackageInfo(
2941 packageName, PACKAGE_MATCH_FLAGS
2942 | (getSignatures ? PackageManager.GET_SIGNATURES : 0), userId);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002943 } catch (RemoteException e) {
2944 // Shouldn't happen.
2945 Slog.wtf(TAG, "RemoteException", e);
2946 return null;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002947 } finally {
2948 injectRestoreCallingIdentity(token);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002949
2950 logDurationStat(
2951 (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO),
2952 start);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002953 }
2954 }
2955
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002956 /**
2957 * Returns {@link ApplicationInfo} unless it's uninstalled or disabled.
2958 */
Makoto Onuki22fcc682016-05-17 14:52:19 -07002959 @Nullable
Makoto Onuki905e8852016-03-28 10:40:58 -07002960 @VisibleForTesting
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002961 final ApplicationInfo getApplicationInfo(String packageName, @UserIdInt int userId) {
2962 return isInstalledOrNull(injectApplicationInfoWithUninstalled(packageName, userId));
2963 }
2964
2965 /**
2966 * Do not use directly; this returns uninstalled packages too.
2967 */
2968 @Nullable
2969 @VisibleForTesting
2970 ApplicationInfo injectApplicationInfoWithUninstalled(
2971 String packageName, @UserIdInt int userId) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07002972 final long start = injectElapsedRealtime();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002973 final long token = injectClearCallingIdentity();
Makoto Onuki905e8852016-03-28 10:40:58 -07002974 try {
2975 return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
2976 } catch (RemoteException e) {
2977 // Shouldn't happen.
2978 Slog.wtf(TAG, "RemoteException", e);
2979 return null;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002980 } finally {
2981 injectRestoreCallingIdentity(token);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002982
2983 logDurationStat(Stats.GET_APPLICATION_INFO, start);
Makoto Onuki905e8852016-03-28 10:40:58 -07002984 }
2985 }
2986
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002987 /**
2988 * Returns {@link ActivityInfo} with its metadata unless it's uninstalled or disabled.
2989 */
Makoto Onuki22fcc682016-05-17 14:52:19 -07002990 @Nullable
Makoto Onukiee6b6e42016-06-29 17:34:02 -07002991 final ActivityInfo getActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) {
2992 return isInstalledOrNull(injectGetActivityInfoWithMetadataWithUninstalled(
2993 activity, userId));
2994 }
2995
2996 /**
2997 * Do not use directly; this returns uninstalled packages too.
2998 */
2999 @Nullable
3000 @VisibleForTesting
3001 ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled(
3002 ComponentName activity, @UserIdInt int userId) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07003003 final long start = injectElapsedRealtime();
3004 final long token = injectClearCallingIdentity();
3005 try {
Makoto Onukib08790c2016-06-23 14:05:46 -07003006 return mIPackageManager.getActivityInfo(activity,
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003007 (PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA), userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07003008 } catch (RemoteException e) {
3009 // Shouldn't happen.
3010 Slog.wtf(TAG, "RemoteException", e);
3011 return null;
3012 } finally {
3013 injectRestoreCallingIdentity(token);
3014
Makoto Onukib08790c2016-06-23 14:05:46 -07003015 logDurationStat(Stats.GET_ACTIVITY_WITH_METADATA, start);
Makoto Onuki22fcc682016-05-17 14:52:19 -07003016 }
3017 }
3018
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003019 /**
3020 * Return all installed and enabled packages.
3021 */
3022 @NonNull
Makoto Onuki22fcc682016-05-17 14:52:19 -07003023 @VisibleForTesting
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003024 final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07003025 final long start = injectElapsedRealtime();
3026 final long token = injectClearCallingIdentity();
3027 try {
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003028 final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId);
3029
3030 all.removeIf(PACKAGE_NOT_INSTALLED);
3031
3032 return all;
Makoto Onuki22fcc682016-05-17 14:52:19 -07003033 } catch (RemoteException e) {
3034 // Shouldn't happen.
3035 Slog.wtf(TAG, "RemoteException", e);
3036 return null;
3037 } finally {
3038 injectRestoreCallingIdentity(token);
3039
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07003040 logDurationStat(Stats.GET_INSTALLED_PACKAGES, start);
Makoto Onuki22fcc682016-05-17 14:52:19 -07003041 }
3042 }
3043
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003044 /**
3045 * Do not use directly; this returns uninstalled packages too.
3046 */
3047 @NonNull
3048 @VisibleForTesting
3049 List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId)
3050 throws RemoteException {
3051 final ParceledListSlice<PackageInfo> parceledList =
3052 mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId);
3053 if (parceledList == null) {
3054 return Collections.emptyList();
3055 }
3056 return parceledList.getList();
3057 }
3058
Makoto Onuki33663282016-08-22 16:19:04 -07003059 private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta,
Makoto Onuki22fcc682016-05-17 14:52:19 -07003060 Consumer<ApplicationInfo> callback) {
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07003061 if (DEBUG) {
Makoto Onuki248a0ef2016-11-03 15:59:01 -07003062 Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime
3063 + " afterOta=" + afterOta);
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07003064 }
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003065 final List<PackageInfo> list = getInstalledPackages(userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07003066 for (int i = list.size() - 1; i >= 0; i--) {
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07003067 final PackageInfo pi = list.get(i);
Makoto Onuki22fcc682016-05-17 14:52:19 -07003068
Makoto Onuki64183d52016-08-08 14:11:34 -07003069 // If the package has been updated since the last scan time, then scan it.
Makoto Onuki248a0ef2016-11-03 15:59:01 -07003070 // Also if it's right after an OTA, always re-scan all apps anyway, since the
3071 // shortcut parser might have changed.
3072 if (afterOta || (pi.lastUpdateTime >= lastScanTime)) {
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07003073 if (DEBUG) {
Makoto Onuki248a0ef2016-11-03 15:59:01 -07003074 Slog.d(TAG, "Found updated package " + pi.packageName
3075 + " updateTime=" + pi.lastUpdateTime);
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07003076 }
3077 callback.accept(pi.applicationInfo);
Makoto Onuki22fcc682016-05-17 14:52:19 -07003078 }
3079 }
3080 }
3081
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003082 private boolean isApplicationFlagSet(@NonNull String packageName, int userId, int flags) {
3083 final ApplicationInfo ai = injectApplicationInfoWithUninstalled(packageName, userId);
Makoto Onuki905e8852016-03-28 10:40:58 -07003084 return (ai != null) && ((ai.flags & flags) == flags);
3085 }
3086
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003087 private static boolean isInstalled(@Nullable ApplicationInfo ai) {
Makoto Onuki82fb2eb2017-03-31 16:58:26 -07003088 return (ai != null) && ai.enabled && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003089 }
3090
Makoto Onuki66e4a2b2017-01-23 11:37:45 -08003091 private static boolean isEphemeralApp(@Nullable ApplicationInfo ai) {
Svetoslav Ganov096d3042017-01-30 16:34:13 -08003092 return (ai != null) && ai.isInstantApp();
Makoto Onuki66e4a2b2017-01-23 11:37:45 -08003093 }
3094
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003095 private static boolean isInstalled(@Nullable PackageInfo pi) {
3096 return (pi != null) && isInstalled(pi.applicationInfo);
3097 }
3098
3099 private static boolean isInstalled(@Nullable ActivityInfo ai) {
3100 return (ai != null) && isInstalled(ai.applicationInfo);
3101 }
3102
3103 private static ApplicationInfo isInstalledOrNull(ApplicationInfo ai) {
3104 return isInstalled(ai) ? ai : null;
3105 }
3106
3107 private static PackageInfo isInstalledOrNull(PackageInfo pi) {
3108 return isInstalled(pi) ? pi : null;
3109 }
3110
3111 private static ActivityInfo isInstalledOrNull(ActivityInfo ai) {
3112 return isInstalled(ai) ? ai : null;
3113 }
3114
Makoto Onuki2e210c42016-03-30 08:30:36 -07003115 boolean isPackageInstalled(String packageName, int userId) {
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003116 return getApplicationInfo(packageName, userId) != null;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003117 }
3118
Makoto Onuki66e4a2b2017-01-23 11:37:45 -08003119 boolean isEphemeralApp(String packageName, int userId) {
3120 return isEphemeralApp(getApplicationInfo(packageName, userId));
3121 }
3122
Makoto Onuki22fcc682016-05-17 14:52:19 -07003123 @Nullable
3124 XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07003125 return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
Makoto Onuki39686e82016-04-13 18:03:00 -07003126 }
3127
Makoto Onuki157b1622016-06-02 16:13:10 -07003128 @Nullable
3129 Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
3130 final long start = injectElapsedRealtime();
3131 final long token = injectClearCallingIdentity();
3132 try {
3133 return mContext.getPackageManager().getResourcesForApplicationAsUser(
3134 packageName, userId);
3135 } catch (NameNotFoundException e) {
3136 Slog.e(TAG, "Resources for package " + packageName + " not found");
3137 return null;
3138 } finally {
3139 injectRestoreCallingIdentity(token);
3140
3141 logDurationStat(Stats.GET_APPLICATION_RESOURCES, start);
3142 }
3143 }
3144
Makoto Onukib08790c2016-06-23 14:05:46 -07003145 private Intent getMainActivityIntent() {
3146 final Intent intent = new Intent(Intent.ACTION_MAIN);
3147 intent.addCategory(LAUNCHER_INTENT_CATEGORY);
3148 return intent;
3149 }
3150
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003151 /**
3152 * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed,
3153 * and only returns exported activities.
3154 */
3155 @NonNull
3156 @VisibleForTesting
3157 List<ResolveInfo> queryActivities(@NonNull Intent baseIntent,
3158 @NonNull String packageName, @Nullable ComponentName activity, int userId) {
3159
3160 baseIntent.setPackage(Preconditions.checkNotNull(packageName));
3161 if (activity != null) {
3162 baseIntent.setComponent(activity);
3163 }
Makoto Onuki2d895c32016-12-02 15:48:40 -08003164 return queryActivities(baseIntent, userId, /* exportedOnly =*/ true);
3165 }
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003166
Makoto Onuki2d895c32016-12-02 15:48:40 -08003167 @NonNull
3168 List<ResolveInfo> queryActivities(@NonNull Intent intent, int userId,
3169 boolean exportedOnly) {
3170 final List<ResolveInfo> resolved;
3171 final long token = injectClearCallingIdentity();
3172 try {
3173 resolved =
3174 mContext.getPackageManager().queryIntentActivitiesAsUser(
3175 intent, PACKAGE_MATCH_FLAGS, userId);
3176 } finally {
3177 injectRestoreCallingIdentity(token);
3178 }
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003179 if (resolved == null || resolved.size() == 0) {
3180 return EMPTY_RESOLVE_INFO;
3181 }
3182 // Make sure the package is installed.
3183 if (!isInstalled(resolved.get(0).activityInfo)) {
3184 return EMPTY_RESOLVE_INFO;
3185 }
Makoto Onuki2d895c32016-12-02 15:48:40 -08003186 if (exportedOnly) {
3187 resolved.removeIf(ACTIVITY_NOT_EXPORTED);
3188 }
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003189 return resolved;
3190 }
3191
3192 /**
3193 * Return the main activity that is enabled and exported. If multiple activities are found,
3194 * return the first one.
3195 */
Makoto Onukib08790c2016-06-23 14:05:46 -07003196 @Nullable
3197 ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
3198 final long start = injectElapsedRealtime();
Makoto Onukib08790c2016-06-23 14:05:46 -07003199 try {
Makoto Onukib08790c2016-06-23 14:05:46 -07003200 final List<ResolveInfo> resolved =
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003201 queryActivities(getMainActivityIntent(), packageName, null, userId);
3202 return resolved.size() == 0 ? null : resolved.get(0).activityInfo.getComponentName();
Makoto Onukib08790c2016-06-23 14:05:46 -07003203 } finally {
Makoto Onukib08790c2016-06-23 14:05:46 -07003204 logDurationStat(Stats.GET_LAUNCHER_ACTIVITY, start);
3205 }
3206 }
3207
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003208 /**
3209 * Return whether an activity is enabled, exported and main.
3210 */
Makoto Onukib08790c2016-06-23 14:05:46 -07003211 boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
3212 final long start = injectElapsedRealtime();
Makoto Onukib08790c2016-06-23 14:05:46 -07003213 try {
Makoto Onuki34145532017-03-14 17:58:36 -07003214 if (activity == null) {
3215 wtf("null activity detected");
3216 return false;
3217 }
Makoto Onuki2d895c32016-12-02 15:48:40 -08003218 if (DUMMY_MAIN_ACTIVITY.equals(activity.getClassName())) {
3219 return true;
3220 }
3221 final List<ResolveInfo> resolved = queryActivities(
3222 getMainActivityIntent(), activity.getPackageName(), activity, userId);
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003223 return resolved.size() > 0;
Makoto Onukib08790c2016-06-23 14:05:46 -07003224 } finally {
Makoto Onukib08790c2016-06-23 14:05:46 -07003225 logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
3226 }
3227 }
3228
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003229 /**
Makoto Onuki2d895c32016-12-02 15:48:40 -08003230 * Create a dummy "main activity" component name which is used to create a dynamic shortcut
3231 * with no main activity temporarily.
3232 */
3233 @NonNull
3234 ComponentName getDummyMainActivity(@NonNull String packageName) {
3235 return new ComponentName(packageName, DUMMY_MAIN_ACTIVITY);
3236 }
3237
Makoto Onuki255461f2017-01-10 11:47:25 -08003238 boolean isDummyMainActivity(@Nullable ComponentName name) {
3239 return name != null && DUMMY_MAIN_ACTIVITY.equals(name.getClassName());
3240 }
3241
Makoto Onuki2d895c32016-12-02 15:48:40 -08003242 /**
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003243 * Return all the enabled, exported and main activities from a package.
3244 */
Makoto Onukib08790c2016-06-23 14:05:46 -07003245 @NonNull
3246 List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
3247 final long start = injectElapsedRealtime();
Makoto Onukib08790c2016-06-23 14:05:46 -07003248 try {
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003249 return queryActivities(getMainActivityIntent(), packageName, null, userId);
Makoto Onukib08790c2016-06-23 14:05:46 -07003250 } finally {
Makoto Onukib08790c2016-06-23 14:05:46 -07003251 logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
3252 }
3253 }
3254
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003255 /**
3256 * Return whether an activity is enabled and exported.
3257 */
3258 @VisibleForTesting
3259 boolean injectIsActivityEnabledAndExported(
3260 @NonNull ComponentName activity, @UserIdInt int userId) {
3261 final long start = injectElapsedRealtime();
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003262 try {
3263 return queryActivities(new Intent(), activity.getPackageName(), activity, userId)
3264 .size() > 0;
3265 } finally {
Makoto Onukiee6b6e42016-06-29 17:34:02 -07003266 logDurationStat(Stats.IS_ACTIVITY_ENABLED, start);
3267 }
3268 }
3269
Makoto Onuki2d895c32016-12-02 15:48:40 -08003270 /**
Sunny Goyal7f7372a2017-01-24 11:53:54 -08003271 * Get the {@link LauncherApps#ACTION_CONFIRM_PIN_SHORTCUT} or
3272 * {@link LauncherApps#ACTION_CONFIRM_PIN_APPWIDGET} activity in a given package depending on
3273 * the requestType.
Makoto Onuki2d895c32016-12-02 15:48:40 -08003274 */
3275 @Nullable
3276 ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
Sunny Goyal7f7372a2017-01-24 11:53:54 -08003277 int launcherUserId, int requestType) {
Makoto Onuki2d895c32016-12-02 15:48:40 -08003278 Preconditions.checkNotNull(launcherPackageName);
Sunny Goyal7f7372a2017-01-24 11:53:54 -08003279 String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
3280 LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
3281 LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
Makoto Onuki2d895c32016-12-02 15:48:40 -08003282
Sunny Goyal7f7372a2017-01-24 11:53:54 -08003283 final Intent confirmIntent = new Intent(action).setPackage(launcherPackageName);
Makoto Onuki2d895c32016-12-02 15:48:40 -08003284 final List<ResolveInfo> candidates = queryActivities(
3285 confirmIntent, launcherUserId, /* exportedOnly =*/ false);
3286 for (ResolveInfo ri : candidates) {
3287 return ri.activityInfo.getComponentName();
3288 }
3289 return null;
3290 }
3291
Makoto Onukib08790c2016-06-23 14:05:46 -07003292 boolean injectIsSafeModeEnabled() {
3293 final long token = injectClearCallingIdentity();
3294 try {
3295 return IWindowManager.Stub
3296 .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE))
3297 .isSafeModeEnabled();
3298 } catch (RemoteException e) {
3299 return false; // Shouldn't happen though.
3300 } finally {
3301 injectRestoreCallingIdentity(token);
3302 }
3303 }
3304
Makoto Onuki2d895c32016-12-02 15:48:40 -08003305 /**
3306 * If {@code userId} is of a managed profile, return the parent user ID. Otherwise return
3307 * itself.
3308 */
3309 int getParentOrSelfUserId(int userId) {
3310 final long token = injectClearCallingIdentity();
3311 try {
3312 final UserInfo parent = mUserManager.getProfileParent(userId);
3313 return (parent != null) ? parent.id : userId;
3314 } finally {
3315 injectRestoreCallingIdentity(token);
3316 }
3317 }
3318
Sunny Goyal87a563e2017-01-01 19:42:45 -08003319 void injectSendIntentSender(IntentSender intentSender, Intent extras) {
Makoto Onuki2d895c32016-12-02 15:48:40 -08003320 if (intentSender == null) {
3321 return;
3322 }
3323 try {
Sunny Goyal87a563e2017-01-01 19:42:45 -08003324 intentSender.sendIntent(mContext, /* code= */ 0, extras,
Makoto Onuki2d895c32016-12-02 15:48:40 -08003325 /* onFinished=*/ null, /* handler= */ null);
3326 } catch (SendIntentException e) {
3327 Slog.w(TAG, "sendIntent failed().", e);
3328 }
3329 }
3330
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003331 // === Backup & restore ===
3332
Makoto Onuki0acbb142016-03-22 17:02:57 -07003333 boolean shouldBackupApp(String packageName, int userId) {
Makoto Onuki905e8852016-03-28 10:40:58 -07003334 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
Makoto Onuki0acbb142016-03-22 17:02:57 -07003335 }
3336
Makoto Onuki2e210c42016-03-30 08:30:36 -07003337 boolean shouldBackupApp(PackageInfo pi) {
3338 return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
3339 }
3340
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003341 @Override
Makoto Onuki2e210c42016-03-30 08:30:36 -07003342 public byte[] getBackupPayload(@UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003343 enforceSystem();
3344 if (DEBUG) {
3345 Slog.d(TAG, "Backing up user " + userId);
3346 }
3347 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003348 if (!isUserUnlockedL(userId)) {
3349 wtf("Can't backup: user " + userId + " is locked or not running");
3350 return null;
3351 }
3352
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003353 final ShortcutUser user = getUserShortcutsLocked(userId);
3354 if (user == null) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003355 wtf("Can't backup: user not found: id=" + userId);
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003356 return null;
3357 }
3358
Makoto Onukic8c33292016-09-12 16:36:59 -07003359 // Update the signatures for all packages.
3360 user.forAllPackageItems(spi -> spi.refreshPackageSignatureAndSave());
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003361
Makoto Onukic8c33292016-09-12 16:36:59 -07003362 // Set the version code for the launchers.
3363 // We shouldn't do this for publisher packages, because we don't want to update the
3364 // version code without rescanning the manifest.
3365 user.forAllLaunchers(launcher -> launcher.ensureVersionInfo());
3366
3367 // Save to the filesystem.
3368 scheduleSaveUser(userId);
3369 saveDirtyInfo();
3370
Makoto Onuki475c3652017-05-08 14:29:03 -07003371 // Note, in case of backup, we don't have to wait on bitmap saving, because we don't
3372 // back up bitmaps anyway.
3373
Makoto Onukic8c33292016-09-12 16:36:59 -07003374 // Then create the backup payload.
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003375 final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024);
3376 try {
3377 saveUserInternalLocked(userId, os, /* forBackup */ true);
Makoto Onukib08790c2016-06-23 14:05:46 -07003378 } catch (XmlPullParserException | IOException e) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003379 // Shouldn't happen.
3380 Slog.w(TAG, "Backup failed.", e);
3381 return null;
3382 }
3383 return os.toByteArray();
3384 }
3385 }
3386
3387 @Override
Makoto Onuki2e210c42016-03-30 08:30:36 -07003388 public void applyRestore(byte[] payload, @UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003389 enforceSystem();
3390 if (DEBUG) {
3391 Slog.d(TAG, "Restoring user " + userId);
3392 }
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003393 synchronized (mLock) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003394 if (!isUserUnlockedL(userId)) {
3395 wtf("Can't restore: user " + userId + " is locked or not running");
3396 return;
3397 }
Makoto Onukifc4cf2d2016-08-24 11:10:26 -07003398 // Actually do restore.
3399 final ShortcutUser restored;
Makoto Onuki02f338e2016-07-29 09:40:40 -07003400 final ByteArrayInputStream is = new ByteArrayInputStream(payload);
3401 try {
Makoto Onukifc4cf2d2016-08-24 11:10:26 -07003402 restored = loadUserInternal(userId, is, /* fromBackup */ true);
3403 } catch (XmlPullParserException | IOException | InvalidFileFormatException e) {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003404 Slog.w(TAG, "Restoration failed.", e);
3405 return;
3406 }
Makoto Onukifc4cf2d2016-08-24 11:10:26 -07003407 getUserShortcutsLocked(userId).mergeRestoredFile(restored);
Makoto Onuki2e210c42016-03-30 08:30:36 -07003408
Makoto Onuki377b7972016-08-09 14:43:55 -07003409 // Rescan all packages to re-publish manifest shortcuts and do other checks.
3410 rescanUpdatedPackagesLocked(userId,
Makoto Onuki248a0ef2016-11-03 15:59:01 -07003411 0 // lastScanTime = 0; rescan all packages.
3412 );
Makoto Onuki2e210c42016-03-30 08:30:36 -07003413
3414 saveUserLocked(userId);
Makoto Onuki9da23fc2016-03-29 11:14:42 -07003415 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07003416 }
3417
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003418 // === Dump ===
3419
3420 @Override
3421 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06003422 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
Makoto Onukic4361e32017-04-03 11:24:25 -07003423 dumpNoCheck(fd, pw, args);
3424 }
3425
3426 @VisibleForTesting
3427 void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) {
Makoto Onuki76269922016-07-15 14:58:54 -07003428 boolean checkin = false;
3429 boolean clear = false;
3430 if (args != null) {
3431 for (String arg : args) {
3432 if ("-c".equals(arg)) {
3433 checkin = true;
3434 } else if ("--checkin".equals(arg)) {
3435 checkin = true;
3436 clear = true;
3437 }
3438 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003439 }
Makoto Onuki76269922016-07-15 14:58:54 -07003440
3441 if (checkin) {
3442 dumpCheckin(pw, clear);
3443 } else {
3444 dumpInner(pw);
3445 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003446 }
3447
Makoto Onuki76269922016-07-15 14:58:54 -07003448 private void dumpInner(PrintWriter pw) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003449 synchronized (mLock) {
3450 final long now = injectCurrentTimeMillis();
3451 pw.print("Now: [");
3452 pw.print(now);
3453 pw.print("] ");
3454 pw.print(formatTime(now));
Makoto Onuki55046222016-03-08 10:49:47 -08003455
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003456 pw.print(" Raw last reset: [");
3457 pw.print(mRawLastResetTime);
3458 pw.print("] ");
3459 pw.print(formatTime(mRawLastResetTime));
3460
3461 final long last = getLastResetTimeLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003462 pw.print(" Last reset: [");
3463 pw.print(last);
3464 pw.print("] ");
3465 pw.print(formatTime(last));
3466
Makoto Onuki55046222016-03-08 10:49:47 -08003467 final long next = getNextResetTimeLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003468 pw.print(" Next reset: [");
3469 pw.print(next);
3470 pw.print("] ");
3471 pw.print(formatTime(next));
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07003472
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003473 pw.print(" Config:");
3474 pw.print(" Max icon dim: ");
3475 pw.println(mMaxIconDimension);
3476 pw.print(" Icon format: ");
3477 pw.println(mIconPersistFormat);
3478 pw.print(" Icon quality: ");
Makoto Onuki2e210c42016-03-30 08:30:36 -07003479 pw.println(mIconPersistQuality);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07003480 pw.print(" saveDelayMillis: ");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003481 pw.println(mSaveDelayMillis);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07003482 pw.print(" resetInterval: ");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003483 pw.println(mResetInterval);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07003484 pw.print(" maxUpdatesPerInterval: ");
Makoto Onukib6d35232016-04-04 15:57:17 -07003485 pw.println(mMaxUpdatesPerInterval);
Makoto Onukib08790c2016-06-23 14:05:46 -07003486 pw.print(" maxShortcutsPerActivity: ");
Makoto Onukib5a012f2016-06-21 11:13:53 -07003487 pw.println(mMaxShortcuts);
Makoto Onuki55046222016-03-08 10:49:47 -08003488 pw.println();
3489
Makoto Onuki2e210c42016-03-30 08:30:36 -07003490 pw.println(" Stats:");
3491 synchronized (mStatLock) {
Makoto Onuki085a05c2016-08-19 11:39:29 -07003492 for (int i = 0; i < Stats.COUNT; i++) {
3493 dumpStatLS(pw, " ", i);
3494 }
Makoto Onuki2e210c42016-03-30 08:30:36 -07003495 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003496
Makoto Onukia2241832016-07-06 13:28:37 -07003497 pw.println();
3498 pw.print(" #Failures: ");
3499 pw.println(mWtfCount);
3500
3501 if (mLastWtfStacktrace != null) {
3502 pw.print(" Last failure stack trace: ");
3503 pw.println(Log.getStackTraceString(mLastWtfStacktrace));
3504 }
3505
Makoto Onuki475c3652017-05-08 14:29:03 -07003506 pw.println();
3507 mShortcutBitmapSaver.dumpLocked(pw, " ");
3508
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08003509 for (int i = 0; i < mUsers.size(); i++) {
3510 pw.println();
Makoto Onukic51b2872016-05-04 15:24:50 -07003511 mUsers.valueAt(i).dump(pw, " ");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003512 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07003513
3514 pw.println();
3515 pw.println(" UID state:");
3516
3517 for (int i = 0; i < mUidState.size(); i++) {
3518 final int uid = mUidState.keyAt(i);
3519 final int state = mUidState.valueAt(i);
3520 pw.print(" UID=");
3521 pw.print(uid);
3522 pw.print(" state=");
3523 pw.print(state);
3524 if (isProcessStateForeground(state)) {
3525 pw.print(" [FG]");
3526 }
3527 pw.print(" last FG=");
3528 pw.print(mUidLastForegroundElapsedTime.get(uid));
3529 pw.println();
3530 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003531 }
3532 }
3533
Makoto Onuki41066a62016-03-09 16:18:44 -08003534 static String formatTime(long time) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003535 Time tobj = new Time();
3536 tobj.set(time);
3537 return tobj.format("%Y-%m-%d %H:%M:%S");
3538 }
3539
Makoto Onuki085a05c2016-08-19 11:39:29 -07003540 private void dumpStatLS(PrintWriter pw, String prefix, int statId) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07003541 pw.print(prefix);
3542 final int count = mCountStats[statId];
3543 final long dur = mDurationStats[statId];
3544 pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
Makoto Onuki085a05c2016-08-19 11:39:29 -07003545 STAT_LABELS[statId], count, dur,
Makoto Onuki2e210c42016-03-30 08:30:36 -07003546 (count == 0 ? 0 : ((double) dur) / count)));
3547 }
3548
Makoto Onuki76269922016-07-15 14:58:54 -07003549 /**
3550 * Dumpsys for checkin.
3551 *
3552 * @param clear if true, clear the history information. Some other system services have this
3553 * behavior but shortcut service doesn't for now.
3554 */
3555 private void dumpCheckin(PrintWriter pw, boolean clear) {
3556 synchronized (mLock) {
3557 try {
3558 final JSONArray users = new JSONArray();
3559
3560 for (int i = 0; i < mUsers.size(); i++) {
3561 users.put(mUsers.valueAt(i).dumpCheckin(clear));
3562 }
3563
3564 final JSONObject result = new JSONObject();
3565
3566 result.put(KEY_SHORTCUT, users);
3567 result.put(KEY_LOW_RAM, injectIsLowRamDevice());
3568 result.put(KEY_ICON_SIZE, mMaxIconDimension);
3569
3570 pw.println(result.toString(1));
3571 } catch (JSONException e) {
3572 Slog.e(TAG, "Unable to write in json", e);
3573 }
3574 }
3575 }
3576
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003577 // === Shell support ===
3578
3579 @Override
3580 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07003581 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003582
3583 enforceShell();
3584
Makoto Onuki0b9d1db2016-07-18 14:16:41 -07003585 final long token = injectClearCallingIdentity();
3586 try {
Dianne Hackborn354736e2016-08-22 17:00:05 -07003587 final int status = (new MyShellCommand()).exec(this, in, out, err, args, callback,
3588 resultReceiver);
Makoto Onuki0b9d1db2016-07-18 14:16:41 -07003589 resultReceiver.send(status, null);
3590 } finally {
3591 injectRestoreCallingIdentity(token);
3592 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003593 }
3594
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003595 static class CommandException extends Exception {
3596 public CommandException(String message) {
3597 super(message);
3598 }
3599 }
3600
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003601 /**
3602 * Handle "adb shell cmd".
3603 */
3604 private class MyShellCommand extends ShellCommand {
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003605
3606 private int mUserId = UserHandle.USER_SYSTEM;
3607
Makoto Onuki02f338e2016-07-29 09:40:40 -07003608 private void parseOptionsLocked(boolean takeUser)
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003609 throws CommandException {
3610 String opt;
3611 while ((opt = getNextOption()) != null) {
3612 switch (opt) {
3613 case "--user":
3614 if (takeUser) {
3615 mUserId = UserHandle.parseUserArg(getNextArgRequired());
Makoto Onuki02f338e2016-07-29 09:40:40 -07003616 if (!isUserUnlockedL(mUserId)) {
Makoto Onukif34c3082016-07-13 10:25:25 -07003617 throw new CommandException(
3618 "User " + mUserId + " is not running or locked");
3619 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003620 break;
3621 }
3622 // fallthrough
3623 default:
3624 throw new CommandException("Unknown option: " + opt);
3625 }
3626 }
3627 }
3628
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003629 @Override
3630 public int onCommand(String cmd) {
3631 if (cmd == null) {
3632 return handleDefaultCommands(cmd);
3633 }
3634 final PrintWriter pw = getOutPrintWriter();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003635 try {
3636 switch (cmd) {
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003637 case "reset-throttling":
3638 handleResetThrottling();
3639 break;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003640 case "reset-all-throttling":
3641 handleResetAllThrottling();
3642 break;
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003643 case "override-config":
3644 handleOverrideConfig();
3645 break;
3646 case "reset-config":
3647 handleResetConfig();
3648 break;
3649 case "clear-default-launcher":
3650 handleClearDefaultLauncher();
3651 break;
3652 case "get-default-launcher":
3653 handleGetDefaultLauncher();
3654 break;
Makoto Onukiac214972016-04-04 10:19:45 -07003655 case "unload-user":
3656 handleUnloadUser();
3657 break;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003658 case "clear-shortcuts":
3659 handleClearShortcuts();
3660 break;
Makoto Onukib08790c2016-06-23 14:05:46 -07003661 case "verify-states": // hidden command to verify various internal states.
3662 handleVerifyStates();
3663 break;
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003664 default:
3665 return handleDefaultCommands(cmd);
3666 }
3667 } catch (CommandException e) {
3668 pw.println("Error: " + e.getMessage());
3669 return 1;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003670 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003671 pw.println("Success");
3672 return 0;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003673 }
3674
3675 @Override
3676 public void onHelp() {
3677 final PrintWriter pw = getOutPrintWriter();
3678 pw.println("Usage: cmd shortcut COMMAND [options ...]");
3679 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003680 pw.println("cmd shortcut reset-throttling [--user USER_ID]");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003681 pw.println(" Reset throttling for all packages and users");
3682 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003683 pw.println("cmd shortcut reset-all-throttling");
3684 pw.println(" Reset the throttling state for all users");
3685 pw.println();
Makoto Onuki4362a662016-03-08 18:59:09 -08003686 pw.println("cmd shortcut override-config CONFIG");
3687 pw.println(" Override the configuration for testing (will last until reboot)");
3688 pw.println();
3689 pw.println("cmd shortcut reset-config");
3690 pw.println(" Reset the configuration set with \"update-config\"");
3691 pw.println();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003692 pw.println("cmd shortcut clear-default-launcher [--user USER_ID]");
3693 pw.println(" Clear the cached default launcher");
3694 pw.println();
3695 pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
Makoto Onuki0b9d1db2016-07-18 14:16:41 -07003696 pw.println(" Show the default launcher");
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003697 pw.println();
Makoto Onukiac214972016-04-04 10:19:45 -07003698 pw.println("cmd shortcut unload-user [--user USER_ID]");
3699 pw.println(" Unload a user from the memory");
3700 pw.println(" (This should not affect any observable behavior)");
3701 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003702 pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
3703 pw.println(" Remove all shortcuts from a package, including pinned shortcuts");
3704 pw.println();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003705 }
3706
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003707 private void handleResetThrottling() throws CommandException {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003708 synchronized (mLock) {
3709 parseOptionsLocked(/* takeUser =*/ true);
Makoto Onuki4554d0e2016-03-14 15:51:41 -07003710
Makoto Onuki02f338e2016-07-29 09:40:40 -07003711 Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003712
Makoto Onuki02f338e2016-07-29 09:40:40 -07003713 resetThrottlingInner(mUserId);
3714 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003715 }
3716
3717 private void handleResetAllThrottling() {
3718 Slog.i(TAG, "cmd: handleResetAllThrottling");
3719
3720 resetAllThrottlingInner();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003721 }
3722
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003723 private void handleOverrideConfig() throws CommandException {
Makoto Onuki4362a662016-03-08 18:59:09 -08003724 final String config = getNextArgRequired();
3725
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003726 Slog.i(TAG, "cmd: handleOverrideConfig: " + config);
3727
Makoto Onuki4362a662016-03-08 18:59:09 -08003728 synchronized (mLock) {
3729 if (!updateConfigurationLocked(config)) {
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003730 throw new CommandException("override-config failed. See logcat for details.");
Makoto Onuki4362a662016-03-08 18:59:09 -08003731 }
3732 }
Makoto Onuki4362a662016-03-08 18:59:09 -08003733 }
3734
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003735 private void handleResetConfig() {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003736 Slog.i(TAG, "cmd: handleResetConfig");
3737
Makoto Onuki4362a662016-03-08 18:59:09 -08003738 synchronized (mLock) {
3739 loadConfigurationLocked();
3740 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003741 }
3742
3743 private void clearLauncher() {
3744 synchronized (mLock) {
Makoto Onuki10305202016-07-14 18:14:08 -07003745 getUserShortcutsLocked(mUserId).forceClearLauncher();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003746 }
3747 }
3748
3749 private void showLauncher() {
3750 synchronized (mLock) {
3751 // This ensures to set the cached launcher. Package name doesn't matter.
3752 hasShortcutHostPermissionInner("-", mUserId);
3753
3754 getOutPrintWriter().println("Launcher: "
Makoto Onuki10305202016-07-14 18:14:08 -07003755 + getUserShortcutsLocked(mUserId).getLastKnownLauncher());
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003756 }
3757 }
3758
3759 private void handleClearDefaultLauncher() throws CommandException {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003760 synchronized (mLock) {
3761 parseOptionsLocked(/* takeUser =*/ true);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003762
Makoto Onuki02f338e2016-07-29 09:40:40 -07003763 clearLauncher();
3764 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003765 }
3766
3767 private void handleGetDefaultLauncher() throws CommandException {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003768 synchronized (mLock) {
3769 parseOptionsLocked(/* takeUser =*/ true);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003770
Makoto Onuki02f338e2016-07-29 09:40:40 -07003771 clearLauncher();
3772 showLauncher();
3773 }
Makoto Onuki4362a662016-03-08 18:59:09 -08003774 }
Makoto Onukiac214972016-04-04 10:19:45 -07003775
3776 private void handleUnloadUser() throws CommandException {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003777 synchronized (mLock) {
3778 parseOptionsLocked(/* takeUser =*/ true);
Makoto Onukiac214972016-04-04 10:19:45 -07003779
Makoto Onuki02f338e2016-07-29 09:40:40 -07003780 Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003781
Makoto Onuki01ce92b2017-04-28 12:24:16 -07003782 ShortcutService.this.handleStopUser(mUserId);
Makoto Onuki02f338e2016-07-29 09:40:40 -07003783 }
Makoto Onukiac214972016-04-04 10:19:45 -07003784 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003785
3786 private void handleClearShortcuts() throws CommandException {
Makoto Onuki02f338e2016-07-29 09:40:40 -07003787 synchronized (mLock) {
3788 parseOptionsLocked(/* takeUser =*/ true);
3789 final String packageName = getNextArgRequired();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003790
Makoto Onuki02f338e2016-07-29 09:40:40 -07003791 Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003792
Makoto Onuki02f338e2016-07-29 09:40:40 -07003793 ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
3794 /* appStillExists = */ true);
3795 }
Makoto Onukib08790c2016-06-23 14:05:46 -07003796 }
3797
3798 private void handleVerifyStates() throws CommandException {
3799 try {
3800 verifyStatesForce(); // This will throw when there's an issue.
3801 } catch (Throwable th) {
3802 throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
3803 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003804 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003805 }
3806
3807 // === Unit test support ===
3808
3809 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07003810 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003811 long injectCurrentTimeMillis() {
3812 return System.currentTimeMillis();
3813 }
3814
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07003815 @VisibleForTesting
3816 long injectElapsedRealtime() {
3817 return SystemClock.elapsedRealtime();
3818 }
3819
Makoto Onuki475c3652017-05-08 14:29:03 -07003820 @VisibleForTesting
3821 long injectUptimeMillis() {
3822 return SystemClock.uptimeMillis();
3823 }
3824
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003825 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07003826 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003827 int injectBinderCallingUid() {
3828 return getCallingUid();
3829 }
3830
Makoto Onuki31459242016-03-22 11:12:18 -07003831 private int getCallingUserId() {
Makoto Onuki4554d0e2016-03-14 15:51:41 -07003832 return UserHandle.getUserId(injectBinderCallingUid());
3833 }
3834
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07003835 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07003836 @VisibleForTesting
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07003837 long injectClearCallingIdentity() {
3838 return Binder.clearCallingIdentity();
3839 }
3840
3841 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07003842 @VisibleForTesting
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07003843 void injectRestoreCallingIdentity(long token) {
3844 Binder.restoreCallingIdentity(token);
3845 }
3846
Makoto Onuki33663282016-08-22 16:19:04 -07003847 // Injection point.
3848 @VisibleForTesting
3849 String injectBuildFingerprint() {
3850 return Build.FINGERPRINT;
3851 }
3852
Makoto Onukide667372016-03-15 14:29:20 -07003853 final void wtf(String message) {
Makoto Onukib08790c2016-06-23 14:05:46 -07003854 wtf(message, /* exception= */ null);
Makoto Onukide667372016-03-15 14:29:20 -07003855 }
3856
Makoto Onuki2e210c42016-03-30 08:30:36 -07003857 // Injection point.
Makoto Onukia2241832016-07-06 13:28:37 -07003858 void wtf(String message, Throwable e) {
3859 if (e == null) {
3860 e = new RuntimeException("Stacktrace");
3861 }
3862 synchronized (mLock) {
3863 mWtfCount++;
3864 mLastWtfStacktrace = new Exception("Last failure was logged here:");
3865 }
Makoto Onukide667372016-03-15 14:29:20 -07003866 Slog.wtf(TAG, message, e);
3867 }
3868
Makoto Onuki31459242016-03-22 11:12:18 -07003869 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003870 File injectSystemDataPath() {
3871 return Environment.getDataSystemDirectory();
3872 }
3873
Makoto Onuki31459242016-03-22 11:12:18 -07003874 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003875 File injectUserDataPath(@UserIdInt int userId) {
Makoto Onuki55046222016-03-08 10:49:47 -08003876 return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
3877 }
3878
Makoto Onuki4362a662016-03-08 18:59:09 -08003879 @VisibleForTesting
Makoto Onuki55046222016-03-08 10:49:47 -08003880 boolean injectIsLowRamDevice() {
3881 return ActivityManager.isLowRamDeviceStatic();
3882 }
3883
Makoto Onuki31459242016-03-22 11:12:18 -07003884 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07003885 void injectRegisterUidObserver(IUidObserver observer, int which) {
3886 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08003887 ActivityManager.getService().registerUidObserver(observer, which,
Dianne Hackborn5614bf52016-11-07 17:26:41 -08003888 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07003889 } catch (RemoteException shouldntHappen) {
3890 }
3891 }
3892
Makoto Onuki55046222016-03-08 10:49:47 -08003893 File getUserBitmapFilePath(@UserIdInt int userId) {
3894 return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003895 }
3896
3897 @VisibleForTesting
Makoto Onuki31459242016-03-22 11:12:18 -07003898 SparseArray<ShortcutUser> getShortcutsForTest() {
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08003899 return mUsers;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003900 }
3901
3902 @VisibleForTesting
Makoto Onukib5a012f2016-06-21 11:13:53 -07003903 int getMaxShortcutsForTest() {
3904 return mMaxShortcuts;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003905 }
3906
3907 @VisibleForTesting
Makoto Onukib6d35232016-04-04 15:57:17 -07003908 int getMaxUpdatesPerIntervalForTest() {
3909 return mMaxUpdatesPerInterval;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003910 }
3911
3912 @VisibleForTesting
Makoto Onuki4362a662016-03-08 18:59:09 -08003913 long getResetIntervalForTest() {
3914 return mResetInterval;
Makoto Onuki55046222016-03-08 10:49:47 -08003915 }
3916
3917 @VisibleForTesting
Makoto Onuki4362a662016-03-08 18:59:09 -08003918 int getMaxIconDimensionForTest() {
3919 return mMaxIconDimension;
3920 }
3921
3922 @VisibleForTesting
3923 CompressFormat getIconPersistFormatForTest() {
3924 return mIconPersistFormat;
3925 }
3926
3927 @VisibleForTesting
3928 int getIconPersistQualityForTest() {
3929 return mIconPersistQuality;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003930 }
Makoto Onuki41066a62016-03-09 16:18:44 -08003931
3932 @VisibleForTesting
Makoto Onuki22fcc682016-05-17 14:52:19 -07003933 ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
Makoto Onuki41066a62016-03-09 16:18:44 -08003934 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07003935 final ShortcutUser user = mUsers.get(userId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07003936 if (user == null) return null;
3937
Makoto Onuki22fcc682016-05-17 14:52:19 -07003938 return user.getAllPackagesForTest().get(packageName);
3939 }
3940 }
3941
3942 @VisibleForTesting
3943 ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
3944 synchronized (mLock) {
Makoto Onukif34c3082016-07-13 10:25:25 -07003945 final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07003946 if (pkg == null) return null;
3947
3948 return pkg.findShortcutById(shortcutId);
Makoto Onuki41066a62016-03-09 16:18:44 -08003949 }
3950 }
Makoto Onuki7001a612016-05-27 13:24:28 -07003951
Makoto Onukifac592f2016-11-21 13:41:32 -08003952 @VisibleForTesting
3953 ShortcutLauncher getLauncherShortcutForTest(String packageName, int userId) {
3954 synchronized (mLock) {
3955 final ShortcutUser user = mUsers.get(userId);
3956 if (user == null) return null;
3957
3958 return user.getAllLaunchersForTest().get(PackageWithUser.of(userId, packageName));
3959 }
3960 }
3961
Makoto Onuki2d895c32016-12-02 15:48:40 -08003962 @VisibleForTesting
3963 ShortcutRequestPinProcessor getShortcutRequestPinProcessorForTest() {
3964 return mShortcutRequestPinProcessor;
3965 }
3966
Makoto Onuki7001a612016-05-27 13:24:28 -07003967 /**
3968 * Control whether {@link #verifyStates} should be performed. We always perform it during unit
3969 * tests.
3970 */
3971 @VisibleForTesting
3972 boolean injectShouldPerformVerification() {
3973 return DEBUG;
3974 }
3975
3976 /**
3977 * Check various internal states and throws if there's any inconsistency.
3978 * This is normally only enabled during unit tests.
3979 */
3980 final void verifyStates() {
3981 if (injectShouldPerformVerification()) {
3982 verifyStatesInner();
3983 }
3984 }
3985
Makoto Onukib08790c2016-06-23 14:05:46 -07003986 private final void verifyStatesForce() {
3987 verifyStatesInner();
3988 }
3989
Makoto Onuki7001a612016-05-27 13:24:28 -07003990 private void verifyStatesInner() {
Makoto Onuki10305202016-07-14 18:14:08 -07003991 synchronized (mLock) {
Makoto Onuki7001a612016-05-27 13:24:28 -07003992 forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
3993 }
3994 }
Makoto Onuki475c3652017-05-08 14:29:03 -07003995
3996 @VisibleForTesting
3997 void waitForBitmapSavesForTest() {
3998 synchronized (mLock) {
3999 mShortcutBitmapSaver.waitForAllSavesLocked();
4000 }
4001 }
Makoto Onuki41066a62016-03-09 16:18:44 -08004002}