blob: 77f70cd5d82421fed7af345482596469b6cd9366 [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 Onuki4d36b3a2016-04-27 12:00:17 -070023import android.app.ActivityManagerNative;
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;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080027import android.content.ComponentName;
28import android.content.Context;
29import android.content.Intent;
Makoto Onuki22fcc682016-05-17 14:52:19 -070030import android.content.pm.ActivityInfo;
Makoto Onuki0acbb142016-03-22 17:02:57 -070031import android.content.pm.ApplicationInfo;
32import android.content.pm.IPackageManager;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080033import android.content.pm.IShortcutService;
34import android.content.pm.LauncherApps;
35import android.content.pm.LauncherApps.ShortcutQuery;
Makoto Onuki0acbb142016-03-22 17:02:57 -070036import android.content.pm.PackageInfo;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080037import android.content.pm.PackageManager;
Makoto Onuki20c95f82016-05-11 16:51:01 -070038import android.content.pm.PackageManager.NameNotFoundException;
Makoto Onuki2d5b4652016-03-11 16:09:54 -080039import android.content.pm.PackageManagerInternal;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080040import android.content.pm.ParceledListSlice;
Makoto Onuki2d5b4652016-03-11 16:09:54 -080041import android.content.pm.ResolveInfo;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080042import android.content.pm.ShortcutInfo;
43import android.content.pm.ShortcutServiceInternal;
44import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
Makoto Onuki157b1622016-06-02 16:13:10 -070045import android.content.res.Resources;
Makoto Onuki22fcc682016-05-17 14:52:19 -070046import android.content.res.XmlResourceParser;
Makoto Onuki55046222016-03-08 10:49:47 -080047import android.graphics.Bitmap;
48import android.graphics.Bitmap.CompressFormat;
Makoto Onuki55046222016-03-08 10:49:47 -080049import android.graphics.Canvas;
50import android.graphics.RectF;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080051import android.graphics.drawable.Icon;
52import android.os.Binder;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080053import android.os.Environment;
Makoto Onuki2e210c42016-03-30 08:30:36 -070054import android.os.FileUtils;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080055import android.os.Handler;
Makoto Onukiaa8b94a2016-03-17 13:14:05 -070056import android.os.Looper;
Makoto Onuki55046222016-03-08 10:49:47 -080057import android.os.ParcelFileDescriptor;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080058import android.os.PersistableBundle;
59import android.os.Process;
60import android.os.RemoteException;
61import android.os.ResultReceiver;
Makoto Onuki55046222016-03-08 10:49:47 -080062import android.os.SELinux;
Makoto Onukib08790c2016-06-23 14:05:46 -070063import android.os.ServiceManager;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080064import android.os.ShellCommand;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070065import android.os.SystemClock;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080066import android.os.UserHandle;
Makoto Onukicdc78f72016-03-21 15:47:52 -070067import android.os.UserManager;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080068import android.text.TextUtils;
69import android.text.format.Time;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080070import android.util.ArraySet;
71import android.util.AtomicFile;
Makoto Onuki4362a662016-03-08 18:59:09 -080072import android.util.KeyValueListParser;
Makoto Onukiac042502016-05-20 16:39:42 -070073import android.util.Log;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080074import android.util.Slog;
75import android.util.SparseArray;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070076import android.util.SparseIntArray;
77import android.util.SparseLongArray;
Makoto Onuki55046222016-03-08 10:49:47 -080078import android.util.TypedValue;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080079import android.util.Xml;
Makoto Onukib08790c2016-06-23 14:05:46 -070080import android.view.IWindowManager;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080081
82import com.android.internal.annotations.GuardedBy;
83import com.android.internal.annotations.VisibleForTesting;
Makoto Onukicdc78f72016-03-21 15:47:52 -070084import com.android.internal.content.PackageMonitor;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080085import com.android.internal.os.BackgroundThread;
86import com.android.internal.util.FastXmlSerializer;
87import com.android.internal.util.Preconditions;
88import com.android.server.LocalServices;
89import com.android.server.SystemService;
Makoto Onukid99c6f02016-03-28 11:02:54 -070090import com.android.server.pm.ShortcutUser.PackageWithUser;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080091
92import libcore.io.IoUtils;
93
94import org.xmlpull.v1.XmlPullParser;
95import org.xmlpull.v1.XmlPullParserException;
96import org.xmlpull.v1.XmlSerializer;
97
Makoto Onuki9da23fc2016-03-29 11:14:42 -070098import java.io.BufferedInputStream;
99import java.io.BufferedOutputStream;
100import java.io.ByteArrayInputStream;
101import java.io.ByteArrayOutputStream;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800102import java.io.File;
103import java.io.FileDescriptor;
104import java.io.FileInputStream;
105import java.io.FileNotFoundException;
106import java.io.FileOutputStream;
107import java.io.IOException;
Makoto Onuki55046222016-03-08 10:49:47 -0800108import java.io.InputStream;
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700109import java.io.OutputStream;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800110import java.io.PrintWriter;
Makoto Onuki7001a612016-05-27 13:24:28 -0700111import java.lang.annotation.Retention;
112import java.lang.annotation.RetentionPolicy;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800113import java.net.URISyntaxException;
114import java.nio.charset.StandardCharsets;
115import java.util.ArrayList;
Makoto Onuki22fcc682016-05-17 14:52:19 -0700116import java.util.Collections;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800117import java.util.List;
Makoto Onukic51b2872016-05-04 15:24:50 -0700118import java.util.concurrent.atomic.AtomicBoolean;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700119import java.util.concurrent.atomic.AtomicLong;
Makoto Onuki2e210c42016-03-30 08:30:36 -0700120import java.util.function.Consumer;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800121import java.util.function.Predicate;
122
123/**
124 * TODO:
Makoto Onuki157b1622016-06-02 16:13:10 -0700125 * - Deal with the async nature of PACKAGE_ADD. Basically when a publisher does anything after
126 * it's upgraded, the manager should make sure the upgrade process has been executed.
127 *
Makoto Onuki22fcc682016-05-17 14:52:19 -0700128 * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
Makoto Onukib5a012f2016-06-21 11:13:53 -0700129 * -> But TypedValue.applyDimension() doesn't differentiate x and y..?
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800130 *
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700131 * - Default launcher check does take a few ms. Worth caching.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800132 *
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700133 * - Detect when already registered instances are passed to APIs again, which might break
Makoto Onukib08790c2016-06-23 14:05:46 -0700134 * internal bitmap handling.
Makoto Onuki2e210c42016-03-30 08:30:36 -0700135 *
136 * - Add more call stats.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800137 */
138public class ShortcutService extends IShortcutService.Stub {
Makoto Onuki55046222016-03-08 10:49:47 -0800139 static final String TAG = "ShortcutService";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800140
Makoto Onuki7001a612016-05-27 13:24:28 -0700141 static final boolean DEBUG = false; // STOPSHIP if true
Makoto Onuki41066a62016-03-09 16:18:44 -0800142 static final boolean DEBUG_LOAD = false; // STOPSHIP if true
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700143 static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800144
Makoto Onuki4362a662016-03-08 18:59:09 -0800145 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700146 static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
Makoto Onuki4362a662016-03-08 18:59:09 -0800147
148 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700149 static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10;
Makoto Onuki4362a662016-03-08 18:59:09 -0800150
151 @VisibleForTesting
152 static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
153
154 @VisibleForTesting
155 static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
156
157 @VisibleForTesting
158 static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
159
160 @VisibleForTesting
161 static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
162
163 @VisibleForTesting
164 static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800165
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700166 @VisibleForTesting
167 static final int DEFAULT_SAVE_DELAY_MS = 3000;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800168
169 @VisibleForTesting
170 static final String FILENAME_BASE_STATE = "shortcut_service.xml";
171
172 @VisibleForTesting
173 static final String DIRECTORY_PER_USER = "shortcut_service";
174
175 @VisibleForTesting
176 static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
177
Makoto Onuki55046222016-03-08 10:49:47 -0800178 static final String DIRECTORY_BITMAPS = "bitmaps";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800179
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700180 private static final String TAG_ROOT = "root";
181 private static final String TAG_LAST_RESET_TIME = "last_reset_time";
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700182 private static final String TAG_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale_seq_no";
Makoto Onuki55046222016-03-08 10:49:47 -0800183
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700184 private static final String ATTR_VALUE = "value";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800185
Makoto Onukib08790c2016-06-23 14:05:46 -0700186 private static final String LAUNCHER_INTENT_CATEGORY = Intent.CATEGORY_LAUNCHER;
187
Makoto Onuki4362a662016-03-08 18:59:09 -0800188 @VisibleForTesting
189 interface ConfigConstants {
190 /**
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700191 * Key name for the save delay, in milliseconds. (int)
192 */
193 String KEY_SAVE_DELAY_MILLIS = "save_delay_ms";
194
195 /**
Makoto Onuki4362a662016-03-08 18:59:09 -0800196 * Key name for the throttling reset interval, in seconds. (long)
197 */
198 String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
199
200 /**
201 * Key name for the max number of modifying API calls per app for every interval. (int)
202 */
Makoto Onukib6d35232016-04-04 15:57:17 -0700203 String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval";
Makoto Onuki4362a662016-03-08 18:59:09 -0800204
205 /**
206 * Key name for the max icon dimensions in DP, for non-low-memory devices.
207 */
208 String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
209
210 /**
211 * Key name for the max icon dimensions in DP, for low-memory devices.
212 */
213 String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
214
215 /**
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700216 * Key name for the max dynamic shortcuts per activity. (int)
Makoto Onuki4362a662016-03-08 18:59:09 -0800217 */
218 String KEY_MAX_SHORTCUTS = "max_shortcuts";
219
220 /**
Makoto Onuki41066a62016-03-09 16:18:44 -0800221 * Key name for icon compression quality, 0-100.
Makoto Onuki4362a662016-03-08 18:59:09 -0800222 */
223 String KEY_ICON_QUALITY = "icon_quality";
224
225 /**
226 * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
227 */
228 String KEY_ICON_FORMAT = "icon_format";
229 }
230
Makoto Onuki41066a62016-03-09 16:18:44 -0800231 final Context mContext;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800232
233 private final Object mLock = new Object();
234
235 private final Handler mHandler;
236
237 @GuardedBy("mLock")
238 private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
239
240 @GuardedBy("mLock")
241 private long mRawLastResetTime;
242
243 /**
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -0800244 * User ID -> UserShortcuts
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800245 */
246 @GuardedBy("mLock")
Makoto Onuki31459242016-03-22 11:12:18 -0700247 private final SparseArray<ShortcutUser> mUsers = new SparseArray<>();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800248
249 /**
Makoto Onukib5a012f2016-06-21 11:13:53 -0700250 * Max number of dynamic + manifest shortcuts that each application can have at a time.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800251 */
Makoto Onukib5a012f2016-06-21 11:13:53 -0700252 private int mMaxShortcuts;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800253
254 /**
Makoto Onukib6d35232016-04-04 15:57:17 -0700255 * Max number of updating API calls that each application can make during the interval.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800256 */
Makoto Onukib6d35232016-04-04 15:57:17 -0700257 int mMaxUpdatesPerInterval;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800258
259 /**
260 * Actual throttling-reset interval. By default it's a day.
261 */
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800262 private long mResetInterval;
263
Makoto Onuki55046222016-03-08 10:49:47 -0800264 /**
265 * Icon max width/height in pixels.
266 */
267 private int mMaxIconDimension;
268
Makoto Onuki4362a662016-03-08 18:59:09 -0800269 private CompressFormat mIconPersistFormat;
270 private int mIconPersistQuality;
Makoto Onuki55046222016-03-08 10:49:47 -0800271
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700272 private int mSaveDelayMillis;
273
Makoto Onuki0acbb142016-03-22 17:02:57 -0700274 private final IPackageManager mIPackageManager;
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800275 private final PackageManagerInternal mPackageManagerInternal;
Makoto Onukicdc78f72016-03-21 15:47:52 -0700276 private final UserManager mUserManager;
Makoto Onukiac042502016-05-20 16:39:42 -0700277 private final UsageStatsManagerInternal mUsageStatsManagerInternal;
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800278
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700279 @GuardedBy("mLock")
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700280 final SparseIntArray mUidState = new SparseIntArray();
281
282 @GuardedBy("mLock")
283 final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray();
284
285 @GuardedBy("mLock")
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700286 private List<Integer> mDirtyUserIds = new ArrayList<>();
287
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700288 /**
Makoto Onukib08790c2016-06-23 14:05:46 -0700289 * A counter that increments every time the system locale changes. We keep track of it to
290 * reset
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700291 * throttling counters on the first call from each package after the last locale change.
292 *
293 * We need this mechanism because we can't do much in the locale change callback, which is
294 * {@link ShortcutServiceInternal#onSystemLocaleChangedNoLock()}.
295 */
296 private final AtomicLong mLocaleChangeSequenceNumber = new AtomicLong();
297
Makoto Onukic51b2872016-05-04 15:24:50 -0700298 private final AtomicBoolean mBootCompleted = new AtomicBoolean();
299
Makoto Onuki905e8852016-03-28 10:40:58 -0700300 private static final int PACKAGE_MATCH_FLAGS =
301 PackageManager.MATCH_DIRECT_BOOT_AWARE
Makoto Onukib08790c2016-06-23 14:05:46 -0700302 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
303 | PackageManager.MATCH_UNINSTALLED_PACKAGES;
Makoto Onuki905e8852016-03-28 10:40:58 -0700304
Makoto Onuki2e210c42016-03-30 08:30:36 -0700305 // Stats
306 @VisibleForTesting
307 interface Stats {
308 int GET_DEFAULT_HOME = 0;
309 int GET_PACKAGE_INFO = 1;
310 int GET_PACKAGE_INFO_WITH_SIG = 2;
311 int GET_APPLICATION_INFO = 3;
312 int LAUNCHER_PERMISSION_CHECK = 4;
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700313 int CLEANUP_DANGLING_BITMAPS = 5;
Makoto Onukib08790c2016-06-23 14:05:46 -0700314 int GET_ACTIVITY_WITH_METADATA = 6;
Makoto Onuki6dd9fb72016-06-01 13:55:54 -0700315 int GET_INSTALLED_PACKAGES = 7;
Makoto Onuki22fcc682016-05-17 14:52:19 -0700316 int CHECK_PACKAGE_CHANGES = 8;
Makoto Onuki157b1622016-06-02 16:13:10 -0700317 int GET_APPLICATION_RESOURCES = 9;
318 int RESOURCE_NAME_LOOKUP = 10;
Makoto Onukib08790c2016-06-23 14:05:46 -0700319 int GET_LAUNCHER_ACTIVITY = 11;
320 int CHECK_LAUNCHER_ACTIVITY = 12;
Makoto Onuki2e210c42016-03-30 08:30:36 -0700321
Makoto Onukib08790c2016-06-23 14:05:46 -0700322 int COUNT = CHECK_LAUNCHER_ACTIVITY + 1;
Makoto Onuki2e210c42016-03-30 08:30:36 -0700323 }
324
325 final Object mStatLock = new Object();
326
327 @GuardedBy("mStatLock")
328 private final int[] mCountStats = new int[Stats.COUNT];
329
330 @GuardedBy("mStatLock")
331 private final long[] mDurationStats = new long[Stats.COUNT];
332
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700333 private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
334 ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
335
Makoto Onuki7001a612016-05-27 13:24:28 -0700336 static final int OPERATION_SET = 0;
337 static final int OPERATION_ADD = 1;
338 static final int OPERATION_UPDATE = 2;
339
340 /** @hide */
341 @IntDef(value = {
342 OPERATION_SET,
343 OPERATION_ADD,
344 OPERATION_UPDATE
Makoto Onukib08790c2016-06-23 14:05:46 -0700345 })
Makoto Onuki7001a612016-05-27 13:24:28 -0700346 @Retention(RetentionPolicy.SOURCE)
Makoto Onukib08790c2016-06-23 14:05:46 -0700347 @interface ShortcutOperation {
348 }
Makoto Onuki7001a612016-05-27 13:24:28 -0700349
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800350 public ShortcutService(Context context) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700351 this(context, BackgroundThread.get().getLooper());
352 }
353
354 @VisibleForTesting
355 ShortcutService(Context context, Looper looper) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800356 mContext = Preconditions.checkNotNull(context);
357 LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700358 mHandler = new Handler(looper);
Makoto Onuki0acbb142016-03-22 17:02:57 -0700359 mIPackageManager = AppGlobals.getPackageManager();
Makoto Onukiac042502016-05-20 16:39:42 -0700360 mPackageManagerInternal = Preconditions.checkNotNull(
361 LocalServices.getService(PackageManagerInternal.class));
362 mUserManager = Preconditions.checkNotNull(context.getSystemService(UserManager.class));
363 mUsageStatsManagerInternal = Preconditions.checkNotNull(
364 LocalServices.getService(UsageStatsManagerInternal.class));
Makoto Onukicdc78f72016-03-21 15:47:52 -0700365
366 mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700367
368 injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
369 | ActivityManager.UID_OBSERVER_GONE);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800370 }
371
Makoto Onuki2e210c42016-03-30 08:30:36 -0700372 void logDurationStat(int statId, long start) {
373 synchronized (mStatLock) {
374 mCountStats[statId]++;
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700375 mDurationStats[statId] += (injectElapsedRealtime() - start);
Makoto Onuki2e210c42016-03-30 08:30:36 -0700376 }
377 }
378
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700379 public long getLocaleChangeSequenceNumber() {
380 return mLocaleChangeSequenceNumber.get();
381 }
382
383 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Makoto Onukib08790c2016-06-23 14:05:46 -0700384 @Override
385 public void onUidStateChanged(int uid, int procState) throws RemoteException {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700386 handleOnUidStateChanged(uid, procState);
387 }
388
Makoto Onukib08790c2016-06-23 14:05:46 -0700389 @Override
390 public void onUidGone(int uid) throws RemoteException {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700391 handleOnUidStateChanged(uid, ActivityManager.MAX_PROCESS_STATE);
392 }
393
Makoto Onukib08790c2016-06-23 14:05:46 -0700394 @Override
395 public void onUidActive(int uid) throws RemoteException {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700396 }
397
Makoto Onukib08790c2016-06-23 14:05:46 -0700398 @Override
399 public void onUidIdle(int uid) throws RemoteException {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700400 }
401 };
402
403 void handleOnUidStateChanged(int uid, int procState) {
404 if (DEBUG_PROCSTATE) {
405 Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
406 }
407 synchronized (mLock) {
408 mUidState.put(uid, procState);
409
410 // We need to keep track of last time an app comes to foreground.
411 // See ShortcutPackage.getApiCallCount() for how it's used.
412 // It doesn't have to be persisted, but it needs to be the elapsed time.
413 if (isProcessStateForeground(procState)) {
414 mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
415 }
416 }
417 }
418
419 private boolean isProcessStateForeground(int processState) {
420 return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
421 }
422
423 boolean isUidForegroundLocked(int uid) {
424 if (uid == Process.SYSTEM_UID) {
425 // IUidObserver doesn't report the state of SYSTEM, but it always has bound services,
426 // so it's foreground anyway.
427 return true;
428 }
429 return isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE));
430 }
431
432 long getUidLastForegroundElapsedTimeLocked(int uid) {
433 return mUidLastForegroundElapsedTime.get(uid);
434 }
435
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800436 /**
437 * System service lifecycle.
438 */
439 public static final class Lifecycle extends SystemService {
440 final ShortcutService mService;
441
442 public Lifecycle(Context context) {
443 super(context);
444 mService = new ShortcutService(context);
445 }
446
447 @Override
448 public void onStart() {
449 publishBinderService(Context.SHORTCUT_SERVICE, mService);
450 }
451
452 @Override
453 public void onBootPhase(int phase) {
454 mService.onBootPhase(phase);
455 }
456
457 @Override
458 public void onCleanupUser(int userHandle) {
Makoto Onukicdc78f72016-03-21 15:47:52 -0700459 mService.handleCleanupUser(userHandle);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800460 }
461
462 @Override
Makoto Onukif3a572b2016-03-10 12:28:38 -0800463 public void onUnlockUser(int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -0700464 mService.handleUnlockUser(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800465 }
466 }
467
468 /** lifecycle event */
469 void onBootPhase(int phase) {
470 if (DEBUG) {
471 Slog.d(TAG, "onBootPhase: " + phase);
472 }
473 switch (phase) {
474 case SystemService.PHASE_LOCK_SETTINGS_READY:
475 initialize();
476 break;
Makoto Onukic51b2872016-05-04 15:24:50 -0700477 case SystemService.PHASE_BOOT_COMPLETED:
478 mBootCompleted.set(true);
479 break;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800480 }
481 }
482
483 /** lifecycle event */
Makoto Onukicdc78f72016-03-21 15:47:52 -0700484 void handleUnlockUser(int userId) {
Makoto Onuki22fcc682016-05-17 14:52:19 -0700485 if (DEBUG) {
486 Slog.d(TAG, "handleUnlockUser: user=" + userId);
487 }
Makoto Onukicdc78f72016-03-21 15:47:52 -0700488 synchronized (mLock) {
489 // Preload
490 getUserShortcutsLocked(userId);
Makoto Onuki0acbb142016-03-22 17:02:57 -0700491
Makoto Onuki39686e82016-04-13 18:03:00 -0700492 checkPackageChanges(userId);
Makoto Onukicdc78f72016-03-21 15:47:52 -0700493 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800494 }
495
496 /** lifecycle event */
Makoto Onukicdc78f72016-03-21 15:47:52 -0700497 void handleCleanupUser(int userId) {
498 synchronized (mLock) {
499 unloadUserLocked(userId);
500 }
501 }
502
503 private void unloadUserLocked(int userId) {
504 if (DEBUG) {
505 Slog.d(TAG, "unloadUserLocked: user=" + userId);
506 }
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700507 // Save all dirty information.
508 saveDirtyInfo();
509
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800510 // Unload
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -0800511 mUsers.delete(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800512 }
513
514 /** Return the base state file name */
515 private AtomicFile getBaseStateFile() {
516 final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
517 path.mkdirs();
518 return new AtomicFile(path);
519 }
520
521 /**
522 * Init the instance. (load the state file, etc)
523 */
524 private void initialize() {
525 synchronized (mLock) {
Makoto Onuki4362a662016-03-08 18:59:09 -0800526 loadConfigurationLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800527 loadBaseStateLocked();
528 }
529 }
530
Makoto Onuki4362a662016-03-08 18:59:09 -0800531 /**
532 * Load the configuration from Settings.
533 */
534 private void loadConfigurationLocked() {
535 updateConfigurationLocked(injectShortcutManagerConstants());
536 }
Makoto Onuki55046222016-03-08 10:49:47 -0800537
Makoto Onuki4362a662016-03-08 18:59:09 -0800538 /**
539 * Load the configuration from Settings.
540 */
541 @VisibleForTesting
542 boolean updateConfigurationLocked(String config) {
543 boolean result = true;
544
545 final KeyValueListParser parser = new KeyValueListParser(',');
546 try {
547 parser.setString(config);
548 } catch (IllegalArgumentException e) {
549 // Failed to parse the settings string, log this and move on
550 // with defaults.
551 Slog.e(TAG, "Bad shortcut manager settings", e);
552 result = false;
553 }
554
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700555 mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
556 DEFAULT_SAVE_DELAY_MS));
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700557
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700558 mResetInterval = Math.max(1, parser.getLong(
Makoto Onuki4362a662016-03-08 18:59:09 -0800559 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700560 * 1000L);
Makoto Onuki4362a662016-03-08 18:59:09 -0800561
Makoto Onukib6d35232016-04-04 15:57:17 -0700562 mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong(
563 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL));
Makoto Onuki4362a662016-03-08 18:59:09 -0800564
Makoto Onukib5a012f2016-06-21 11:13:53 -0700565 mMaxShortcuts = Math.max(0, (int) parser.getLong(
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700566 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP));
Makoto Onuki4362a662016-03-08 18:59:09 -0800567
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700568 final int iconDimensionDp = Math.max(1, injectIsLowRamDevice()
Makoto Onuki4362a662016-03-08 18:59:09 -0800569 ? (int) parser.getLong(
Makoto Onukib08790c2016-06-23 14:05:46 -0700570 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
571 DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
Makoto Onuki4362a662016-03-08 18:59:09 -0800572 : (int) parser.getLong(
Makoto Onukib08790c2016-06-23 14:05:46 -0700573 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
574 DEFAULT_MAX_ICON_DIMENSION_DP));
Makoto Onuki4362a662016-03-08 18:59:09 -0800575
576 mMaxIconDimension = injectDipToPixel(iconDimensionDp);
577
578 mIconPersistFormat = CompressFormat.valueOf(
579 parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
580
581 mIconPersistQuality = (int) parser.getLong(
582 ConfigConstants.KEY_ICON_QUALITY,
583 DEFAULT_ICON_PERSIST_QUALITY);
584
585 return result;
586 }
587
588 @VisibleForTesting
589 String injectShortcutManagerConstants() {
590 return android.provider.Settings.Global.getString(
591 mContext.getContentResolver(),
592 android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
593 }
594
595 @VisibleForTesting
596 int injectDipToPixel(int dip) {
597 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
598 mContext.getResources().getDisplayMetrics());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800599 }
600
Makoto Onuki55046222016-03-08 10:49:47 -0800601 // === Persisting ===
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800602
603 @Nullable
Makoto Onuki41066a62016-03-09 16:18:44 -0800604 static String parseStringAttribute(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800605 return parser.getAttributeValue(null, attribute);
606 }
607
Makoto Onuki0acbb142016-03-22 17:02:57 -0700608 static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) {
609 return parseLongAttribute(parser, attribute) == 1;
610 }
611
Makoto Onuki41066a62016-03-09 16:18:44 -0800612 static int parseIntAttribute(XmlPullParser parser, String attribute) {
613 return (int) parseLongAttribute(parser, attribute);
614 }
615
Makoto Onukid99c6f02016-03-28 11:02:54 -0700616 static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
617 return (int) parseLongAttribute(parser, attribute, def);
618 }
619
Makoto Onuki41066a62016-03-09 16:18:44 -0800620 static long parseLongAttribute(XmlPullParser parser, String attribute) {
Makoto Onukid99c6f02016-03-28 11:02:54 -0700621 return parseLongAttribute(parser, attribute, 0);
622 }
623
624 static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800625 final String value = parseStringAttribute(parser, attribute);
626 if (TextUtils.isEmpty(value)) {
Makoto Onukid99c6f02016-03-28 11:02:54 -0700627 return def;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800628 }
629 try {
630 return Long.parseLong(value);
631 } catch (NumberFormatException e) {
632 Slog.e(TAG, "Error parsing long " + value);
Makoto Onukid99c6f02016-03-28 11:02:54 -0700633 return def;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800634 }
635 }
636
637 @Nullable
Makoto Onuki41066a62016-03-09 16:18:44 -0800638 static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800639 final String value = parseStringAttribute(parser, attribute);
640 if (TextUtils.isEmpty(value)) {
641 return null;
642 }
643 return ComponentName.unflattenFromString(value);
644 }
645
646 @Nullable
Makoto Onuki41066a62016-03-09 16:18:44 -0800647 static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800648 final String value = parseStringAttribute(parser, attribute);
Makoto Onukib5a012f2016-06-21 11:13:53 -0700649 Intent parsed = null;
650 if (!TextUtils.isEmpty(value)) {
651 try {
652 parsed = Intent.parseUri(value, /* flags =*/ 0);
653 } catch (URISyntaxException e) {
654 Slog.e(TAG, "Error parsing intent", e);
655 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800656 }
Makoto Onukib5a012f2016-06-21 11:13:53 -0700657 if (parsed == null) {
658 // Default intent.
659 parsed = new Intent(Intent.ACTION_VIEW);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800660 }
Makoto Onukib5a012f2016-06-21 11:13:53 -0700661 return parsed;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800662 }
663
Makoto Onuki41066a62016-03-09 16:18:44 -0800664 static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800665 if (TextUtils.isEmpty(value)) return;
666
667 out.startTag(null, tag);
668 out.attribute(null, ATTR_VALUE, value);
669 out.endTag(null, tag);
670 }
671
Makoto Onuki41066a62016-03-09 16:18:44 -0800672 static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800673 writeTagValue(out, tag, Long.toString(value));
674 }
675
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800676 static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
677 if (name == null) return;
678 writeTagValue(out, tag, name.flattenToString());
679 }
680
Makoto Onuki41066a62016-03-09 16:18:44 -0800681 static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800682 throws IOException, XmlPullParserException {
683 if (bundle == null) return;
684
685 out.startTag(null, tag);
686 bundle.saveToXml(out);
687 out.endTag(null, tag);
688 }
689
Makoto Onuki22fcc682016-05-17 14:52:19 -0700690 static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800691 if (TextUtils.isEmpty(value)) return;
692
Makoto Onuki22fcc682016-05-17 14:52:19 -0700693 out.attribute(null, name, value.toString());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800694 }
695
Makoto Onuki41066a62016-03-09 16:18:44 -0800696 static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800697 writeAttr(out, name, String.valueOf(value));
698 }
699
Makoto Onuki0acbb142016-03-22 17:02:57 -0700700 static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
701 if (value) {
702 writeAttr(out, name, "1");
703 }
704 }
705
Makoto Onuki41066a62016-03-09 16:18:44 -0800706 static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800707 if (comp == null) return;
708 writeAttr(out, name, comp.flattenToString());
709 }
710
Makoto Onuki41066a62016-03-09 16:18:44 -0800711 static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800712 if (intent == null) return;
713
714 writeAttr(out, name, intent.toUri(/* flags =*/ 0));
715 }
716
717 @VisibleForTesting
718 void saveBaseStateLocked() {
719 final AtomicFile file = getBaseStateFile();
720 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700721 Slog.d(TAG, "Saving to " + file.getBaseFile());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800722 }
723
724 FileOutputStream outs = null;
725 try {
726 outs = file.startWrite();
727
728 // Write to XML
729 XmlSerializer out = new FastXmlSerializer();
730 out.setOutput(outs, StandardCharsets.UTF_8.name());
731 out.startDocument(null, true);
732 out.startTag(null, TAG_ROOT);
733
734 // Body.
735 writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700736 writeTagValue(out, TAG_LOCALE_CHANGE_SEQUENCE_NUMBER,
737 mLocaleChangeSequenceNumber.get());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800738
739 // Epilogue.
740 out.endTag(null, TAG_ROOT);
741 out.endDocument();
742
743 // Close.
744 file.finishWrite(outs);
745 } catch (IOException e) {
746 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
747 file.failWrite(outs);
748 }
749 }
750
751 private void loadBaseStateLocked() {
752 mRawLastResetTime = 0;
753
754 final AtomicFile file = getBaseStateFile();
755 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700756 Slog.d(TAG, "Loading from " + file.getBaseFile());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800757 }
758 try (FileInputStream in = file.openRead()) {
759 XmlPullParser parser = Xml.newPullParser();
760 parser.setInput(in, StandardCharsets.UTF_8.name());
761
762 int type;
763 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
764 if (type != XmlPullParser.START_TAG) {
765 continue;
766 }
767 final int depth = parser.getDepth();
768 // Check the root tag
769 final String tag = parser.getName();
770 if (depth == 1) {
771 if (!TAG_ROOT.equals(tag)) {
772 Slog.e(TAG, "Invalid root tag: " + tag);
773 return;
774 }
775 continue;
776 }
777 // Assume depth == 2
778 switch (tag) {
779 case TAG_LAST_RESET_TIME:
780 mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
781 break;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700782 case TAG_LOCALE_CHANGE_SEQUENCE_NUMBER:
783 mLocaleChangeSequenceNumber.set(parseLongAttribute(parser, ATTR_VALUE));
784 break;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800785 default:
786 Slog.e(TAG, "Invalid tag: " + tag);
787 break;
788 }
789 }
790 } catch (FileNotFoundException e) {
791 // Use the default
Makoto Onukib08790c2016-06-23 14:05:46 -0700792 } catch (IOException | XmlPullParserException e) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800793 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
794
795 mRawLastResetTime = 0;
796 }
797 // Adjust the last reset time.
798 getLastResetTimeLocked();
799 }
800
801 private void saveUserLocked(@UserIdInt int userId) {
802 final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
803 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700804 Slog.d(TAG, "Saving to " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800805 }
806 path.mkdirs();
807 final AtomicFile file = new AtomicFile(path);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700808 FileOutputStream os = null;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800809 try {
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700810 os = file.startWrite();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800811
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700812 saveUserInternalLocked(userId, os, /* forBackup= */ false);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800813
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700814 file.finishWrite(os);
Makoto Onukib08790c2016-06-23 14:05:46 -0700815 } catch (XmlPullParserException | IOException e) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800816 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700817 file.failWrite(os);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800818 }
819 }
820
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700821 private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
822 boolean forBackup) throws IOException, XmlPullParserException {
823
824 final BufferedOutputStream bos = new BufferedOutputStream(os);
825
826 // Write to XML
827 XmlSerializer out = new FastXmlSerializer();
828 out.setOutput(bos, StandardCharsets.UTF_8.name());
829 out.startDocument(null, true);
830
Makoto Onukic51b2872016-05-04 15:24:50 -0700831 getUserShortcutsLocked(userId).saveToXml(out, forBackup);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700832
833 out.endDocument();
834
835 bos.flush();
836 os.flush();
837 }
838
Makoto Onuki41066a62016-03-09 16:18:44 -0800839 static IOException throwForInvalidTag(int depth, String tag) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800840 throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
841 }
842
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700843 static void warnForInvalidTag(int depth, String tag) throws IOException {
844 Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
845 }
846
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800847 @Nullable
Makoto Onuki31459242016-03-22 11:12:18 -0700848 private ShortcutUser loadUserLocked(@UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800849 final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
850 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700851 Slog.d(TAG, "Loading from " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800852 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800853 final AtomicFile file = new AtomicFile(path);
854
855 final FileInputStream in;
856 try {
857 in = file.openRead();
858 } catch (FileNotFoundException e) {
859 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700860 Slog.d(TAG, "Not found " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800861 }
862 return null;
863 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800864 try {
Makoto Onukib08790c2016-06-23 14:05:46 -0700865 final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false);
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700866 cleanupDanglingBitmapDirectoriesLocked(userId, ret);
867 return ret;
Makoto Onukib08790c2016-06-23 14:05:46 -0700868 } catch (IOException | XmlPullParserException e) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800869 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
870 return null;
871 } finally {
872 IoUtils.closeQuietly(in);
873 }
874 }
875
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700876 private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
877 boolean fromBackup) throws XmlPullParserException, IOException {
878
879 final BufferedInputStream bis = new BufferedInputStream(is);
880
881 ShortcutUser ret = null;
882 XmlPullParser parser = Xml.newPullParser();
883 parser.setInput(bis, StandardCharsets.UTF_8.name());
884
885 int type;
886 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
887 if (type != XmlPullParser.START_TAG) {
888 continue;
889 }
890 final int depth = parser.getDepth();
891
892 final String tag = parser.getName();
893 if (DEBUG_LOAD) {
894 Slog.d(TAG, String.format("depth=%d type=%d name=%s",
895 depth, type, tag));
896 }
897 if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
898 ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup);
899 continue;
900 }
901 throwForInvalidTag(depth, tag);
902 }
903 return ret;
904 }
905
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800906 private void scheduleSaveBaseState() {
Makoto Onuki0acbb142016-03-22 17:02:57 -0700907 scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800908 }
909
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800910 void scheduleSaveUser(@UserIdInt int userId) {
Makoto Onuki0acbb142016-03-22 17:02:57 -0700911 scheduleSaveInner(userId);
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700912 }
913
914 // In order to re-schedule, we need to reuse the same instance, so keep it in final.
915 private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo;
916
Makoto Onuki0acbb142016-03-22 17:02:57 -0700917 private void scheduleSaveInner(@UserIdInt int userId) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700918 if (DEBUG) {
919 Slog.d(TAG, "Scheduling to save for " + userId);
920 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800921 synchronized (mLock) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700922 if (!mDirtyUserIds.contains(userId)) {
923 mDirtyUserIds.add(userId);
924 }
925 }
926 // If already scheduled, remove that and re-schedule in N seconds.
927 mHandler.removeCallbacks(mSaveDirtyInfoRunner);
928 mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis);
929 }
930
931 @VisibleForTesting
932 void saveDirtyInfo() {
933 if (DEBUG) {
934 Slog.d(TAG, "saveDirtyInfo");
935 }
936 synchronized (mLock) {
937 for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
938 final int userId = mDirtyUserIds.get(i);
939 if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
940 saveBaseStateLocked();
941 } else {
942 saveUserLocked(userId);
943 }
944 }
945 mDirtyUserIds.clear();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800946 }
947 }
948
949 /** Return the last reset time. */
950 long getLastResetTimeLocked() {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700951 updateTimesLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800952 return mRawLastResetTime;
953 }
954
955 /** Return the next reset time. */
956 long getNextResetTimeLocked() {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700957 updateTimesLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800958 return mRawLastResetTime + mResetInterval;
959 }
960
Makoto Onuki4554d0e2016-03-14 15:51:41 -0700961 static boolean isClockValid(long time) {
962 return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT
963 }
964
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800965 /**
966 * Update the last reset time.
967 */
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700968 private void updateTimesLocked() {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800969
970 final long now = injectCurrentTimeMillis();
971
972 final long prevLastResetTime = mRawLastResetTime;
973
974 if (mRawLastResetTime == 0) { // first launch.
975 // TODO Randomize??
976 mRawLastResetTime = now;
977 } else if (now < mRawLastResetTime) {
978 // Clock rewound.
Makoto Onuki4554d0e2016-03-14 15:51:41 -0700979 if (isClockValid(now)) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700980 Slog.w(TAG, "Clock rewound");
Makoto Onuki4554d0e2016-03-14 15:51:41 -0700981 // TODO Randomize??
982 mRawLastResetTime = now;
983 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800984 } else {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700985 if ((mRawLastResetTime + mResetInterval) <= now) {
986 final long offset = mRawLastResetTime % mResetInterval;
987 mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800988 }
989 }
990 if (prevLastResetTime != mRawLastResetTime) {
991 scheduleSaveBaseState();
992 }
993 }
994
Makoto Onukicdc78f72016-03-21 15:47:52 -0700995 @GuardedBy("mLock")
996 @NonNull
Makoto Onuki2e210c42016-03-30 08:30:36 -0700997 private boolean isUserLoadedLocked(@UserIdInt int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -0700998 return mUsers.get(userId) != null;
999 }
1000
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001001 /** Return the per-user state. */
1002 @GuardedBy("mLock")
1003 @NonNull
Makoto Onuki31459242016-03-22 11:12:18 -07001004 ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
1005 ShortcutUser userPackages = mUsers.get(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001006 if (userPackages == null) {
1007 userPackages = loadUserLocked(userId);
1008 if (userPackages == null) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001009 userPackages = new ShortcutUser(this, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001010 }
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08001011 mUsers.put(userId, userPackages);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001012 }
1013 return userPackages;
1014 }
1015
Makoto Onuki2e210c42016-03-30 08:30:36 -07001016 void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
1017 for (int i = mUsers.size() - 1; i >= 0; i--) {
1018 c.accept(mUsers.valueAt(i));
1019 }
1020 }
1021
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001022 /** Return the per-user per-package state. */
1023 @GuardedBy("mLock")
1024 @NonNull
Makoto Onuki31459242016-03-22 11:12:18 -07001025 ShortcutPackage getPackageShortcutsLocked(
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001026 @NonNull String packageName, @UserIdInt int userId) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001027 return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
Makoto Onukide667372016-03-15 14:29:20 -07001028 }
1029
1030 @GuardedBy("mLock")
1031 @NonNull
Makoto Onuki2e210c42016-03-30 08:30:36 -07001032 ShortcutLauncher getLauncherShortcutsLocked(
1033 @NonNull String packageName, @UserIdInt int ownerUserId,
1034 @UserIdInt int launcherUserId) {
1035 return getUserShortcutsLocked(ownerUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07001036 .getLauncherShortcuts(packageName, launcherUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001037 }
1038
1039 // === Caller validation ===
1040
Makoto Onuki55046222016-03-08 10:49:47 -08001041 void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) {
1042 if (shortcut.getBitmapPath() != null) {
1043 if (DEBUG) {
1044 Slog.d(TAG, "Removing " + shortcut.getBitmapPath());
1045 }
1046 new File(shortcut.getBitmapPath()).delete();
1047
1048 shortcut.setBitmapPath(null);
Makoto Onuki55046222016-03-08 10:49:47 -08001049 }
Makoto Onukidd097812016-06-29 13:10:09 -07001050 shortcut.setIconResourceId(0);
1051 shortcut.setIconResName(null);
1052 shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES);
Makoto Onuki55046222016-03-08 10:49:47 -08001053 }
1054
Makoto Onuki0033b2a2016-04-14 17:19:16 -07001055 public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) {
1056 final File packagePath = new File(getUserBitmapFilePath(userId), packageName);
1057 if (!packagePath.isDirectory()) {
1058 return;
1059 }
1060 if (!(FileUtils.deleteContents(packagePath) && packagePath.delete())) {
1061 Slog.w(TAG, "Unable to remove directory " + packagePath);
1062 }
1063 }
1064
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001065 private void cleanupDanglingBitmapDirectoriesLocked(
1066 @UserIdInt int userId, @NonNull ShortcutUser user) {
1067 if (DEBUG) {
1068 Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
1069 }
1070 final long start = injectElapsedRealtime();
1071
1072 final File bitmapDir = getUserBitmapFilePath(userId);
1073 final File[] children = bitmapDir.listFiles();
1074 if (children == null) {
1075 return;
1076 }
1077 for (File child : children) {
1078 if (!child.isDirectory()) {
1079 continue;
1080 }
1081 final String packageName = child.getName();
1082 if (DEBUG) {
1083 Slog.d(TAG, "cleanupDanglingBitmaps: Found directory=" + packageName);
1084 }
1085 if (!user.hasPackage(packageName)) {
1086 if (DEBUG) {
1087 Slog.d(TAG, "Removing dangling bitmap directory: " + packageName);
1088 }
1089 cleanupBitmapsForPackage(userId, packageName);
1090 } else {
1091 cleanupDanglingBitmapFilesLocked(userId, user, packageName, child);
1092 }
1093 }
1094 logDurationStat(Stats.CLEANUP_DANGLING_BITMAPS, start);
1095 }
1096
1097 private void cleanupDanglingBitmapFilesLocked(@UserIdInt int userId, @NonNull ShortcutUser user,
1098 @NonNull String packageName, @NonNull File path) {
1099 final ArraySet<String> usedFiles =
Makoto Onukic51b2872016-05-04 15:24:50 -07001100 user.getPackageShortcuts(packageName).getUsedBitmapFiles();
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001101
1102 for (File child : path.listFiles()) {
1103 if (!child.isFile()) {
1104 continue;
1105 }
1106 final String name = child.getName();
1107 if (!usedFiles.contains(name)) {
1108 if (DEBUG) {
1109 Slog.d(TAG, "Removing dangling bitmap file: " + child.getAbsolutePath());
1110 }
1111 child.delete();
1112 }
1113 }
1114 }
1115
Makoto Onuki55046222016-03-08 10:49:47 -08001116 @VisibleForTesting
1117 static class FileOutputStreamWithPath extends FileOutputStream {
1118 private final File mFile;
1119
1120 public FileOutputStreamWithPath(File file) throws FileNotFoundException {
1121 super(file);
1122 mFile = file;
1123 }
1124
1125 public File getFile() {
1126 return mFile;
1127 }
1128 }
1129
1130 /**
1131 * Build the cached bitmap filename for a shortcut icon.
1132 *
1133 * The filename will be based on the ID, except certain characters will be escaped.
1134 */
1135 @VisibleForTesting
1136 FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
1137 throws IOException {
1138 final File packagePath = new File(getUserBitmapFilePath(userId),
Makoto Onuki22fcc682016-05-17 14:52:19 -07001139 shortcut.getPackage());
Makoto Onuki55046222016-03-08 10:49:47 -08001140 if (!packagePath.isDirectory()) {
1141 packagePath.mkdirs();
1142 if (!packagePath.isDirectory()) {
1143 throw new IOException("Unable to create directory " + packagePath);
1144 }
1145 SELinux.restorecon(packagePath);
1146 }
1147
1148 final String baseName = String.valueOf(injectCurrentTimeMillis());
Makoto Onukib08790c2016-06-23 14:05:46 -07001149 for (int suffix = 0; ; suffix++) {
Makoto Onuki55046222016-03-08 10:49:47 -08001150 final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
1151 final File file = new File(packagePath, filename);
1152 if (!file.exists()) {
1153 if (DEBUG) {
1154 Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
1155 }
1156 return new FileOutputStreamWithPath(file);
1157 }
1158 }
1159 }
1160
1161 void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) {
1162 if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
1163 return;
1164 }
1165
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001166 final long token = injectClearCallingIdentity();
Makoto Onuki55046222016-03-08 10:49:47 -08001167 try {
1168 // Clear icon info on the shortcut.
Makoto Onukidd097812016-06-29 13:10:09 -07001169 removeIcon(userId, shortcut);
Makoto Onuki55046222016-03-08 10:49:47 -08001170
1171 final Icon icon = shortcut.getIcon();
1172 if (icon == null) {
1173 return; // has no icon
1174 }
1175
Makoto Onukiabe84422016-04-07 09:41:19 -07001176 Bitmap bitmap;
Makoto Onuki55046222016-03-08 10:49:47 -08001177 try {
1178 switch (icon.getType()) {
1179 case Icon.TYPE_RESOURCE: {
1180 injectValidateIconResPackage(shortcut, icon);
1181
1182 shortcut.setIconResourceId(icon.getResId());
1183 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
1184 return;
1185 }
1186 case Icon.TYPE_BITMAP: {
Makoto Onukiabe84422016-04-07 09:41:19 -07001187 bitmap = icon.getBitmap(); // Don't recycle in this case.
Makoto Onuki55046222016-03-08 10:49:47 -08001188 break;
1189 }
Makoto Onuki55046222016-03-08 10:49:47 -08001190 default:
1191 // This shouldn't happen because we've already validated the icon, but
1192 // just in case.
1193 throw ShortcutInfo.getInvalidIconException();
1194 }
1195 if (bitmap == null) {
1196 Slog.e(TAG, "Null bitmap detected");
1197 return;
1198 }
1199 // Shrink and write to the file.
1200 File path = null;
1201 try {
1202 final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut);
1203 try {
1204 path = out.getFile();
1205
Makoto Onukiabe84422016-04-07 09:41:19 -07001206 Bitmap shrunk = shrinkBitmap(bitmap, mMaxIconDimension);
1207 try {
1208 shrunk.compress(mIconPersistFormat, mIconPersistQuality, out);
1209 } finally {
1210 if (bitmap != shrunk) {
1211 shrunk.recycle();
1212 }
1213 }
Makoto Onuki55046222016-03-08 10:49:47 -08001214
1215 shortcut.setBitmapPath(out.getFile().getAbsolutePath());
1216 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
1217 } finally {
1218 IoUtils.closeQuietly(out);
1219 }
Makoto Onukib08790c2016-06-23 14:05:46 -07001220 } catch (IOException | RuntimeException e) {
Makoto Onuki55046222016-03-08 10:49:47 -08001221 // STOPSHIP Change wtf to e
1222 Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
1223 if (path != null && path.exists()) {
1224 path.delete();
1225 }
1226 }
1227 } finally {
Makoto Onuki55046222016-03-08 10:49:47 -08001228 // Once saved, we won't use the original icon information, so null it out.
1229 shortcut.clearIcon();
1230 }
1231 } finally {
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001232 injectRestoreCallingIdentity(token);
Makoto Onuki55046222016-03-08 10:49:47 -08001233 }
1234 }
1235
1236 // Unfortunately we can't do this check in unit tests because we fake creator package names,
1237 // so override in unit tests.
1238 // TODO CTS this case.
1239 void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001240 if (!shortcut.getPackage().equals(icon.getResPackage())) {
Makoto Onuki55046222016-03-08 10:49:47 -08001241 throw new IllegalArgumentException(
1242 "Icon resource must reside in shortcut owner package");
1243 }
1244 }
1245
1246 @VisibleForTesting
1247 static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
1248 // Original width/height.
1249 final int ow = in.getWidth();
1250 final int oh = in.getHeight();
1251 if ((ow <= maxSize) && (oh <= maxSize)) {
1252 if (DEBUG) {
1253 Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
1254 }
1255 return in;
1256 }
1257 final int longerDimension = Math.max(ow, oh);
1258
1259 // New width and height.
1260 final int nw = ow * maxSize / longerDimension;
1261 final int nh = oh * maxSize / longerDimension;
1262 if (DEBUG) {
1263 Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
1264 ow, oh, nw, nh));
1265 }
1266
1267 final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
1268 final Canvas c = new Canvas(scaledBitmap);
1269
1270 final RectF dst = new RectF(0, 0, nw, nh);
1271
1272 c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
1273
Makoto Onuki55046222016-03-08 10:49:47 -08001274 return scaledBitmap;
1275 }
1276
Makoto Onuki157b1622016-06-02 16:13:10 -07001277 /**
1278 * For a shortcut, update all resource names from resource IDs, and also update all
1279 * resource-based strings.
1280 */
1281 void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
1282 final Resources publisherRes = injectGetResourcesForApplicationAsUser(
1283 si.getPackage(), si.getUserId());
1284 if (publisherRes != null) {
1285 final long start = injectElapsedRealtime();
1286 try {
1287 si.lookupAndFillInResourceNames(publisherRes);
1288 } finally {
1289 logDurationStat(Stats.RESOURCE_NAME_LOOKUP, start);
1290 }
1291 si.resolveResourceStrings(publisherRes);
1292 }
1293 }
1294
Makoto Onuki55046222016-03-08 10:49:47 -08001295 // === Caller validation ===
1296
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001297 private boolean isCallerSystem() {
1298 final int callingUid = injectBinderCallingUid();
Makoto Onukib08790c2016-06-23 14:05:46 -07001299 return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001300 }
1301
1302 private boolean isCallerShell() {
1303 final int callingUid = injectBinderCallingUid();
1304 return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
1305 }
1306
1307 private void enforceSystemOrShell() {
1308 Preconditions.checkState(isCallerSystem() || isCallerShell(),
1309 "Caller must be system or shell");
1310 }
1311
1312 private void enforceShell() {
1313 Preconditions.checkState(isCallerShell(), "Caller must be shell");
1314 }
1315
Makoto Onuki9da23fc2016-03-29 11:14:42 -07001316 private void enforceSystem() {
1317 Preconditions.checkState(isCallerSystem(), "Caller must be system");
1318 }
1319
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001320 private void enforceResetThrottlingPermission() {
1321 if (isCallerSystem()) {
1322 return;
1323 }
1324 injectEnforceCallingPermission(
1325 android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null);
1326 }
1327
1328 /**
1329 * Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse
1330 * mockito. So instead we extracted it here and override it in the tests.
1331 */
1332 @VisibleForTesting
1333 void injectEnforceCallingPermission(
1334 @NonNull String permission, @Nullable String message) {
1335 mContext.enforceCallingPermission(permission, message);
1336 }
1337
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001338 private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
1339 Preconditions.checkStringNotEmpty(packageName, "packageName");
1340
1341 if (isCallerSystem()) {
1342 return; // no check
1343 }
1344
1345 final int callingUid = injectBinderCallingUid();
1346
1347 // Otherwise, make sure the arguments are valid.
1348 if (UserHandle.getUserId(callingUid) != userId) {
1349 throw new SecurityException("Invalid user-ID");
1350 }
Makoto Onuki55046222016-03-08 10:49:47 -08001351 if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001352 return; // Caller is valid.
1353 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001354 throw new SecurityException("Calling package name mismatch");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001355 }
1356
Makoto Onuki157b1622016-06-02 16:13:10 -07001357 // Overridden in unit tests to execute r synchronously.
1358 void injectPostToHandler(Runnable r) {
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001359 mHandler.post(r);
1360 }
1361
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001362 /**
Makoto Onuki7001a612016-05-27 13:24:28 -07001363 * @throws IllegalArgumentException if {@code numShortcuts} is bigger than
Makoto Onukib08790c2016-06-23 14:05:46 -07001364 * {@link #getMaxActivityShortcuts()}.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001365 */
Makoto Onuki7001a612016-05-27 13:24:28 -07001366 void enforceMaxActivityShortcuts(int numShortcuts) {
Makoto Onukib5a012f2016-06-21 11:13:53 -07001367 if (numShortcuts > mMaxShortcuts) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001368 throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
1369 }
1370 }
1371
1372 /**
Makoto Onuki7001a612016-05-27 13:24:28 -07001373 * Return the max number of dynamic + manifest shortcuts for each launcher icon.
1374 */
1375 int getMaxActivityShortcuts() {
Makoto Onukib5a012f2016-06-21 11:13:53 -07001376 return mMaxShortcuts;
Makoto Onuki7001a612016-05-27 13:24:28 -07001377 }
1378
1379 /**
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001380 * - Sends a notification to LauncherApps
1381 * - Write to file
1382 */
Makoto Onuki39686e82016-04-13 18:03:00 -07001383 void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) {
1384 if (DEBUG) {
1385 Slog.d(TAG, String.format(
1386 "Shortcut changes: package=%s, user=%d", packageName, userId));
1387 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001388 notifyListeners(packageName, userId);
1389 scheduleSaveUser(userId);
1390 }
1391
1392 private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
Makoto Onuki85694522016-05-04 12:53:37 -07001393 final long token = injectClearCallingIdentity();
1394 try {
1395 if (!mUserManager.isUserRunning(userId)) {
1396 return;
1397 }
1398 } finally {
1399 injectRestoreCallingIdentity(token);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001400 }
Makoto Onuki157b1622016-06-02 16:13:10 -07001401 injectPostToHandler(() -> {
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001402 final ArrayList<ShortcutChangeListener> copy;
1403 synchronized (mLock) {
1404 copy = new ArrayList<>(mListeners);
1405 }
1406 // Note onShortcutChanged() needs to be called with the system service permissions.
1407 for (int i = copy.size() - 1; i >= 0; i--) {
1408 copy.get(i).onShortcutChanged(packageName, userId);
1409 }
1410 });
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001411 }
1412
1413 /**
1414 * Clean up / validate an incoming shortcut.
1415 * - Make sure all mandatory fields are set.
1416 * - Make sure the intent's extras are persistable, and them to set
Makoto Onukib08790c2016-06-23 14:05:46 -07001417 * {@link ShortcutInfo#mIntentPersistableExtras}. Also clear its extras.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001418 * - Clear flags.
Makoto Onuki55046222016-03-08 10:49:47 -08001419 *
1420 * TODO Detailed unit tests
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001421 */
Makoto Onuki55046222016-03-08 10:49:47 -08001422 private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001423 Preconditions.checkNotNull(shortcut, "Null shortcut detected");
Makoto Onuki22fcc682016-05-17 14:52:19 -07001424 if (shortcut.getActivity() != null) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001425 Preconditions.checkState(
Makoto Onuki22fcc682016-05-17 14:52:19 -07001426 shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
Makoto Onukib08790c2016-06-23 14:05:46 -07001427 "Cannot publish shortcut: activity " + shortcut.getActivity() + " does not"
1428 + " belong to package " + shortcut.getPackage());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001429 }
1430
Makoto Onuki55046222016-03-08 10:49:47 -08001431 if (!forUpdate) {
1432 shortcut.enforceMandatoryFields();
Makoto Onukib08790c2016-06-23 14:05:46 -07001433 Preconditions.checkArgument(
1434 injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
1435 "Cannot publish shortcut: " + shortcut.getActivity() + " is not main activity");
Makoto Onuki55046222016-03-08 10:49:47 -08001436 }
1437 if (shortcut.getIcon() != null) {
1438 ShortcutInfo.validateIcon(shortcut.getIcon());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001439 }
1440
Makoto Onukide667372016-03-15 14:29:20 -07001441 shortcut.replaceFlags(0);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001442 }
1443
Makoto Onukib08790c2016-06-23 14:05:46 -07001444 /**
1445 * When a shortcut has no target activity, set the default one from the package.
1446 */
1447 private void fillInDefaultActivity(List<ShortcutInfo> shortcuts) {
1448
1449 ComponentName defaultActivity = null;
1450 for (int i = shortcuts.size() - 1; i >= 0; i--) {
1451 final ShortcutInfo si = shortcuts.get(i);
1452 if (si.getActivity() == null) {
1453 if (defaultActivity == null) {
1454 defaultActivity = injectGetDefaultMainActivity(
1455 si.getPackage(), si.getUserId());
1456 Preconditions.checkState(defaultActivity != null,
1457 "Launcher activity not found for package " + si.getPackage());
1458 }
1459 si.setActivity(defaultActivity);
1460 }
1461 }
1462 }
1463
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001464 private void assignImplicitRanks(List<ShortcutInfo> shortcuts) {
1465 for (int i = shortcuts.size() - 1; i >= 0; i--) {
1466 shortcuts.get(i).setImplicitRank(i);
1467 }
1468 }
1469
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001470 // === APIs ===
1471
1472 @Override
1473 public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
1474 @UserIdInt int userId) {
1475 verifyCaller(packageName, userId);
1476
1477 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
1478 final int size = newShortcuts.size();
1479
1480 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07001481 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001482
Makoto Onuki22fcc682016-05-17 14:52:19 -07001483 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1484
Makoto Onukib08790c2016-06-23 14:05:46 -07001485 fillInDefaultActivity(newShortcuts);
1486
Makoto Onuki7001a612016-05-27 13:24:28 -07001487 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
1488
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001489 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001490 if (!ps.tryApiCall()) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001491 return false;
1492 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001493
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001494 // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
1495 ps.clearAllImplicitRanks();
1496 assignImplicitRanks(newShortcuts);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001497
Makoto Onukidf6da042016-06-16 09:51:40 -07001498 for (int i = 0; i < size; i++) {
1499 fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
1500 }
1501
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001502 // First, remove all un-pinned; dynamic shortcuts
Makoto Onukic51b2872016-05-04 15:24:50 -07001503 ps.deleteAllDynamicShortcuts();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001504
1505 // Then, add/update all. We need to make sure to take over "pinned" flag.
1506 for (int i = 0; i < size; i++) {
1507 final ShortcutInfo newShortcut = newShortcuts.get(i);
Makoto Onuki22fcc682016-05-17 14:52:19 -07001508 ps.addOrUpdateDynamicShortcut(newShortcut);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001509 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001510
1511 // Lastly, adjust the ranks.
1512 ps.adjustRanks();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001513 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001514 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001515
1516 verifyStates();
1517
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001518 return true;
1519 }
1520
1521 @Override
1522 public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
1523 @UserIdInt int userId) {
1524 verifyCaller(packageName, userId);
1525
1526 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
Makoto Onuki55046222016-03-08 10:49:47 -08001527 final int size = newShortcuts.size();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001528
1529 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07001530 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001531
Makoto Onuki22fcc682016-05-17 14:52:19 -07001532 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1533
Makoto Onukib08790c2016-06-23 14:05:46 -07001534 // For update, don't fill in the default activity. Having null activity means
1535 // "don't update the activity" here.
1536
Makoto Onuki7001a612016-05-27 13:24:28 -07001537 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
1538
Makoto Onuki55046222016-03-08 10:49:47 -08001539 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001540 if (!ps.tryApiCall()) {
Makoto Onuki55046222016-03-08 10:49:47 -08001541 return false;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001542 }
1543
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001544 // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
1545 ps.clearAllImplicitRanks();
1546 assignImplicitRanks(newShortcuts);
1547
Makoto Onuki55046222016-03-08 10:49:47 -08001548 for (int i = 0; i < size; i++) {
1549 final ShortcutInfo source = newShortcuts.get(i);
1550 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
1551
1552 final ShortcutInfo target = ps.findShortcutById(source.getId());
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001553 if (target == null) {
1554 continue;
1555 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07001556
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001557 if (target.isEnabled() != source.isEnabled()) {
1558 Slog.w(TAG,
1559 "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
1560 }
Makoto Onuki55046222016-03-08 10:49:47 -08001561
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001562 // When updating the rank, we need to insert between existing ranks, so set
1563 // this setRankChanged, and also copy the implicit rank fo adjustRanks().
1564 if (source.hasRank()) {
1565 target.setRankChanged();
1566 target.setImplicitRank(source.getImplicitRank());
1567 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07001568
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001569 final boolean replacingIcon = (source.getIcon() != null);
1570 if (replacingIcon) {
1571 removeIcon(userId, target);
1572 }
Makoto Onuki55046222016-03-08 10:49:47 -08001573
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001574 if (source.getActivity() != null &&
1575 !source.getActivity().equals(target.getActivity())) {
1576 // TODO When activity is changing, check the dynamic count.
1577 }
Makoto Onuki157b1622016-06-02 16:13:10 -07001578
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001579 // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
1580 target.copyNonNullFieldsFrom(source);
1581 target.setTimestamp(injectCurrentTimeMillis());
1582
1583 if (replacingIcon) {
1584 saveIconAndFixUpShortcut(userId, target);
1585 }
1586
1587 // When we're updating any resource related fields, re-extract the res names and
1588 // the values.
1589 if (replacingIcon || source.hasStringResources()) {
1590 fixUpShortcutResourceNamesAndValues(target);
Makoto Onuki55046222016-03-08 10:49:47 -08001591 }
1592 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001593
1594 // Lastly, adjust the ranks.
1595 ps.adjustRanks();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001596 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001597 packageShortcutsChanged(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001598
Makoto Onuki7001a612016-05-27 13:24:28 -07001599 verifyStates();
1600
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001601 return true;
1602 }
1603
1604 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001605 public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001606 @UserIdInt int userId) {
1607 verifyCaller(packageName, userId);
1608
Makoto Onukib6d35232016-04-04 15:57:17 -07001609 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
1610 final int size = newShortcuts.size();
1611
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001612 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07001613 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001614
Makoto Onuki22fcc682016-05-17 14:52:19 -07001615 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1616
Makoto Onukib08790c2016-06-23 14:05:46 -07001617 fillInDefaultActivity(newShortcuts);
1618
Makoto Onuki7001a612016-05-27 13:24:28 -07001619 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
1620
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001621 // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
1622 ps.clearAllImplicitRanks();
1623 assignImplicitRanks(newShortcuts);
1624
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001625 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001626 if (!ps.tryApiCall()) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001627 return false;
1628 }
Makoto Onukib6d35232016-04-04 15:57:17 -07001629 for (int i = 0; i < size; i++) {
1630 final ShortcutInfo newShortcut = newShortcuts.get(i);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001631
Makoto Onukib6d35232016-04-04 15:57:17 -07001632 // Validate the shortcut.
1633 fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001634
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001635 // When ranks are changing, we need to insert between ranks, so set the
1636 // "rank changed" flag.
1637 newShortcut.setRankChanged();
1638
Makoto Onukib6d35232016-04-04 15:57:17 -07001639 // Add it.
Makoto Onuki22fcc682016-05-17 14:52:19 -07001640 ps.addOrUpdateDynamicShortcut(newShortcut);
Makoto Onukib6d35232016-04-04 15:57:17 -07001641 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001642
1643 // Lastly, adjust the ranks.
1644 ps.adjustRanks();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001645 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001646 packageShortcutsChanged(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001647
Makoto Onuki7001a612016-05-27 13:24:28 -07001648 verifyStates();
1649
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001650 return true;
1651 }
1652
1653 @Override
Makoto Onuki20c95f82016-05-11 16:51:01 -07001654 public void disableShortcuts(String packageName, List shortcutIds,
1655 String disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
1656 verifyCaller(packageName, userId);
1657 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
1658
1659 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001660 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
1661
1662 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1663
1664 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
1665 ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)),
1666 disabledMessage, disabledMessageResId,
1667 /* overrideImmutable=*/ false);
1668 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001669
1670 // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
1671 ps.adjustRanks();
Makoto Onuki22fcc682016-05-17 14:52:19 -07001672 }
1673 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001674
1675 verifyStates();
Makoto Onuki22fcc682016-05-17 14:52:19 -07001676 }
1677
1678 @Override
1679 public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
1680 verifyCaller(packageName, userId);
1681 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
1682
1683 synchronized (mLock) {
1684 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
1685
1686 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1687
1688 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
1689 ps.enableWithId((String) shortcutIds.get(i));
1690 }
Makoto Onuki20c95f82016-05-11 16:51:01 -07001691 }
1692 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001693
1694 verifyStates();
Makoto Onuki20c95f82016-05-11 16:51:01 -07001695 }
1696
1697 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001698 public void removeDynamicShortcuts(String packageName, List shortcutIds,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001699 @UserIdInt int userId) {
1700 verifyCaller(packageName, userId);
Makoto Onukib6d35232016-04-04 15:57:17 -07001701 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001702
1703 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001704 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
1705
1706 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1707
Makoto Onukib6d35232016-04-04 15:57:17 -07001708 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001709 ps.deleteDynamicWithId(
Makoto Onukib6d35232016-04-04 15:57:17 -07001710 Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
1711 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001712
1713 // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
1714 ps.adjustRanks();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001715 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001716 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001717
1718 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001719 }
1720
1721 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001722 public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001723 verifyCaller(packageName, userId);
1724
1725 synchronized (mLock) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001726 getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001727 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001728 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001729
1730 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001731 }
1732
1733 @Override
1734 public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
1735 @UserIdInt int userId) {
1736 verifyCaller(packageName, userId);
1737 synchronized (mLock) {
1738 return getShortcutsWithQueryLocked(
1739 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
1740 ShortcutInfo::isDynamic);
1741 }
1742 }
1743
1744 @Override
Makoto Onuki22fcc682016-05-17 14:52:19 -07001745 public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
1746 @UserIdInt int userId) {
1747 verifyCaller(packageName, userId);
1748 synchronized (mLock) {
1749 return getShortcutsWithQueryLocked(
1750 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
1751 ShortcutInfo::isManifestShortcut);
1752 }
1753 }
1754
1755 @Override
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001756 public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
1757 @UserIdInt int userId) {
1758 verifyCaller(packageName, userId);
1759 synchronized (mLock) {
1760 return getShortcutsWithQueryLocked(
1761 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
1762 ShortcutInfo::isPinned);
1763 }
1764 }
1765
1766 private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
1767 @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
1768
1769 final ArrayList<ShortcutInfo> ret = new ArrayList<>();
1770
Makoto Onukic51b2872016-05-04 15:24:50 -07001771 getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001772
1773 return new ParceledListSlice<>(ret);
1774 }
1775
1776 @Override
Makoto Onukib5a012f2016-06-21 11:13:53 -07001777 public int getMaxShortcutCountForActivity(String packageName, @UserIdInt int userId)
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001778 throws RemoteException {
1779 verifyCaller(packageName, userId);
1780
Makoto Onukib5a012f2016-06-21 11:13:53 -07001781 return mMaxShortcuts;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001782 }
1783
1784 @Override
1785 public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
1786 verifyCaller(packageName, userId);
1787
1788 synchronized (mLock) {
Makoto Onukib6d35232016-04-04 15:57:17 -07001789 return mMaxUpdatesPerInterval
Makoto Onukic51b2872016-05-04 15:24:50 -07001790 - getPackageShortcutsLocked(packageName, userId).getApiCallCount();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001791 }
1792 }
1793
1794 @Override
1795 public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
1796 verifyCaller(packageName, userId);
1797
1798 synchronized (mLock) {
1799 return getNextResetTimeLocked();
1800 }
1801 }
1802
Makoto Onuki55046222016-03-08 10:49:47 -08001803 @Override
Makoto Onuki20c95f82016-05-11 16:51:01 -07001804 public int getIconMaxDimensions(String packageName, int userId) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001805 verifyCaller(packageName, userId);
1806
Makoto Onuki55046222016-03-08 10:49:47 -08001807 synchronized (mLock) {
1808 return mMaxIconDimension;
1809 }
1810 }
1811
Makoto Onuki20c95f82016-05-11 16:51:01 -07001812 @Override
1813 public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
1814 verifyCaller(packageName, userId);
1815
Makoto Onukiac042502016-05-20 16:39:42 -07001816 Preconditions.checkNotNull(shortcutId);
1817
1818 if (DEBUG) {
1819 Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
1820 shortcutId, packageName, userId));
1821 }
1822
1823 synchronized (mLock) {
1824 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
1825 if (ps.findShortcutById(shortcutId) == null) {
1826 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s",
1827 packageName, shortcutId));
1828 return;
1829 }
1830 }
1831
1832 final long token = injectClearCallingIdentity();
1833 try {
1834 mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
1835 } finally {
1836 injectRestoreCallingIdentity(token);
1837 }
Makoto Onuki20c95f82016-05-11 16:51:01 -07001838 }
1839
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001840 /**
Makoto Onukib08790c2016-06-23 14:05:46 -07001841 * Reset all throttling, for developer options and command line. Only system/shell can call
1842 * it.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001843 */
1844 @Override
1845 public void resetThrottling() {
1846 enforceSystemOrShell();
1847
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001848 resetThrottlingInner(getCallingUserId());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001849 }
1850
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001851 void resetThrottlingInner(@UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001852 synchronized (mLock) {
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001853 getUserShortcutsLocked(userId).resetThrottling();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001854 }
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001855 scheduleSaveUser(userId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001856 Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
1857 }
1858
1859 void resetAllThrottlingInner() {
1860 synchronized (mLock) {
1861 mRawLastResetTime = injectCurrentTimeMillis();
1862 }
1863 scheduleSaveBaseState();
1864 Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001865 }
1866
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001867 void resetPackageThrottling(String packageName, int userId) {
1868 synchronized (mLock) {
1869 getPackageShortcutsLocked(packageName, userId)
1870 .resetRateLimitingForCommandLineNoSaving();
1871 saveUserLocked(userId);
1872 }
1873 }
1874
1875 @Override
1876 public void onApplicationActive(String packageName, int userId) {
1877 if (DEBUG) {
1878 Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
1879 }
1880 enforceResetThrottlingPermission();
1881 resetPackageThrottling(packageName, userId);
1882 }
1883
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001884 // We override this method in unit tests to do a simpler check.
1885 boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
1886 return hasShortcutHostPermissionInner(callingPackage, userId);
1887 }
1888
1889 // This method is extracted so we can directly call this method from unit tests,
1890 // even when hasShortcutPermission() is overridden.
1891 @VisibleForTesting
1892 boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
1893 synchronized (mLock) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001894 final long start = injectElapsedRealtime();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001895
Makoto Onuki31459242016-03-22 11:12:18 -07001896 final ShortcutUser user = getUserShortcutsLocked(userId);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001897
1898 final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
1899
1900 // Default launcher from package manager.
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001901 final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001902 final ComponentName defaultLauncher = injectPackageManagerInternal()
1903 .getHomeActivitiesAsUser(allHomeCandidates, userId);
Makoto Onuki2e210c42016-03-30 08:30:36 -07001904 logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001905
1906 ComponentName detected;
1907 if (defaultLauncher != null) {
1908 detected = defaultLauncher;
1909 if (DEBUG) {
1910 Slog.v(TAG, "Default launcher from PM: " + detected);
1911 }
1912 } else {
Makoto Onukic51b2872016-05-04 15:24:50 -07001913 detected = user.getDefaultLauncherComponent();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001914
1915 // TODO: Make sure it's still enabled.
1916 if (DEBUG) {
1917 Slog.v(TAG, "Cached launcher: " + detected);
1918 }
1919 }
1920
1921 if (detected == null) {
1922 // If we reach here, that means it's the first check since the user was created,
1923 // and there's already multiple launchers and there's no default set.
1924 // Find the system one with the highest priority.
1925 // (We need to check the priority too because of FallbackHome in Settings.)
1926 // If there's no system launcher yet, then no one can access shortcuts, until
1927 // the user explicitly
1928 final int size = allHomeCandidates.size();
1929
1930 int lastPriority = Integer.MIN_VALUE;
1931 for (int i = 0; i < size; i++) {
1932 final ResolveInfo ri = allHomeCandidates.get(i);
1933 if (!ri.activityInfo.applicationInfo.isSystemApp()) {
1934 continue;
1935 }
1936 if (DEBUG) {
1937 Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
1938 ri.activityInfo.getComponentName(), ri.priority));
1939 }
1940 if (ri.priority < lastPriority) {
1941 continue;
1942 }
1943 detected = ri.activityInfo.getComponentName();
1944 lastPriority = ri.priority;
1945 }
1946 }
Makoto Onuki2e210c42016-03-30 08:30:36 -07001947 logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
1948
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001949 if (detected != null) {
1950 if (DEBUG) {
1951 Slog.v(TAG, "Detected launcher: " + detected);
1952 }
Makoto Onukic51b2872016-05-04 15:24:50 -07001953 user.setDefaultLauncherComponent(detected);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001954 return detected.getPackageName().equals(callingPackage);
1955 } else {
1956 // Default launcher not found.
1957 return false;
1958 }
1959 }
1960 }
1961
Makoto Onukicdc78f72016-03-21 15:47:52 -07001962 // === House keeping ===
1963
Makoto Onukib08790c2016-06-23 14:05:46 -07001964 private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId,
1965 boolean appStillExists) {
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001966 synchronized (mLock) {
1967 forEachLoadedUserLocked(user ->
Makoto Onukib08790c2016-06-23 14:05:46 -07001968 cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
1969 appStillExists));
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001970 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001971 }
1972
Makoto Onuki2e210c42016-03-30 08:30:36 -07001973 /**
1974 * Remove all the information associated with a package. This will really remove all the
1975 * information, including the restore information (i.e. it'll remove packages even if they're
1976 * shadow).
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001977 *
1978 * This is called when an app is uninstalled, or an app gets "clear data"ed.
Makoto Onuki2e210c42016-03-30 08:30:36 -07001979 */
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001980 @VisibleForTesting
Makoto Onukib08790c2016-06-23 14:05:46 -07001981 void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
1982 boolean appStillExists) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07001983 final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001984
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001985 final ShortcutUser user = getUserShortcutsLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001986 boolean doNotify = false;
1987
1988 // First, remove the package from the package list (if the package is a publisher).
Makoto Onukid99c6f02016-03-28 11:02:54 -07001989 if (packageUserId == owningUserId) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001990 if (user.removePackage(packageName) != null) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07001991 doNotify = true;
1992 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07001993 }
Makoto Onukid99c6f02016-03-28 11:02:54 -07001994
Makoto Onukicdc78f72016-03-21 15:47:52 -07001995 // Also remove from the launcher list (if the package is a launcher).
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001996 user.removeLauncher(packageUserId, packageName);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001997
1998 // Then remove pinned shortcuts from all launchers.
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001999 user.forAllLaunchers(l -> l.cleanUpPackage(packageName, packageUserId));
2000
2001 // Now there may be orphan shortcuts because we removed pinned shortcuts at the previous
Makoto Onukicdc78f72016-03-21 15:47:52 -07002002 // step. Remove them too.
Makoto Onukic51b2872016-05-04 15:24:50 -07002003 user.forAllPackages(p -> p.refreshPinnedFlags());
Makoto Onukicdc78f72016-03-21 15:47:52 -07002004
Makoto Onukid99c6f02016-03-28 11:02:54 -07002005 scheduleSaveUser(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002006
2007 if (doNotify) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002008 notifyListeners(packageName, owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002009 }
2010
Makoto Onukib08790c2016-06-23 14:05:46 -07002011 // If the app still exists (i.e. data cleared), we need to re-publish manifest shortcuts.
2012 if (appStillExists && (packageUserId == owningUserId)) {
2013 // This will do the notification and save when needed, so do it after the above
2014 // notifyListeners.
2015 user.handlePackageAddedOrUpdated(packageName, /* forceRescan=*/ true);
2016 }
2017
Makoto Onukicdc78f72016-03-21 15:47:52 -07002018 if (!wasUserLoaded) {
2019 // Note this will execute the scheduled save.
Makoto Onukid99c6f02016-03-28 11:02:54 -07002020 unloadUserLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002021 }
2022 }
2023
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002024 /**
2025 * Entry point from {@link LauncherApps}.
2026 */
2027 private class LocalService extends ShortcutServiceInternal {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002028
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002029 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002030 public List<ShortcutInfo> getShortcuts(int launcherUserId,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002031 @NonNull String callingPackage, long changedSince,
Makoto Onukiabe84422016-04-07 09:41:19 -07002032 @Nullable String packageName, @Nullable List<String> shortcutIds,
2033 @Nullable ComponentName componentName,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002034 int queryFlags, int userId) {
2035 final ArrayList<ShortcutInfo> ret = new ArrayList<>();
Makoto Onuki20c95f82016-05-11 16:51:01 -07002036 final boolean cloneKeyFieldOnly =
2037 ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
2038 final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO
2039 : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
Makoto Onukiabe84422016-04-07 09:41:19 -07002040 if (packageName == null) {
2041 shortcutIds = null; // LauncherAppsService already threw for it though.
2042 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002043
2044 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002045 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002046 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002047
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002048 if (packageName != null) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002049 getShortcutsInnerLocked(launcherUserId,
Makoto Onukiabe84422016-04-07 09:41:19 -07002050 callingPackage, packageName, shortcutIds, changedSince,
Makoto Onukide667372016-03-15 14:29:20 -07002051 componentName, queryFlags, userId, ret, cloneFlag);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002052 } else {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002053 final List<String> shortcutIdsF = shortcutIds;
2054 getUserShortcutsLocked(userId).forAllPackages(p -> {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002055 getShortcutsInnerLocked(launcherUserId,
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002056 callingPackage, p.getPackageName(), shortcutIdsF, changedSince,
Makoto Onukide667372016-03-15 14:29:20 -07002057 componentName, queryFlags, userId, ret, cloneFlag);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002058 });
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002059 }
2060 }
2061 return ret;
2062 }
2063
Makoto Onukid99c6f02016-03-28 11:02:54 -07002064 private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
Makoto Onukiabe84422016-04-07 09:41:19 -07002065 @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002066 @Nullable ComponentName componentName, int queryFlags,
2067 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
Makoto Onukiabe84422016-04-07 09:41:19 -07002068 final ArraySet<String> ids = shortcutIds == null ? null
2069 : new ArraySet<>(shortcutIds);
2070
Makoto Onukic51b2872016-05-04 15:24:50 -07002071 final ShortcutPackage p = getUserShortcutsLocked(userId)
2072 .getPackageShortcutsIfExists(packageName);
2073 if (p == null) {
2074 return; // No need to instantiate ShortcutPackage.
2075 }
2076
2077 p.findAll(ret,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002078 (ShortcutInfo si) -> {
2079 if (si.getLastChangedTimestamp() < changedSince) {
2080 return false;
2081 }
Makoto Onukiabe84422016-04-07 09:41:19 -07002082 if (ids != null && !ids.contains(si.getId())) {
2083 return false;
2084 }
Makoto Onuki85694522016-05-04 12:53:37 -07002085 if (componentName != null) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002086 if (si.getActivity() != null
2087 && !si.getActivity().equals(componentName)) {
Makoto Onuki85694522016-05-04 12:53:37 -07002088 return false;
2089 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002090 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002091 if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
2092 && si.isDynamic()) {
2093 return true;
2094 }
2095 if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
2096 && si.isPinned()) {
2097 return true;
2098 }
2099 if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0)
2100 && si.isManifestShortcut()) {
2101 return true;
2102 }
2103 return false;
Makoto Onukid99c6f02016-03-28 11:02:54 -07002104 }, cloneFlag, callingPackage, launcherUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002105 }
2106
2107 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002108 public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
2109 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2110 Preconditions.checkStringNotEmpty(packageName, "packageName");
2111 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
2112
2113 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002114 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002115 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002116
Makoto Onukid99c6f02016-03-28 11:02:54 -07002117 final ShortcutInfo si = getShortcutInfoLocked(
2118 launcherUserId, callingPackage, packageName, shortcutId, userId);
2119 return si != null && si.isPinned();
2120 }
2121 }
2122
Makoto Onuki2e210c42016-03-30 08:30:36 -07002123 private ShortcutInfo getShortcutInfoLocked(
Makoto Onukid99c6f02016-03-28 11:02:54 -07002124 int launcherUserId, @NonNull String callingPackage,
2125 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2126 Preconditions.checkStringNotEmpty(packageName, "packageName");
2127 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
2128
Makoto Onukic51b2872016-05-04 15:24:50 -07002129 final ShortcutPackage p = getUserShortcutsLocked(userId)
2130 .getPackageShortcutsIfExists(packageName);
2131 if (p == null) {
2132 return null;
2133 }
2134
Makoto Onukid99c6f02016-03-28 11:02:54 -07002135 final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
Makoto Onukic51b2872016-05-04 15:24:50 -07002136 p.findAll(list,
Makoto Onukid99c6f02016-03-28 11:02:54 -07002137 (ShortcutInfo si) -> shortcutId.equals(si.getId()),
2138 /* clone flags=*/ 0, callingPackage, launcherUserId);
2139 return list.size() == 0 ? null : list.get(0);
2140 }
2141
2142 @Override
2143 public void pinShortcuts(int launcherUserId,
2144 @NonNull String callingPackage, @NonNull String packageName,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002145 @NonNull List<String> shortcutIds, int userId) {
2146 // Calling permission must be checked by LauncherAppsImpl.
2147 Preconditions.checkStringNotEmpty(packageName, "packageName");
2148 Preconditions.checkNotNull(shortcutIds, "shortcutIds");
2149
2150 synchronized (mLock) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002151 final ShortcutLauncher launcher =
Makoto Onuki2e210c42016-03-30 08:30:36 -07002152 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
Makoto Onukic51b2872016-05-04 15:24:50 -07002153 launcher.attemptToRestoreIfNeededAndSave();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002154
Makoto Onukic51b2872016-05-04 15:24:50 -07002155 launcher.pinShortcuts(userId, packageName, shortcutIds);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002156 }
Makoto Onuki39686e82016-04-13 18:03:00 -07002157 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07002158
2159 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002160 }
2161
2162 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002163 public Intent createShortcutIntent(int launcherUserId,
2164 @NonNull String callingPackage,
Makoto Onuki43204b82016-03-08 16:16:44 -08002165 @NonNull String packageName, @NonNull String shortcutId, int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002166 // Calling permission must be checked by LauncherAppsImpl.
Makoto Onuki43204b82016-03-08 16:16:44 -08002167 Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
2168 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002169
2170 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002171 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002172 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002173
Makoto Onukid99c6f02016-03-28 11:02:54 -07002174 // Make sure the shortcut is actually visible to the launcher.
2175 final ShortcutInfo si = getShortcutInfoLocked(
2176 launcherUserId, callingPackage, packageName, shortcutId, userId);
2177 // "si == null" should suffice here, but check the flags too just to make sure.
Makoto Onuki22fcc682016-05-17 14:52:19 -07002178 if (si == null || !si.isEnabled() || !si.isAlive()) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002179 return null;
2180 }
2181 return si.getIntent();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002182 }
2183 }
2184
2185 @Override
2186 public void addListener(@NonNull ShortcutChangeListener listener) {
2187 synchronized (mLock) {
2188 mListeners.add(Preconditions.checkNotNull(listener));
2189 }
2190 }
Makoto Onuki55046222016-03-08 10:49:47 -08002191
2192 @Override
Makoto Onukiabe84422016-04-07 09:41:19 -07002193 public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
2194 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2195 Preconditions.checkNotNull(callingPackage, "callingPackage");
2196 Preconditions.checkNotNull(packageName, "packageName");
2197 Preconditions.checkNotNull(shortcutId, "shortcutId");
Makoto Onuki55046222016-03-08 10:49:47 -08002198
2199 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002200 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002201 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002202
Makoto Onukic51b2872016-05-04 15:24:50 -07002203 final ShortcutPackage p = getUserShortcutsLocked(userId)
2204 .getPackageShortcutsIfExists(packageName);
2205 if (p == null) {
2206 return 0;
2207 }
2208
2209 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
Makoto Onuki55046222016-03-08 10:49:47 -08002210 return (shortcutInfo != null && shortcutInfo.hasIconResource())
2211 ? shortcutInfo.getIconResourceId() : 0;
2212 }
2213 }
2214
2215 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002216 public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
Makoto Onukiabe84422016-04-07 09:41:19 -07002217 @NonNull String callingPackage, @NonNull String packageName,
2218 @NonNull String shortcutId, int userId) {
2219 Preconditions.checkNotNull(callingPackage, "callingPackage");
2220 Preconditions.checkNotNull(packageName, "packageName");
2221 Preconditions.checkNotNull(shortcutId, "shortcutId");
Makoto Onuki55046222016-03-08 10:49:47 -08002222
2223 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002224 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002225 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002226
Makoto Onukic51b2872016-05-04 15:24:50 -07002227 final ShortcutPackage p = getUserShortcutsLocked(userId)
2228 .getPackageShortcutsIfExists(packageName);
2229 if (p == null) {
2230 return null;
2231 }
2232
2233 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
Makoto Onuki55046222016-03-08 10:49:47 -08002234 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
2235 return null;
2236 }
2237 try {
Makoto Onuki34d1c912016-03-10 14:24:58 -08002238 if (shortcutInfo.getBitmapPath() == null) {
2239 Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
2240 return null;
2241 }
Makoto Onuki55046222016-03-08 10:49:47 -08002242 return ParcelFileDescriptor.open(
2243 new File(shortcutInfo.getBitmapPath()),
2244 ParcelFileDescriptor.MODE_READ_ONLY);
2245 } catch (FileNotFoundException e) {
2246 Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath());
2247 return null;
2248 }
2249 }
2250 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002251
2252 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002253 public boolean hasShortcutHostPermission(int launcherUserId,
2254 @NonNull String callingPackage) {
2255 return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002256 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002257
2258 /**
2259 * Called by AM when the system locale changes *within the AM lock. ABSOLUTELY do not take
2260 * any locks in this method.
2261 */
2262 @Override
2263 public void onSystemLocaleChangedNoLock() {
2264 // DO NOT HOLD ANY LOCKS HERE.
2265
2266 // We want to reset throttling for all packages for all users. But we can't just do so
2267 // here because:
2268 // - We can't load/save users that are locked.
2269 // - Even for loaded users, resetting the counters would require us to hold mLock.
2270 //
2271 // So we use a "pull" model instead. In here, we just increment the "locale change
2272 // sequence number". Each ShortcutUser has the "last known locale change sequence".
2273 //
2274 // This allows ShortcutUser's to detect the system locale change, so they can reset
2275 // counters.
2276
Makoto Onukic51b2872016-05-04 15:24:50 -07002277 // Ignore all callback during system boot.
2278 if (mBootCompleted.get()) {
2279 mLocaleChangeSequenceNumber.incrementAndGet();
2280 if (DEBUG) {
2281 Slog.d(TAG, "onSystemLocaleChangedNoLock: " + mLocaleChangeSequenceNumber.get());
2282 }
Makoto Onuki157b1622016-06-02 16:13:10 -07002283 injectPostToHandler(() -> handleLocaleChanged());
Makoto Onukic51b2872016-05-04 15:24:50 -07002284 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002285 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002286 }
2287
Makoto Onuki157b1622016-06-02 16:13:10 -07002288 void handleLocaleChanged() {
2289 if (DEBUG) {
2290 Slog.d(TAG, "handleLocaleChanged");
2291 }
2292 scheduleSaveBaseState();
2293
2294 final long token = injectClearCallingIdentity();
2295 try {
2296 forEachLoadedUserLocked(u -> u.forAllPackages(p -> p.resolveResourceStrings()));
2297 } finally {
2298 injectRestoreCallingIdentity(token);
2299 }
2300 }
2301
Makoto Onuki0acbb142016-03-22 17:02:57 -07002302 /**
2303 * Package event callbacks.
2304 */
2305 @VisibleForTesting
2306 final PackageMonitor mPackageMonitor = new PackageMonitor() {
2307 @Override
2308 public void onPackageAdded(String packageName, int uid) {
2309 handlePackageAdded(packageName, getChangingUserId());
2310 }
2311
Makoto Onukicdc78f72016-03-21 15:47:52 -07002312 @Override
2313 public void onPackageUpdateFinished(String packageName, int uid) {
2314 handlePackageUpdateFinished(packageName, getChangingUserId());
2315 }
2316
2317 @Override
2318 public void onPackageRemoved(String packageName, int uid) {
2319 handlePackageRemoved(packageName, getChangingUserId());
2320 }
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002321
2322 @Override
2323 public void onPackageDataCleared(String packageName, int uid) {
2324 handlePackageDataCleared(packageName, getChangingUserId());
2325 }
Makoto Onukib08790c2016-06-23 14:05:46 -07002326
2327 @Override
2328 public boolean onPackageChanged(String packageName, int uid, String[] components) {
2329 handlePackageChanged(packageName, getChangingUserId());
2330 return false; // We don't need to receive onSomePackagesChanged(), so just false.
2331 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07002332 };
2333
Makoto Onuki0acbb142016-03-22 17:02:57 -07002334 /**
Makoto Onuki39686e82016-04-13 18:03:00 -07002335 * Called when a user is unlocked.
2336 * - Check all known packages still exist, and otherwise perform cleanup.
2337 * - If a package still exists, check the version code. If it's been updated, may need to
Makoto Onukib08790c2016-06-23 14:05:46 -07002338 * update timestamps of its shortcuts.
Makoto Onuki0acbb142016-03-22 17:02:57 -07002339 */
Makoto Onukid99c6f02016-03-28 11:02:54 -07002340 @VisibleForTesting
Makoto Onuki39686e82016-04-13 18:03:00 -07002341 void checkPackageChanges(@UserIdInt int ownerUserId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -07002342 if (DEBUG) {
Makoto Onuki39686e82016-04-13 18:03:00 -07002343 Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002344 }
Makoto Onukib08790c2016-06-23 14:05:46 -07002345 if (injectIsSafeModeEnabled()) {
2346 Slog.i(TAG, "Safe mode, skipping checkPackageChanges()");
2347 return;
2348 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002349
Makoto Onuki22fcc682016-05-17 14:52:19 -07002350 final long start = injectElapsedRealtime();
2351 try {
2352 final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002353
Makoto Onuki22fcc682016-05-17 14:52:19 -07002354 synchronized (mLock) {
2355 final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
2356
2357 // Find packages that have been uninstalled.
2358 user.forAllPackageItems(spi -> {
2359 if (spi.getPackageInfo().isShadow()) {
2360 return; // Don't delete shadow information.
2361 }
2362 if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
2363 gonePackages.add(PackageWithUser.of(spi));
2364 }
2365 });
2366 if (gonePackages.size() > 0) {
2367 for (int i = gonePackages.size() - 1; i >= 0; i--) {
2368 final PackageWithUser pu = gonePackages.get(i);
Makoto Onukib08790c2016-06-23 14:05:46 -07002369 cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId,
2370 /* appStillExists = */ false);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002371 }
Makoto Onukid99c6f02016-03-28 11:02:54 -07002372 }
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002373 final long now = injectCurrentTimeMillis();
Makoto Onuki22fcc682016-05-17 14:52:19 -07002374
2375 // Then for each installed app, publish manifest shortcuts when needed.
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002376 forUpdatedPackages(ownerUserId, user.getLastAppScanTime(), ai -> {
Makoto Onukib08790c2016-06-23 14:05:46 -07002377 user.handlePackageAddedOrUpdated(ai.packageName, /* forceRescan=*/ false);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002378 });
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002379
2380 // Write the time just before the scan, because there may be apps that have just
2381 // been updated, and we want to catch them in the next time.
2382 user.setLastAppScanTime(now);
2383 scheduleSaveUser(ownerUserId);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002384 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002385 } finally {
2386 logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002387 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002388 verifyStates();
Makoto Onukicdc78f72016-03-21 15:47:52 -07002389 }
2390
Makoto Onuki0acbb142016-03-22 17:02:57 -07002391 private void handlePackageAdded(String packageName, @UserIdInt int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -07002392 if (DEBUG) {
Makoto Onuki0acbb142016-03-22 17:02:57 -07002393 Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
2394 }
2395 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002396 final ShortcutUser user = getUserShortcutsLocked(userId);
2397 user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
Makoto Onukib08790c2016-06-23 14:05:46 -07002398 user.handlePackageAddedOrUpdated(packageName, /* forceRescan=*/ false);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002399 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002400 verifyStates();
Makoto Onuki0acbb142016-03-22 17:02:57 -07002401 }
2402
2403 private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
Makoto Onuki905e8852016-03-28 10:40:58 -07002404 if (DEBUG) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002405 Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
2406 packageName, userId));
Makoto Onuki0acbb142016-03-22 17:02:57 -07002407 }
2408 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002409 final ShortcutUser user = getUserShortcutsLocked(userId);
2410 user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
Makoto Onuki39686e82016-04-13 18:03:00 -07002411
Makoto Onuki22fcc682016-05-17 14:52:19 -07002412 if (isPackageInstalled(packageName, userId)) {
Makoto Onukib08790c2016-06-23 14:05:46 -07002413 user.handlePackageAddedOrUpdated(packageName, /* forceRescan=*/ false);
Makoto Onuki39686e82016-04-13 18:03:00 -07002414 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002415 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002416 verifyStates();
Makoto Onuki0acbb142016-03-22 17:02:57 -07002417 }
2418
Makoto Onuki2e210c42016-03-30 08:30:36 -07002419 private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
Makoto Onuki0acbb142016-03-22 17:02:57 -07002420 if (DEBUG) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002421 Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
2422 packageUserId));
Makoto Onukicdc78f72016-03-21 15:47:52 -07002423 }
Makoto Onukib08790c2016-06-23 14:05:46 -07002424 cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false);
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002425
2426 verifyStates();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002427 }
2428
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002429 private void handlePackageDataCleared(String packageName, int packageUserId) {
2430 if (DEBUG) {
2431 Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
2432 packageUserId));
Makoto Onukicdc78f72016-03-21 15:47:52 -07002433 }
Makoto Onukib08790c2016-06-23 14:05:46 -07002434 cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true);
2435
2436 verifyStates();
2437 }
2438
2439 private void handlePackageChanged(String packageName, int packageUserId) {
2440 if (DEBUG) {
2441 Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName,
2442 packageUserId));
2443 }
2444
2445 // Activities may be disabled or enabled. Just rescan the package.
2446 synchronized (mLock) {
2447 final ShortcutUser user = getUserShortcutsLocked(packageUserId);
2448
2449 user.handlePackageAddedOrUpdated(packageName, /* forceRescan=*/ true);
2450 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07002451
2452 verifyStates();
Makoto Onukicdc78f72016-03-21 15:47:52 -07002453 }
2454
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002455 // === PackageManager interaction ===
Makoto Onuki0acbb142016-03-22 17:02:57 -07002456
Makoto Onuki22fcc682016-05-17 14:52:19 -07002457 @Nullable
Makoto Onuki905e8852016-03-28 10:40:58 -07002458 PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
2459 return injectPackageInfo(packageName, userId, true);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002460 }
2461
Makoto Onuki22fcc682016-05-17 14:52:19 -07002462 @Nullable
2463 PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) {
2464 return injectPackageInfo(packageName, userId, false);
2465 }
2466
Makoto Onuki905e8852016-03-28 10:40:58 -07002467 int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002468 final long token = injectClearCallingIdentity();
Makoto Onuki905e8852016-03-28 10:40:58 -07002469 try {
2470 return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS
2471 , userId);
2472 } catch (RemoteException e) {
2473 // Shouldn't happen.
2474 Slog.wtf(TAG, "RemoteException", e);
2475 return -1;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002476 } finally {
2477 injectRestoreCallingIdentity(token);
Makoto Onuki905e8852016-03-28 10:40:58 -07002478 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002479 }
2480
Makoto Onuki22fcc682016-05-17 14:52:19 -07002481 @Nullable
Makoto Onuki0acbb142016-03-22 17:02:57 -07002482 @VisibleForTesting
2483 PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
2484 boolean getSignatures) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07002485 final long start = injectElapsedRealtime();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002486 final long token = injectClearCallingIdentity();
Makoto Onuki0acbb142016-03-22 17:02:57 -07002487 try {
Makoto Onuki905e8852016-03-28 10:40:58 -07002488 return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS
Makoto Onukib08790c2016-06-23 14:05:46 -07002489 | (getSignatures ? PackageManager.GET_SIGNATURES : 0)
Makoto Onuki0acbb142016-03-22 17:02:57 -07002490 , userId);
2491 } catch (RemoteException e) {
2492 // Shouldn't happen.
2493 Slog.wtf(TAG, "RemoteException", e);
2494 return null;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002495 } finally {
2496 injectRestoreCallingIdentity(token);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002497
2498 logDurationStat(
2499 (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO),
2500 start);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002501 }
2502 }
2503
Makoto Onuki22fcc682016-05-17 14:52:19 -07002504 @Nullable
Makoto Onuki905e8852016-03-28 10:40:58 -07002505 @VisibleForTesting
2506 ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07002507 final long start = injectElapsedRealtime();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002508 final long token = injectClearCallingIdentity();
Makoto Onuki905e8852016-03-28 10:40:58 -07002509 try {
2510 return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
2511 } catch (RemoteException e) {
2512 // Shouldn't happen.
2513 Slog.wtf(TAG, "RemoteException", e);
2514 return null;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002515 } finally {
2516 injectRestoreCallingIdentity(token);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002517
2518 logDurationStat(Stats.GET_APPLICATION_INFO, start);
Makoto Onuki905e8852016-03-28 10:40:58 -07002519 }
2520 }
2521
Makoto Onuki22fcc682016-05-17 14:52:19 -07002522 @Nullable
Makoto Onukib08790c2016-06-23 14:05:46 -07002523 ActivityInfo injectGetActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002524 final long start = injectElapsedRealtime();
2525 final long token = injectClearCallingIdentity();
2526 try {
Makoto Onukib08790c2016-06-23 14:05:46 -07002527 return mIPackageManager.getActivityInfo(activity,
2528 PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA, userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002529 } catch (RemoteException e) {
2530 // Shouldn't happen.
2531 Slog.wtf(TAG, "RemoteException", e);
2532 return null;
2533 } finally {
2534 injectRestoreCallingIdentity(token);
2535
Makoto Onukib08790c2016-06-23 14:05:46 -07002536 logDurationStat(Stats.GET_ACTIVITY_WITH_METADATA, start);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002537 }
2538 }
2539
2540 @Nullable
2541 @VisibleForTesting
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002542 List<PackageInfo> injectInstalledPackages(@UserIdInt int userId) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002543 final long start = injectElapsedRealtime();
2544 final long token = injectClearCallingIdentity();
2545 try {
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002546 final ParceledListSlice<PackageInfo> parceledList =
2547 mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002548 if (parceledList == null) {
2549 return Collections.emptyList();
2550 }
2551 return parceledList.getList();
2552 } catch (RemoteException e) {
2553 // Shouldn't happen.
2554 Slog.wtf(TAG, "RemoteException", e);
2555 return null;
2556 } finally {
2557 injectRestoreCallingIdentity(token);
2558
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002559 logDurationStat(Stats.GET_INSTALLED_PACKAGES, start);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002560 }
2561 }
2562
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002563 private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime,
Makoto Onuki22fcc682016-05-17 14:52:19 -07002564 Consumer<ApplicationInfo> callback) {
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002565 if (DEBUG) {
2566 Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime);
2567 }
2568 final List<PackageInfo> list = injectInstalledPackages(userId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002569 for (int i = list.size() - 1; i >= 0; i--) {
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002570 final PackageInfo pi = list.get(i);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002571
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002572 if (((pi.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0)
2573 && (pi.lastUpdateTime >= lastScanTime)) {
2574 if (DEBUG) {
2575 Slog.d(TAG, "Found updated package " + pi.packageName);
2576 }
2577 callback.accept(pi.applicationInfo);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002578 }
2579 }
2580 }
2581
Makoto Onuki905e8852016-03-28 10:40:58 -07002582 private boolean isApplicationFlagSet(String packageName, int userId, int flags) {
2583 final ApplicationInfo ai = injectApplicationInfo(packageName, userId);
2584 return (ai != null) && ((ai.flags & flags) == flags);
2585 }
2586
Makoto Onuki2e210c42016-03-30 08:30:36 -07002587 boolean isPackageInstalled(String packageName, int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002588 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
2589 }
2590
Makoto Onuki22fcc682016-05-17 14:52:19 -07002591 @Nullable
2592 XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002593 return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
Makoto Onuki39686e82016-04-13 18:03:00 -07002594 }
2595
Makoto Onuki157b1622016-06-02 16:13:10 -07002596 @Nullable
2597 Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
2598 final long start = injectElapsedRealtime();
2599 final long token = injectClearCallingIdentity();
2600 try {
2601 return mContext.getPackageManager().getResourcesForApplicationAsUser(
2602 packageName, userId);
2603 } catch (NameNotFoundException e) {
2604 Slog.e(TAG, "Resources for package " + packageName + " not found");
2605 return null;
2606 } finally {
2607 injectRestoreCallingIdentity(token);
2608
2609 logDurationStat(Stats.GET_APPLICATION_RESOURCES, start);
2610 }
2611 }
2612
Makoto Onukib08790c2016-06-23 14:05:46 -07002613 private Intent getMainActivityIntent() {
2614 final Intent intent = new Intent(Intent.ACTION_MAIN);
2615 intent.addCategory(LAUNCHER_INTENT_CATEGORY);
2616 return intent;
2617 }
2618
2619 @Nullable
2620 ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
2621 final long start = injectElapsedRealtime();
2622 final long token = injectClearCallingIdentity();
2623 try {
2624 final Intent intent = getMainActivityIntent();
2625 intent.setPackage(packageName);
2626
2627 final List<ResolveInfo> resolved =
2628 mContext.getPackageManager().queryIntentActivitiesAsUser(
2629 intent, PACKAGE_MATCH_FLAGS, userId);
2630
2631 return (resolved == null || resolved.size() == 0)
2632 ? null : resolved.get(0).activityInfo.getComponentName();
2633 } finally {
2634 injectRestoreCallingIdentity(token);
2635
2636 logDurationStat(Stats.GET_LAUNCHER_ACTIVITY, start);
2637 }
2638 }
2639
2640 boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
2641 final long start = injectElapsedRealtime();
2642 final long token = injectClearCallingIdentity();
2643 try {
2644 final Intent intent = getMainActivityIntent();
2645 intent.setPackage(activity.getPackageName());
2646 intent.setComponent(activity);
2647
2648 final List<ResolveInfo> resolved =
2649 mContext.getPackageManager().queryIntentActivitiesAsUser(
2650 intent, PACKAGE_MATCH_FLAGS, userId);
2651
2652 return resolved != null && resolved.size() > 0;
2653 } finally {
2654 injectRestoreCallingIdentity(token);
2655
2656 logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
2657 }
2658 }
2659
2660 @NonNull
2661 List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
2662 final long start = injectElapsedRealtime();
2663 final long token = injectClearCallingIdentity();
2664 try {
2665 final Intent intent = getMainActivityIntent();
2666 intent.setPackage(packageName);
2667
2668 final List<ResolveInfo> resolved =
2669 mContext.getPackageManager().queryIntentActivitiesAsUser(
2670 intent, PACKAGE_MATCH_FLAGS, userId);
2671
2672 return (resolved != null) ? resolved : new ArrayList<>(0);
2673 } finally {
2674 injectRestoreCallingIdentity(token);
2675
2676 logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
2677 }
2678 }
2679
2680 boolean injectIsSafeModeEnabled() {
2681 final long token = injectClearCallingIdentity();
2682 try {
2683 return IWindowManager.Stub
2684 .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE))
2685 .isSafeModeEnabled();
2686 } catch (RemoteException e) {
2687 return false; // Shouldn't happen though.
2688 } finally {
2689 injectRestoreCallingIdentity(token);
2690 }
2691 }
2692
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002693 // === Backup & restore ===
2694
Makoto Onuki0acbb142016-03-22 17:02:57 -07002695 boolean shouldBackupApp(String packageName, int userId) {
Makoto Onuki905e8852016-03-28 10:40:58 -07002696 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002697 }
2698
Makoto Onuki2e210c42016-03-30 08:30:36 -07002699 boolean shouldBackupApp(PackageInfo pi) {
2700 return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
2701 }
2702
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002703 @Override
Makoto Onuki2e210c42016-03-30 08:30:36 -07002704 public byte[] getBackupPayload(@UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002705 enforceSystem();
2706 if (DEBUG) {
2707 Slog.d(TAG, "Backing up user " + userId);
2708 }
2709 synchronized (mLock) {
2710 final ShortcutUser user = getUserShortcutsLocked(userId);
2711 if (user == null) {
2712 Slog.w(TAG, "Can't backup: user not found: id=" + userId);
2713 return null;
2714 }
2715
Makoto Onukic51b2872016-05-04 15:24:50 -07002716 user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave());
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002717
2718 // Then save.
2719 final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024);
2720 try {
2721 saveUserInternalLocked(userId, os, /* forBackup */ true);
Makoto Onukib08790c2016-06-23 14:05:46 -07002722 } catch (XmlPullParserException | IOException e) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002723 // Shouldn't happen.
2724 Slog.w(TAG, "Backup failed.", e);
2725 return null;
2726 }
2727 return os.toByteArray();
2728 }
2729 }
2730
2731 @Override
Makoto Onuki2e210c42016-03-30 08:30:36 -07002732 public void applyRestore(byte[] payload, @UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002733 enforceSystem();
2734 if (DEBUG) {
2735 Slog.d(TAG, "Restoring user " + userId);
2736 }
2737 final ShortcutUser user;
2738 final ByteArrayInputStream is = new ByteArrayInputStream(payload);
2739 try {
2740 user = loadUserInternal(userId, is, /* fromBackup */ true);
Makoto Onukib08790c2016-06-23 14:05:46 -07002741 } catch (XmlPullParserException | IOException e) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002742 Slog.w(TAG, "Restoration failed.", e);
2743 return;
2744 }
2745 synchronized (mLock) {
2746 mUsers.put(userId, user);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002747
2748 // Then purge all the save images.
2749 final File bitmapPath = getUserBitmapFilePath(userId);
2750 final boolean success = FileUtils.deleteContents(bitmapPath);
2751 if (!success) {
2752 Slog.w(TAG, "Failed to delete " + bitmapPath);
2753 }
2754
2755 saveUserLocked(userId);
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002756 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07002757 }
2758
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002759 // === Dump ===
2760
2761 @Override
2762 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2763 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2764 != PackageManager.PERMISSION_GRANTED) {
2765 pw.println("Permission Denial: can't dump UserManager from from pid="
2766 + Binder.getCallingPid()
2767 + ", uid=" + Binder.getCallingUid()
2768 + " without permission "
2769 + android.Manifest.permission.DUMP);
2770 return;
2771 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002772 dumpInner(pw, args);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002773 }
2774
2775 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002776 void dumpInner(PrintWriter pw, String[] args) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002777 synchronized (mLock) {
2778 final long now = injectCurrentTimeMillis();
2779 pw.print("Now: [");
2780 pw.print(now);
2781 pw.print("] ");
2782 pw.print(formatTime(now));
Makoto Onuki55046222016-03-08 10:49:47 -08002783
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002784 pw.print(" Raw last reset: [");
2785 pw.print(mRawLastResetTime);
2786 pw.print("] ");
2787 pw.print(formatTime(mRawLastResetTime));
2788
2789 final long last = getLastResetTimeLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002790 pw.print(" Last reset: [");
2791 pw.print(last);
2792 pw.print("] ");
2793 pw.print(formatTime(last));
2794
Makoto Onuki55046222016-03-08 10:49:47 -08002795 final long next = getNextResetTimeLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002796 pw.print(" Next reset: [");
2797 pw.print(next);
2798 pw.print("] ");
2799 pw.print(formatTime(next));
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002800
2801 pw.print(" Locale change seq#: ");
2802 pw.print(mLocaleChangeSequenceNumber.get());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002803 pw.println();
2804
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002805 pw.print(" Config:");
2806 pw.print(" Max icon dim: ");
2807 pw.println(mMaxIconDimension);
2808 pw.print(" Icon format: ");
2809 pw.println(mIconPersistFormat);
2810 pw.print(" Icon quality: ");
Makoto Onuki2e210c42016-03-30 08:30:36 -07002811 pw.println(mIconPersistQuality);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07002812 pw.print(" saveDelayMillis: ");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002813 pw.println(mSaveDelayMillis);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07002814 pw.print(" resetInterval: ");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002815 pw.println(mResetInterval);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07002816 pw.print(" maxUpdatesPerInterval: ");
Makoto Onukib6d35232016-04-04 15:57:17 -07002817 pw.println(mMaxUpdatesPerInterval);
Makoto Onukib08790c2016-06-23 14:05:46 -07002818 pw.print(" maxShortcutsPerActivity: ");
Makoto Onukib5a012f2016-06-21 11:13:53 -07002819 pw.println(mMaxShortcuts);
Makoto Onuki55046222016-03-08 10:49:47 -08002820 pw.println();
2821
Makoto Onuki2e210c42016-03-30 08:30:36 -07002822 pw.println(" Stats:");
2823 synchronized (mStatLock) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002824 final String p = " ";
Makoto Onuki2e210c42016-03-30 08:30:36 -07002825 dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()");
2826 dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check");
2827
2828 dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()");
2829 dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)");
2830 dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07002831 dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps");
Makoto Onukib08790c2016-06-23 14:05:46 -07002832 dumpStatLS(pw, p, Stats.GET_ACTIVITY_WITH_METADATA, "getActivity+metadata");
Makoto Onuki6dd9fb72016-06-01 13:55:54 -07002833 dumpStatLS(pw, p, Stats.GET_INSTALLED_PACKAGES, "getInstalledPackages");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002834 dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges");
Makoto Onuki157b1622016-06-02 16:13:10 -07002835 dumpStatLS(pw, p, Stats.GET_APPLICATION_RESOURCES, "getApplicationResources");
2836 dumpStatLS(pw, p, Stats.RESOURCE_NAME_LOOKUP, "resourceNameLookup");
Makoto Onukib08790c2016-06-23 14:05:46 -07002837 dumpStatLS(pw, p, Stats.GET_LAUNCHER_ACTIVITY, "getLauncherActivity");
2838 dumpStatLS(pw, p, Stats.CHECK_LAUNCHER_ACTIVITY, "checkLauncherActivity");
Makoto Onuki2e210c42016-03-30 08:30:36 -07002839 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002840
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08002841 for (int i = 0; i < mUsers.size(); i++) {
2842 pw.println();
Makoto Onukic51b2872016-05-04 15:24:50 -07002843 mUsers.valueAt(i).dump(pw, " ");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002844 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002845
2846 pw.println();
2847 pw.println(" UID state:");
2848
2849 for (int i = 0; i < mUidState.size(); i++) {
2850 final int uid = mUidState.keyAt(i);
2851 final int state = mUidState.valueAt(i);
2852 pw.print(" UID=");
2853 pw.print(uid);
2854 pw.print(" state=");
2855 pw.print(state);
2856 if (isProcessStateForeground(state)) {
2857 pw.print(" [FG]");
2858 }
2859 pw.print(" last FG=");
2860 pw.print(mUidLastForegroundElapsedTime.get(uid));
2861 pw.println();
2862 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002863 }
2864 }
2865
Makoto Onuki41066a62016-03-09 16:18:44 -08002866 static String formatTime(long time) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002867 Time tobj = new Time();
2868 tobj.set(time);
2869 return tobj.format("%Y-%m-%d %H:%M:%S");
2870 }
2871
Makoto Onuki2e210c42016-03-30 08:30:36 -07002872 private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) {
2873 pw.print(prefix);
2874 final int count = mCountStats[statId];
2875 final long dur = mDurationStats[statId];
2876 pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
2877 label, count, dur,
2878 (count == 0 ? 0 : ((double) dur) / count)));
2879 }
2880
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002881 // === Shell support ===
2882
2883 @Override
2884 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
2885 String[] args, ResultReceiver resultReceiver) throws RemoteException {
2886
2887 enforceShell();
2888
Makoto Onukib08790c2016-06-23 14:05:46 -07002889 final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
2890
2891 resultReceiver.send(status, null);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002892 }
2893
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002894 static class CommandException extends Exception {
2895 public CommandException(String message) {
2896 super(message);
2897 }
2898 }
2899
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002900 /**
2901 * Handle "adb shell cmd".
2902 */
2903 private class MyShellCommand extends ShellCommand {
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002904
2905 private int mUserId = UserHandle.USER_SYSTEM;
2906
2907 private void parseOptions(boolean takeUser)
2908 throws CommandException {
2909 String opt;
2910 while ((opt = getNextOption()) != null) {
2911 switch (opt) {
2912 case "--user":
2913 if (takeUser) {
2914 mUserId = UserHandle.parseUserArg(getNextArgRequired());
2915 break;
2916 }
2917 // fallthrough
2918 default:
2919 throw new CommandException("Unknown option: " + opt);
2920 }
2921 }
2922 }
2923
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002924 @Override
2925 public int onCommand(String cmd) {
2926 if (cmd == null) {
2927 return handleDefaultCommands(cmd);
2928 }
2929 final PrintWriter pw = getOutPrintWriter();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002930 try {
2931 switch (cmd) {
2932 case "reset-package-throttling":
2933 handleResetPackageThrottling();
2934 break;
2935 case "reset-throttling":
2936 handleResetThrottling();
2937 break;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002938 case "reset-all-throttling":
2939 handleResetAllThrottling();
2940 break;
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002941 case "override-config":
2942 handleOverrideConfig();
2943 break;
2944 case "reset-config":
2945 handleResetConfig();
2946 break;
2947 case "clear-default-launcher":
2948 handleClearDefaultLauncher();
2949 break;
2950 case "get-default-launcher":
2951 handleGetDefaultLauncher();
2952 break;
2953 case "refresh-default-launcher":
2954 handleRefreshDefaultLauncher();
2955 break;
Makoto Onukiac214972016-04-04 10:19:45 -07002956 case "unload-user":
2957 handleUnloadUser();
2958 break;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002959 case "clear-shortcuts":
2960 handleClearShortcuts();
2961 break;
Makoto Onukib08790c2016-06-23 14:05:46 -07002962 case "verify-states": // hidden command to verify various internal states.
2963 handleVerifyStates();
2964 break;
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002965 default:
2966 return handleDefaultCommands(cmd);
2967 }
2968 } catch (CommandException e) {
2969 pw.println("Error: " + e.getMessage());
2970 return 1;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002971 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002972 pw.println("Success");
2973 return 0;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002974 }
2975
2976 @Override
2977 public void onHelp() {
2978 final PrintWriter pw = getOutPrintWriter();
2979 pw.println("Usage: cmd shortcut COMMAND [options ...]");
2980 pw.println();
2981 pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
2982 pw.println(" Reset throttling for a package");
2983 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002984 pw.println("cmd shortcut reset-throttling [--user USER_ID]");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002985 pw.println(" Reset throttling for all packages and users");
2986 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002987 pw.println("cmd shortcut reset-all-throttling");
2988 pw.println(" Reset the throttling state for all users");
2989 pw.println();
Makoto Onuki4362a662016-03-08 18:59:09 -08002990 pw.println("cmd shortcut override-config CONFIG");
2991 pw.println(" Override the configuration for testing (will last until reboot)");
2992 pw.println();
2993 pw.println("cmd shortcut reset-config");
2994 pw.println(" Reset the configuration set with \"update-config\"");
2995 pw.println();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002996 pw.println("cmd shortcut clear-default-launcher [--user USER_ID]");
2997 pw.println(" Clear the cached default launcher");
2998 pw.println();
2999 pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
3000 pw.println(" Show the cached default launcher");
3001 pw.println();
3002 pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]");
3003 pw.println(" Refresh the cached default launcher");
3004 pw.println();
Makoto Onukiac214972016-04-04 10:19:45 -07003005 pw.println("cmd shortcut unload-user [--user USER_ID]");
3006 pw.println(" Unload a user from the memory");
3007 pw.println(" (This should not affect any observable behavior)");
3008 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003009 pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
3010 pw.println(" Remove all shortcuts from a package, including pinned shortcuts");
3011 pw.println();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003012 }
3013
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003014 private void handleResetThrottling() throws CommandException {
Makoto Onuki4554d0e2016-03-14 15:51:41 -07003015 parseOptions(/* takeUser =*/ true);
3016
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003017 Slog.i(TAG, "cmd: handleResetThrottling");
3018
Makoto Onuki4554d0e2016-03-14 15:51:41 -07003019 resetThrottlingInner(mUserId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003020 }
3021
3022 private void handleResetAllThrottling() {
3023 Slog.i(TAG, "cmd: handleResetAllThrottling");
3024
3025 resetAllThrottlingInner();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003026 }
3027
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003028 private void handleResetPackageThrottling() throws CommandException {
3029 parseOptions(/* takeUser =*/ true);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003030
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003031 final String packageName = getNextArgRequired();
3032
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003033 Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName);
3034
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07003035 resetPackageThrottling(packageName, mUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003036 }
Makoto Onuki4362a662016-03-08 18:59:09 -08003037
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003038 private void handleOverrideConfig() throws CommandException {
Makoto Onuki4362a662016-03-08 18:59:09 -08003039 final String config = getNextArgRequired();
3040
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003041 Slog.i(TAG, "cmd: handleOverrideConfig: " + config);
3042
Makoto Onuki4362a662016-03-08 18:59:09 -08003043 synchronized (mLock) {
3044 if (!updateConfigurationLocked(config)) {
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003045 throw new CommandException("override-config failed. See logcat for details.");
Makoto Onuki4362a662016-03-08 18:59:09 -08003046 }
3047 }
Makoto Onuki4362a662016-03-08 18:59:09 -08003048 }
3049
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003050 private void handleResetConfig() {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003051 Slog.i(TAG, "cmd: handleResetConfig");
3052
Makoto Onuki4362a662016-03-08 18:59:09 -08003053 synchronized (mLock) {
3054 loadConfigurationLocked();
3055 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003056 }
3057
3058 private void clearLauncher() {
3059 synchronized (mLock) {
Makoto Onukic51b2872016-05-04 15:24:50 -07003060 getUserShortcutsLocked(mUserId).setDefaultLauncherComponent(null);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003061 }
3062 }
3063
3064 private void showLauncher() {
3065 synchronized (mLock) {
3066 // This ensures to set the cached launcher. Package name doesn't matter.
3067 hasShortcutHostPermissionInner("-", mUserId);
3068
3069 getOutPrintWriter().println("Launcher: "
Makoto Onukic51b2872016-05-04 15:24:50 -07003070 + getUserShortcutsLocked(mUserId).getDefaultLauncherComponent());
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003071 }
3072 }
3073
3074 private void handleClearDefaultLauncher() throws CommandException {
3075 parseOptions(/* takeUser =*/ true);
3076
3077 clearLauncher();
3078 }
3079
3080 private void handleGetDefaultLauncher() throws CommandException {
3081 parseOptions(/* takeUser =*/ true);
3082
3083 showLauncher();
3084 }
3085
3086 private void handleRefreshDefaultLauncher() throws CommandException {
3087 parseOptions(/* takeUser =*/ true);
3088
3089 clearLauncher();
3090 showLauncher();
Makoto Onuki4362a662016-03-08 18:59:09 -08003091 }
Makoto Onukiac214972016-04-04 10:19:45 -07003092
3093 private void handleUnloadUser() throws CommandException {
3094 parseOptions(/* takeUser =*/ true);
3095
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003096 Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId);
3097
Makoto Onukiac214972016-04-04 10:19:45 -07003098 ShortcutService.this.handleCleanupUser(mUserId);
3099 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003100
3101 private void handleClearShortcuts() throws CommandException {
3102 parseOptions(/* takeUser =*/ true);
3103 final String packageName = getNextArgRequired();
3104
3105 Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName);
3106
Makoto Onukib08790c2016-06-23 14:05:46 -07003107 ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
3108 /* appStillExists = */ true);
3109 }
3110
3111 private void handleVerifyStates() throws CommandException {
3112 try {
3113 verifyStatesForce(); // This will throw when there's an issue.
3114 } catch (Throwable th) {
3115 throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
3116 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07003117 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003118 }
3119
3120 // === Unit test support ===
3121
3122 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07003123 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003124 long injectCurrentTimeMillis() {
3125 return System.currentTimeMillis();
3126 }
3127
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07003128 @VisibleForTesting
3129 long injectElapsedRealtime() {
3130 return SystemClock.elapsedRealtime();
3131 }
3132
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003133 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07003134 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003135 int injectBinderCallingUid() {
3136 return getCallingUid();
3137 }
3138
Makoto Onuki31459242016-03-22 11:12:18 -07003139 private int getCallingUserId() {
Makoto Onuki4554d0e2016-03-14 15:51:41 -07003140 return UserHandle.getUserId(injectBinderCallingUid());
3141 }
3142
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07003143 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07003144 @VisibleForTesting
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07003145 long injectClearCallingIdentity() {
3146 return Binder.clearCallingIdentity();
3147 }
3148
3149 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07003150 @VisibleForTesting
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07003151 void injectRestoreCallingIdentity(long token) {
3152 Binder.restoreCallingIdentity(token);
3153 }
3154
Makoto Onukide667372016-03-15 14:29:20 -07003155 final void wtf(String message) {
Makoto Onukib08790c2016-06-23 14:05:46 -07003156 wtf(message, /* exception= */ null);
Makoto Onukide667372016-03-15 14:29:20 -07003157 }
3158
Makoto Onuki2e210c42016-03-30 08:30:36 -07003159 // Injection point.
Makoto Onukide667372016-03-15 14:29:20 -07003160 void wtf(String message, Exception e) {
3161 Slog.wtf(TAG, message, e);
3162 }
3163
Makoto Onuki31459242016-03-22 11:12:18 -07003164 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003165 File injectSystemDataPath() {
3166 return Environment.getDataSystemDirectory();
3167 }
3168
Makoto Onuki31459242016-03-22 11:12:18 -07003169 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003170 File injectUserDataPath(@UserIdInt int userId) {
Makoto Onuki55046222016-03-08 10:49:47 -08003171 return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
3172 }
3173
Makoto Onuki4362a662016-03-08 18:59:09 -08003174 @VisibleForTesting
Makoto Onuki55046222016-03-08 10:49:47 -08003175 boolean injectIsLowRamDevice() {
3176 return ActivityManager.isLowRamDeviceStatic();
3177 }
3178
Makoto Onuki31459242016-03-22 11:12:18 -07003179 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07003180 void injectRegisterUidObserver(IUidObserver observer, int which) {
3181 try {
3182 ActivityManagerNative.getDefault().registerUidObserver(observer, which);
3183 } catch (RemoteException shouldntHappen) {
3184 }
3185 }
3186
3187 @VisibleForTesting
Makoto Onuki2d5b4652016-03-11 16:09:54 -08003188 PackageManagerInternal injectPackageManagerInternal() {
3189 return mPackageManagerInternal;
3190 }
3191
Makoto Onuki55046222016-03-08 10:49:47 -08003192 File getUserBitmapFilePath(@UserIdInt int userId) {
3193 return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003194 }
3195
3196 @VisibleForTesting
Makoto Onuki31459242016-03-22 11:12:18 -07003197 SparseArray<ShortcutUser> getShortcutsForTest() {
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08003198 return mUsers;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003199 }
3200
3201 @VisibleForTesting
Makoto Onukib5a012f2016-06-21 11:13:53 -07003202 int getMaxShortcutsForTest() {
3203 return mMaxShortcuts;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003204 }
3205
3206 @VisibleForTesting
Makoto Onukib6d35232016-04-04 15:57:17 -07003207 int getMaxUpdatesPerIntervalForTest() {
3208 return mMaxUpdatesPerInterval;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003209 }
3210
3211 @VisibleForTesting
Makoto Onuki4362a662016-03-08 18:59:09 -08003212 long getResetIntervalForTest() {
3213 return mResetInterval;
Makoto Onuki55046222016-03-08 10:49:47 -08003214 }
3215
3216 @VisibleForTesting
Makoto Onuki4362a662016-03-08 18:59:09 -08003217 int getMaxIconDimensionForTest() {
3218 return mMaxIconDimension;
3219 }
3220
3221 @VisibleForTesting
3222 CompressFormat getIconPersistFormatForTest() {
3223 return mIconPersistFormat;
3224 }
3225
3226 @VisibleForTesting
3227 int getIconPersistQualityForTest() {
3228 return mIconPersistQuality;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08003229 }
Makoto Onuki41066a62016-03-09 16:18:44 -08003230
3231 @VisibleForTesting
Makoto Onuki22fcc682016-05-17 14:52:19 -07003232 ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
Makoto Onuki41066a62016-03-09 16:18:44 -08003233 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07003234 final ShortcutUser user = mUsers.get(userId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07003235 if (user == null) return null;
3236
Makoto Onuki22fcc682016-05-17 14:52:19 -07003237 return user.getAllPackagesForTest().get(packageName);
3238 }
3239 }
3240
3241 @VisibleForTesting
3242 ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
3243 synchronized (mLock) {
3244 final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07003245 if (pkg == null) return null;
3246
3247 return pkg.findShortcutById(shortcutId);
Makoto Onuki41066a62016-03-09 16:18:44 -08003248 }
3249 }
Makoto Onuki7001a612016-05-27 13:24:28 -07003250
3251 /**
3252 * Control whether {@link #verifyStates} should be performed. We always perform it during unit
3253 * tests.
3254 */
3255 @VisibleForTesting
3256 boolean injectShouldPerformVerification() {
3257 return DEBUG;
3258 }
3259
3260 /**
3261 * Check various internal states and throws if there's any inconsistency.
3262 * This is normally only enabled during unit tests.
3263 */
3264 final void verifyStates() {
3265 if (injectShouldPerformVerification()) {
3266 verifyStatesInner();
3267 }
3268 }
3269
Makoto Onukib08790c2016-06-23 14:05:46 -07003270 private final void verifyStatesForce() {
3271 verifyStatesInner();
3272 }
3273
Makoto Onuki7001a612016-05-27 13:24:28 -07003274 private void verifyStatesInner() {
3275 synchronized (this) {
3276 forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
3277 }
3278 }
Makoto Onuki41066a62016-03-09 16:18:44 -08003279}