blob: 78352319e1567f5c11f1c2f0425ddf4412757a6d [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 Onuki22fcc682016-05-17 14:52:19 -070045import android.content.res.XmlResourceParser;
Makoto Onuki55046222016-03-08 10:49:47 -080046import android.graphics.Bitmap;
47import android.graphics.Bitmap.CompressFormat;
Makoto Onuki55046222016-03-08 10:49:47 -080048import android.graphics.Canvas;
49import android.graphics.RectF;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080050import android.graphics.drawable.Icon;
51import android.os.Binder;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080052import android.os.Environment;
Makoto Onuki2e210c42016-03-30 08:30:36 -070053import android.os.FileUtils;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080054import android.os.Handler;
Makoto Onukiaa8b94a2016-03-17 13:14:05 -070055import android.os.Looper;
Makoto Onuki55046222016-03-08 10:49:47 -080056import android.os.ParcelFileDescriptor;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080057import android.os.PersistableBundle;
58import android.os.Process;
59import android.os.RemoteException;
60import android.os.ResultReceiver;
Makoto Onuki55046222016-03-08 10:49:47 -080061import android.os.SELinux;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080062import android.os.ShellCommand;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070063import android.os.SystemClock;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080064import android.os.UserHandle;
Makoto Onukicdc78f72016-03-21 15:47:52 -070065import android.os.UserManager;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080066import android.text.TextUtils;
67import android.text.format.Time;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080068import android.util.ArraySet;
69import android.util.AtomicFile;
Makoto Onuki4362a662016-03-08 18:59:09 -080070import android.util.KeyValueListParser;
Makoto Onukiac042502016-05-20 16:39:42 -070071import android.util.Log;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080072import android.util.Slog;
73import android.util.SparseArray;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070074import android.util.SparseIntArray;
75import android.util.SparseLongArray;
Makoto Onuki55046222016-03-08 10:49:47 -080076import android.util.TypedValue;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080077import android.util.Xml;
78
79import com.android.internal.annotations.GuardedBy;
80import com.android.internal.annotations.VisibleForTesting;
Makoto Onukicdc78f72016-03-21 15:47:52 -070081import com.android.internal.content.PackageMonitor;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080082import com.android.internal.os.BackgroundThread;
83import com.android.internal.util.FastXmlSerializer;
84import com.android.internal.util.Preconditions;
85import com.android.server.LocalServices;
86import com.android.server.SystemService;
Makoto Onukid99c6f02016-03-28 11:02:54 -070087import com.android.server.pm.ShortcutUser.PackageWithUser;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080088
89import libcore.io.IoUtils;
90
91import org.xmlpull.v1.XmlPullParser;
92import org.xmlpull.v1.XmlPullParserException;
93import org.xmlpull.v1.XmlSerializer;
94
Makoto Onuki9da23fc2016-03-29 11:14:42 -070095import java.io.BufferedInputStream;
96import java.io.BufferedOutputStream;
97import java.io.ByteArrayInputStream;
98import java.io.ByteArrayOutputStream;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080099import java.io.File;
100import java.io.FileDescriptor;
101import java.io.FileInputStream;
102import java.io.FileNotFoundException;
103import java.io.FileOutputStream;
104import java.io.IOException;
Makoto Onuki55046222016-03-08 10:49:47 -0800105import java.io.InputStream;
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700106import java.io.OutputStream;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800107import java.io.PrintWriter;
Makoto Onuki7001a612016-05-27 13:24:28 -0700108import java.lang.annotation.Retention;
109import java.lang.annotation.RetentionPolicy;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800110import java.net.URISyntaxException;
111import java.nio.charset.StandardCharsets;
112import java.util.ArrayList;
Makoto Onuki22fcc682016-05-17 14:52:19 -0700113import java.util.Collections;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800114import java.util.List;
Makoto Onukic51b2872016-05-04 15:24:50 -0700115import java.util.concurrent.atomic.AtomicBoolean;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700116import java.util.concurrent.atomic.AtomicLong;
Makoto Onuki2e210c42016-03-30 08:30:36 -0700117import java.util.function.Consumer;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800118import java.util.function.Predicate;
119
120/**
121 * TODO:
Makoto Onuki22fcc682016-05-17 14:52:19 -0700122 * - HandleUnlockUser needs to be async. Wait on it in onCleanupUser.
Makoto Onuki20c95f82016-05-11 16:51:01 -0700123 *
124 * - Implement reportShortcutUsed().
125 *
Makoto Onuki22fcc682016-05-17 14:52:19 -0700126 * - validateForXml() should be removed.
127 *
Makoto Onuki20c95f82016-05-11 16:51:01 -0700128 * - Ranks should be recalculated after each update.
129 *
130 * - When the system locale changes, update timestamps for shortcuts with string resources,
Makoto Onuki22fcc682016-05-17 14:52:19 -0700131 * and notify the launcher. Right now, it resets the throttling, but timestamps are not changed
132 * and there's no notification either.
133 *
134 * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800135 *
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700136 * - Default launcher check does take a few ms. Worth caching.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800137 *
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700138 * - Detect when already registered instances are passed to APIs again, which might break
139 * internal bitmap handling.
Makoto Onuki2e210c42016-03-30 08:30:36 -0700140 *
141 * - Add more call stats.
Makoto Onuki22fcc682016-05-17 14:52:19 -0700142 *
Makoto Onuki7001a612016-05-27 13:24:28 -0700143 * - Rename mMaxDynamicShortcuts, because it includes manifest shortcuts too.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800144 */
145public class ShortcutService extends IShortcutService.Stub {
Makoto Onuki55046222016-03-08 10:49:47 -0800146 static final String TAG = "ShortcutService";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800147
Makoto Onuki7001a612016-05-27 13:24:28 -0700148 static final boolean DEBUG = false; // STOPSHIP if true
Makoto Onuki41066a62016-03-09 16:18:44 -0800149 static final boolean DEBUG_LOAD = false; // STOPSHIP if true
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700150 static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800151
Makoto Onuki4362a662016-03-08 18:59:09 -0800152 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700153 static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
Makoto Onuki4362a662016-03-08 18:59:09 -0800154
155 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700156 static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10;
Makoto Onuki4362a662016-03-08 18:59:09 -0800157
158 @VisibleForTesting
159 static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
160
161 @VisibleForTesting
162 static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
163
164 @VisibleForTesting
165 static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
166
167 @VisibleForTesting
168 static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
169
170 @VisibleForTesting
171 static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800172
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700173 @VisibleForTesting
174 static final int DEFAULT_SAVE_DELAY_MS = 3000;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800175
176 @VisibleForTesting
177 static final String FILENAME_BASE_STATE = "shortcut_service.xml";
178
179 @VisibleForTesting
180 static final String DIRECTORY_PER_USER = "shortcut_service";
181
182 @VisibleForTesting
183 static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
184
Makoto Onuki55046222016-03-08 10:49:47 -0800185 static final String DIRECTORY_BITMAPS = "bitmaps";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800186
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700187 private static final String TAG_ROOT = "root";
188 private static final String TAG_LAST_RESET_TIME = "last_reset_time";
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700189 private static final String TAG_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale_seq_no";
Makoto Onuki55046222016-03-08 10:49:47 -0800190
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700191 private static final String ATTR_VALUE = "value";
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800192
Makoto Onuki4362a662016-03-08 18:59:09 -0800193 @VisibleForTesting
194 interface ConfigConstants {
195 /**
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700196 * Key name for the save delay, in milliseconds. (int)
197 */
198 String KEY_SAVE_DELAY_MILLIS = "save_delay_ms";
199
200 /**
Makoto Onuki4362a662016-03-08 18:59:09 -0800201 * Key name for the throttling reset interval, in seconds. (long)
202 */
203 String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
204
205 /**
206 * Key name for the max number of modifying API calls per app for every interval. (int)
207 */
Makoto Onukib6d35232016-04-04 15:57:17 -0700208 String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval";
Makoto Onuki4362a662016-03-08 18:59:09 -0800209
210 /**
211 * Key name for the max icon dimensions in DP, for non-low-memory devices.
212 */
213 String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
214
215 /**
216 * Key name for the max icon dimensions in DP, for low-memory devices.
217 */
218 String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
219
220 /**
221 * Key name for the max dynamic shortcuts per app. (int)
222 */
223 String KEY_MAX_SHORTCUTS = "max_shortcuts";
224
225 /**
Makoto Onuki41066a62016-03-09 16:18:44 -0800226 * Key name for icon compression quality, 0-100.
Makoto Onuki4362a662016-03-08 18:59:09 -0800227 */
228 String KEY_ICON_QUALITY = "icon_quality";
229
230 /**
231 * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
232 */
233 String KEY_ICON_FORMAT = "icon_format";
234 }
235
Makoto Onuki41066a62016-03-09 16:18:44 -0800236 final Context mContext;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800237
238 private final Object mLock = new Object();
239
240 private final Handler mHandler;
241
242 @GuardedBy("mLock")
243 private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
244
245 @GuardedBy("mLock")
246 private long mRawLastResetTime;
247
248 /**
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -0800249 * User ID -> UserShortcuts
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800250 */
251 @GuardedBy("mLock")
Makoto Onuki31459242016-03-22 11:12:18 -0700252 private final SparseArray<ShortcutUser> mUsers = new SparseArray<>();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800253
254 /**
255 * Max number of dynamic shortcuts that each application can have at a time.
256 */
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800257 private int mMaxDynamicShortcuts;
258
259 /**
Makoto Onukib6d35232016-04-04 15:57:17 -0700260 * Max number of updating API calls that each application can make during the interval.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800261 */
Makoto Onukib6d35232016-04-04 15:57:17 -0700262 int mMaxUpdatesPerInterval;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800263
264 /**
265 * Actual throttling-reset interval. By default it's a day.
266 */
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800267 private long mResetInterval;
268
Makoto Onuki55046222016-03-08 10:49:47 -0800269 /**
270 * Icon max width/height in pixels.
271 */
272 private int mMaxIconDimension;
273
Makoto Onuki4362a662016-03-08 18:59:09 -0800274 private CompressFormat mIconPersistFormat;
275 private int mIconPersistQuality;
Makoto Onuki55046222016-03-08 10:49:47 -0800276
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700277 private int mSaveDelayMillis;
278
Makoto Onuki0acbb142016-03-22 17:02:57 -0700279 private final IPackageManager mIPackageManager;
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800280 private final PackageManagerInternal mPackageManagerInternal;
Makoto Onukicdc78f72016-03-21 15:47:52 -0700281 private final UserManager mUserManager;
Makoto Onukiac042502016-05-20 16:39:42 -0700282 private final UsageStatsManagerInternal mUsageStatsManagerInternal;
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800283
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700284 @GuardedBy("mLock")
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700285 final SparseIntArray mUidState = new SparseIntArray();
286
287 @GuardedBy("mLock")
288 final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray();
289
290 @GuardedBy("mLock")
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700291 private List<Integer> mDirtyUserIds = new ArrayList<>();
292
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700293 /**
294 * A counter that increments every time the system locale changes. We keep track of it to reset
295 * throttling counters on the first call from each package after the last locale change.
296 *
297 * We need this mechanism because we can't do much in the locale change callback, which is
298 * {@link ShortcutServiceInternal#onSystemLocaleChangedNoLock()}.
299 */
300 private final AtomicLong mLocaleChangeSequenceNumber = new AtomicLong();
301
Makoto Onukic51b2872016-05-04 15:24:50 -0700302 private final AtomicBoolean mBootCompleted = new AtomicBoolean();
303
Makoto Onuki905e8852016-03-28 10:40:58 -0700304 private static final int PACKAGE_MATCH_FLAGS =
305 PackageManager.MATCH_DIRECT_BOOT_AWARE
306 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
307 | PackageManager.MATCH_UNINSTALLED_PACKAGES;
308
Makoto Onuki2e210c42016-03-30 08:30:36 -0700309 // Stats
310 @VisibleForTesting
311 interface Stats {
312 int GET_DEFAULT_HOME = 0;
313 int GET_PACKAGE_INFO = 1;
314 int GET_PACKAGE_INFO_WITH_SIG = 2;
315 int GET_APPLICATION_INFO = 3;
316 int LAUNCHER_PERMISSION_CHECK = 4;
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700317 int CLEANUP_DANGLING_BITMAPS = 5;
Makoto Onuki22fcc682016-05-17 14:52:19 -0700318 int GET_ACTIVITIES_WITH_METADATA = 6;
319 int GET_INSTALLED_APPLICATIONS = 7;
320 int CHECK_PACKAGE_CHANGES = 8;
Makoto Onuki2e210c42016-03-30 08:30:36 -0700321
Makoto Onuki22fcc682016-05-17 14:52:19 -0700322 int COUNT = CHECK_PACKAGE_CHANGES + 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
345 })
346 @Retention(RetentionPolicy.SOURCE)
347 @interface ShortcutOperation {}
348
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800349 public ShortcutService(Context context) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700350 this(context, BackgroundThread.get().getLooper());
351 }
352
353 @VisibleForTesting
354 ShortcutService(Context context, Looper looper) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800355 mContext = Preconditions.checkNotNull(context);
356 LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700357 mHandler = new Handler(looper);
Makoto Onuki0acbb142016-03-22 17:02:57 -0700358 mIPackageManager = AppGlobals.getPackageManager();
Makoto Onukiac042502016-05-20 16:39:42 -0700359 mPackageManagerInternal = Preconditions.checkNotNull(
360 LocalServices.getService(PackageManagerInternal.class));
361 mUserManager = Preconditions.checkNotNull(context.getSystemService(UserManager.class));
362 mUsageStatsManagerInternal = Preconditions.checkNotNull(
363 LocalServices.getService(UsageStatsManagerInternal.class));
Makoto Onukicdc78f72016-03-21 15:47:52 -0700364
365 mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700366
367 injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
368 | ActivityManager.UID_OBSERVER_GONE);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800369 }
370
Makoto Onuki2e210c42016-03-30 08:30:36 -0700371 void logDurationStat(int statId, long start) {
372 synchronized (mStatLock) {
373 mCountStats[statId]++;
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700374 mDurationStats[statId] += (injectElapsedRealtime() - start);
Makoto Onuki2e210c42016-03-30 08:30:36 -0700375 }
376 }
377
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700378 public long getLocaleChangeSequenceNumber() {
379 return mLocaleChangeSequenceNumber.get();
380 }
381
382 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
383 @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
384 handleOnUidStateChanged(uid, procState);
385 }
386
387 @Override public void onUidGone(int uid) throws RemoteException {
388 handleOnUidStateChanged(uid, ActivityManager.MAX_PROCESS_STATE);
389 }
390
391 @Override public void onUidActive(int uid) throws RemoteException {
392 }
393
394 @Override public void onUidIdle(int uid) throws RemoteException {
395 }
396 };
397
398 void handleOnUidStateChanged(int uid, int procState) {
399 if (DEBUG_PROCSTATE) {
400 Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
401 }
402 synchronized (mLock) {
403 mUidState.put(uid, procState);
404
405 // We need to keep track of last time an app comes to foreground.
406 // See ShortcutPackage.getApiCallCount() for how it's used.
407 // It doesn't have to be persisted, but it needs to be the elapsed time.
408 if (isProcessStateForeground(procState)) {
409 mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
410 }
411 }
412 }
413
414 private boolean isProcessStateForeground(int processState) {
415 return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
416 }
417
418 boolean isUidForegroundLocked(int uid) {
419 if (uid == Process.SYSTEM_UID) {
420 // IUidObserver doesn't report the state of SYSTEM, but it always has bound services,
421 // so it's foreground anyway.
422 return true;
423 }
424 return isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE));
425 }
426
427 long getUidLastForegroundElapsedTimeLocked(int uid) {
428 return mUidLastForegroundElapsedTime.get(uid);
429 }
430
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800431 /**
432 * System service lifecycle.
433 */
434 public static final class Lifecycle extends SystemService {
435 final ShortcutService mService;
436
437 public Lifecycle(Context context) {
438 super(context);
439 mService = new ShortcutService(context);
440 }
441
442 @Override
443 public void onStart() {
444 publishBinderService(Context.SHORTCUT_SERVICE, mService);
445 }
446
447 @Override
448 public void onBootPhase(int phase) {
449 mService.onBootPhase(phase);
450 }
451
452 @Override
453 public void onCleanupUser(int userHandle) {
Makoto Onukicdc78f72016-03-21 15:47:52 -0700454 mService.handleCleanupUser(userHandle);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800455 }
456
457 @Override
Makoto Onukif3a572b2016-03-10 12:28:38 -0800458 public void onUnlockUser(int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -0700459 mService.handleUnlockUser(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800460 }
461 }
462
463 /** lifecycle event */
464 void onBootPhase(int phase) {
465 if (DEBUG) {
466 Slog.d(TAG, "onBootPhase: " + phase);
467 }
468 switch (phase) {
469 case SystemService.PHASE_LOCK_SETTINGS_READY:
470 initialize();
471 break;
Makoto Onukic51b2872016-05-04 15:24:50 -0700472 case SystemService.PHASE_BOOT_COMPLETED:
473 mBootCompleted.set(true);
474 break;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800475 }
476 }
477
478 /** lifecycle event */
Makoto Onukicdc78f72016-03-21 15:47:52 -0700479 void handleUnlockUser(int userId) {
Makoto Onuki22fcc682016-05-17 14:52:19 -0700480 if (DEBUG) {
481 Slog.d(TAG, "handleUnlockUser: user=" + userId);
482 }
Makoto Onukicdc78f72016-03-21 15:47:52 -0700483 synchronized (mLock) {
484 // Preload
485 getUserShortcutsLocked(userId);
Makoto Onuki0acbb142016-03-22 17:02:57 -0700486
Makoto Onuki39686e82016-04-13 18:03:00 -0700487 checkPackageChanges(userId);
Makoto Onukicdc78f72016-03-21 15:47:52 -0700488 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800489 }
490
491 /** lifecycle event */
Makoto Onukicdc78f72016-03-21 15:47:52 -0700492 void handleCleanupUser(int userId) {
493 synchronized (mLock) {
494 unloadUserLocked(userId);
495 }
496 }
497
498 private void unloadUserLocked(int userId) {
499 if (DEBUG) {
500 Slog.d(TAG, "unloadUserLocked: user=" + userId);
501 }
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700502 // Save all dirty information.
503 saveDirtyInfo();
504
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800505 // Unload
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -0800506 mUsers.delete(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800507 }
508
509 /** Return the base state file name */
510 private AtomicFile getBaseStateFile() {
511 final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
512 path.mkdirs();
513 return new AtomicFile(path);
514 }
515
516 /**
517 * Init the instance. (load the state file, etc)
518 */
519 private void initialize() {
520 synchronized (mLock) {
Makoto Onuki4362a662016-03-08 18:59:09 -0800521 loadConfigurationLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800522 loadBaseStateLocked();
523 }
524 }
525
Makoto Onuki4362a662016-03-08 18:59:09 -0800526 /**
527 * Load the configuration from Settings.
528 */
529 private void loadConfigurationLocked() {
530 updateConfigurationLocked(injectShortcutManagerConstants());
531 }
Makoto Onuki55046222016-03-08 10:49:47 -0800532
Makoto Onuki4362a662016-03-08 18:59:09 -0800533 /**
534 * Load the configuration from Settings.
535 */
536 @VisibleForTesting
537 boolean updateConfigurationLocked(String config) {
538 boolean result = true;
539
540 final KeyValueListParser parser = new KeyValueListParser(',');
541 try {
542 parser.setString(config);
543 } catch (IllegalArgumentException e) {
544 // Failed to parse the settings string, log this and move on
545 // with defaults.
546 Slog.e(TAG, "Bad shortcut manager settings", e);
547 result = false;
548 }
549
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700550 mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
551 DEFAULT_SAVE_DELAY_MS));
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700552
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700553 mResetInterval = Math.max(1, parser.getLong(
Makoto Onuki4362a662016-03-08 18:59:09 -0800554 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700555 * 1000L);
Makoto Onuki4362a662016-03-08 18:59:09 -0800556
Makoto Onukib6d35232016-04-04 15:57:17 -0700557 mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong(
558 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL));
Makoto Onuki4362a662016-03-08 18:59:09 -0800559
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700560 mMaxDynamicShortcuts = Math.max(0, (int) parser.getLong(
561 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP));
Makoto Onuki4362a662016-03-08 18:59:09 -0800562
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700563 final int iconDimensionDp = Math.max(1, injectIsLowRamDevice()
Makoto Onuki4362a662016-03-08 18:59:09 -0800564 ? (int) parser.getLong(
565 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
566 DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
567 : (int) parser.getLong(
568 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700569 DEFAULT_MAX_ICON_DIMENSION_DP));
Makoto Onuki4362a662016-03-08 18:59:09 -0800570
571 mMaxIconDimension = injectDipToPixel(iconDimensionDp);
572
573 mIconPersistFormat = CompressFormat.valueOf(
574 parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
575
576 mIconPersistQuality = (int) parser.getLong(
577 ConfigConstants.KEY_ICON_QUALITY,
578 DEFAULT_ICON_PERSIST_QUALITY);
579
580 return result;
581 }
582
583 @VisibleForTesting
584 String injectShortcutManagerConstants() {
585 return android.provider.Settings.Global.getString(
586 mContext.getContentResolver(),
587 android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
588 }
589
590 @VisibleForTesting
591 int injectDipToPixel(int dip) {
592 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
593 mContext.getResources().getDisplayMetrics());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800594 }
595
Makoto Onuki55046222016-03-08 10:49:47 -0800596 // === Persisting ===
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800597
598 @Nullable
Makoto Onuki41066a62016-03-09 16:18:44 -0800599 static String parseStringAttribute(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800600 return parser.getAttributeValue(null, attribute);
601 }
602
Makoto Onuki0acbb142016-03-22 17:02:57 -0700603 static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) {
604 return parseLongAttribute(parser, attribute) == 1;
605 }
606
Makoto Onuki41066a62016-03-09 16:18:44 -0800607 static int parseIntAttribute(XmlPullParser parser, String attribute) {
608 return (int) parseLongAttribute(parser, attribute);
609 }
610
Makoto Onukid99c6f02016-03-28 11:02:54 -0700611 static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
612 return (int) parseLongAttribute(parser, attribute, def);
613 }
614
Makoto Onuki41066a62016-03-09 16:18:44 -0800615 static long parseLongAttribute(XmlPullParser parser, String attribute) {
Makoto Onukid99c6f02016-03-28 11:02:54 -0700616 return parseLongAttribute(parser, attribute, 0);
617 }
618
619 static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800620 final String value = parseStringAttribute(parser, attribute);
621 if (TextUtils.isEmpty(value)) {
Makoto Onukid99c6f02016-03-28 11:02:54 -0700622 return def;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800623 }
624 try {
625 return Long.parseLong(value);
626 } catch (NumberFormatException e) {
627 Slog.e(TAG, "Error parsing long " + value);
Makoto Onukid99c6f02016-03-28 11:02:54 -0700628 return def;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800629 }
630 }
631
632 @Nullable
Makoto Onuki41066a62016-03-09 16:18:44 -0800633 static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800634 final String value = parseStringAttribute(parser, attribute);
635 if (TextUtils.isEmpty(value)) {
636 return null;
637 }
638 return ComponentName.unflattenFromString(value);
639 }
640
641 @Nullable
Makoto Onuki41066a62016-03-09 16:18:44 -0800642 static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800643 final String value = parseStringAttribute(parser, attribute);
644 if (TextUtils.isEmpty(value)) {
645 return null;
646 }
647 try {
648 return Intent.parseUri(value, /* flags =*/ 0);
649 } catch (URISyntaxException e) {
650 Slog.e(TAG, "Error parsing intent", e);
651 return null;
652 }
653 }
654
Makoto Onuki41066a62016-03-09 16:18:44 -0800655 static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800656 if (TextUtils.isEmpty(value)) return;
657
658 out.startTag(null, tag);
659 out.attribute(null, ATTR_VALUE, value);
660 out.endTag(null, tag);
661 }
662
Makoto Onuki41066a62016-03-09 16:18:44 -0800663 static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800664 writeTagValue(out, tag, Long.toString(value));
665 }
666
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800667 static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
668 if (name == null) return;
669 writeTagValue(out, tag, name.flattenToString());
670 }
671
Makoto Onuki41066a62016-03-09 16:18:44 -0800672 static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800673 throws IOException, XmlPullParserException {
674 if (bundle == null) return;
675
676 out.startTag(null, tag);
677 bundle.saveToXml(out);
678 out.endTag(null, tag);
679 }
680
Makoto Onuki22fcc682016-05-17 14:52:19 -0700681 static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800682 if (TextUtils.isEmpty(value)) return;
683
Makoto Onuki22fcc682016-05-17 14:52:19 -0700684 out.attribute(null, name, value.toString());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800685 }
686
Makoto Onuki41066a62016-03-09 16:18:44 -0800687 static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800688 writeAttr(out, name, String.valueOf(value));
689 }
690
Makoto Onuki0acbb142016-03-22 17:02:57 -0700691 static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
692 if (value) {
693 writeAttr(out, name, "1");
694 }
695 }
696
Makoto Onuki41066a62016-03-09 16:18:44 -0800697 static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800698 if (comp == null) return;
699 writeAttr(out, name, comp.flattenToString());
700 }
701
Makoto Onuki41066a62016-03-09 16:18:44 -0800702 static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800703 if (intent == null) return;
704
705 writeAttr(out, name, intent.toUri(/* flags =*/ 0));
706 }
707
708 @VisibleForTesting
709 void saveBaseStateLocked() {
710 final AtomicFile file = getBaseStateFile();
711 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700712 Slog.d(TAG, "Saving to " + file.getBaseFile());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800713 }
714
715 FileOutputStream outs = null;
716 try {
717 outs = file.startWrite();
718
719 // Write to XML
720 XmlSerializer out = new FastXmlSerializer();
721 out.setOutput(outs, StandardCharsets.UTF_8.name());
722 out.startDocument(null, true);
723 out.startTag(null, TAG_ROOT);
724
725 // Body.
726 writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700727 writeTagValue(out, TAG_LOCALE_CHANGE_SEQUENCE_NUMBER,
728 mLocaleChangeSequenceNumber.get());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800729
730 // Epilogue.
731 out.endTag(null, TAG_ROOT);
732 out.endDocument();
733
734 // Close.
735 file.finishWrite(outs);
736 } catch (IOException e) {
737 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
738 file.failWrite(outs);
739 }
740 }
741
742 private void loadBaseStateLocked() {
743 mRawLastResetTime = 0;
744
745 final AtomicFile file = getBaseStateFile();
746 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700747 Slog.d(TAG, "Loading from " + file.getBaseFile());
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800748 }
749 try (FileInputStream in = file.openRead()) {
750 XmlPullParser parser = Xml.newPullParser();
751 parser.setInput(in, StandardCharsets.UTF_8.name());
752
753 int type;
754 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
755 if (type != XmlPullParser.START_TAG) {
756 continue;
757 }
758 final int depth = parser.getDepth();
759 // Check the root tag
760 final String tag = parser.getName();
761 if (depth == 1) {
762 if (!TAG_ROOT.equals(tag)) {
763 Slog.e(TAG, "Invalid root tag: " + tag);
764 return;
765 }
766 continue;
767 }
768 // Assume depth == 2
769 switch (tag) {
770 case TAG_LAST_RESET_TIME:
771 mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
772 break;
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700773 case TAG_LOCALE_CHANGE_SEQUENCE_NUMBER:
774 mLocaleChangeSequenceNumber.set(parseLongAttribute(parser, ATTR_VALUE));
775 break;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800776 default:
777 Slog.e(TAG, "Invalid tag: " + tag);
778 break;
779 }
780 }
781 } catch (FileNotFoundException e) {
782 // Use the default
783 } catch (IOException|XmlPullParserException e) {
784 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
785
786 mRawLastResetTime = 0;
787 }
788 // Adjust the last reset time.
789 getLastResetTimeLocked();
790 }
791
792 private void saveUserLocked(@UserIdInt int userId) {
793 final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
794 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700795 Slog.d(TAG, "Saving to " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800796 }
797 path.mkdirs();
798 final AtomicFile file = new AtomicFile(path);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700799 FileOutputStream os = null;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800800 try {
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700801 os = file.startWrite();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800802
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700803 saveUserInternalLocked(userId, os, /* forBackup= */ false);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800804
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700805 file.finishWrite(os);
806 } catch (XmlPullParserException|IOException e) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800807 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700808 file.failWrite(os);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800809 }
810 }
811
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700812 private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
813 boolean forBackup) throws IOException, XmlPullParserException {
814
815 final BufferedOutputStream bos = new BufferedOutputStream(os);
816
817 // Write to XML
818 XmlSerializer out = new FastXmlSerializer();
819 out.setOutput(bos, StandardCharsets.UTF_8.name());
820 out.startDocument(null, true);
821
Makoto Onukic51b2872016-05-04 15:24:50 -0700822 getUserShortcutsLocked(userId).saveToXml(out, forBackup);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700823
824 out.endDocument();
825
826 bos.flush();
827 os.flush();
828 }
829
Makoto Onuki41066a62016-03-09 16:18:44 -0800830 static IOException throwForInvalidTag(int depth, String tag) throws IOException {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800831 throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
832 }
833
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700834 static void warnForInvalidTag(int depth, String tag) throws IOException {
835 Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
836 }
837
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800838 @Nullable
Makoto Onuki31459242016-03-22 11:12:18 -0700839 private ShortcutUser loadUserLocked(@UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800840 final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
841 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700842 Slog.d(TAG, "Loading from " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800843 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800844 final AtomicFile file = new AtomicFile(path);
845
846 final FileInputStream in;
847 try {
848 in = file.openRead();
849 } catch (FileNotFoundException e) {
850 if (DEBUG) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700851 Slog.d(TAG, "Not found " + path);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800852 }
853 return null;
854 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800855 try {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -0700856 final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false);
857 cleanupDanglingBitmapDirectoriesLocked(userId, ret);
858 return ret;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800859 } catch (IOException|XmlPullParserException e) {
860 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
861 return null;
862 } finally {
863 IoUtils.closeQuietly(in);
864 }
865 }
866
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700867 private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
868 boolean fromBackup) throws XmlPullParserException, IOException {
869
870 final BufferedInputStream bis = new BufferedInputStream(is);
871
872 ShortcutUser ret = null;
873 XmlPullParser parser = Xml.newPullParser();
874 parser.setInput(bis, StandardCharsets.UTF_8.name());
875
876 int type;
877 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
878 if (type != XmlPullParser.START_TAG) {
879 continue;
880 }
881 final int depth = parser.getDepth();
882
883 final String tag = parser.getName();
884 if (DEBUG_LOAD) {
885 Slog.d(TAG, String.format("depth=%d type=%d name=%s",
886 depth, type, tag));
887 }
888 if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
889 ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup);
890 continue;
891 }
892 throwForInvalidTag(depth, tag);
893 }
894 return ret;
895 }
896
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800897 private void scheduleSaveBaseState() {
Makoto Onuki0acbb142016-03-22 17:02:57 -0700898 scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800899 }
900
Makoto Onuki2d5b4652016-03-11 16:09:54 -0800901 void scheduleSaveUser(@UserIdInt int userId) {
Makoto Onuki0acbb142016-03-22 17:02:57 -0700902 scheduleSaveInner(userId);
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700903 }
904
905 // In order to re-schedule, we need to reuse the same instance, so keep it in final.
906 private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo;
907
Makoto Onuki0acbb142016-03-22 17:02:57 -0700908 private void scheduleSaveInner(@UserIdInt int userId) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700909 if (DEBUG) {
910 Slog.d(TAG, "Scheduling to save for " + userId);
911 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800912 synchronized (mLock) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700913 if (!mDirtyUserIds.contains(userId)) {
914 mDirtyUserIds.add(userId);
915 }
916 }
917 // If already scheduled, remove that and re-schedule in N seconds.
918 mHandler.removeCallbacks(mSaveDirtyInfoRunner);
919 mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis);
920 }
921
922 @VisibleForTesting
923 void saveDirtyInfo() {
924 if (DEBUG) {
925 Slog.d(TAG, "saveDirtyInfo");
926 }
927 synchronized (mLock) {
928 for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
929 final int userId = mDirtyUserIds.get(i);
930 if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
931 saveBaseStateLocked();
932 } else {
933 saveUserLocked(userId);
934 }
935 }
936 mDirtyUserIds.clear();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800937 }
938 }
939
940 /** Return the last reset time. */
941 long getLastResetTimeLocked() {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700942 updateTimesLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800943 return mRawLastResetTime;
944 }
945
946 /** Return the next reset time. */
947 long getNextResetTimeLocked() {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700948 updateTimesLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800949 return mRawLastResetTime + mResetInterval;
950 }
951
Makoto Onuki4554d0e2016-03-14 15:51:41 -0700952 static boolean isClockValid(long time) {
953 return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT
954 }
955
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800956 /**
957 * Update the last reset time.
958 */
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700959 private void updateTimesLocked() {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800960
961 final long now = injectCurrentTimeMillis();
962
963 final long prevLastResetTime = mRawLastResetTime;
964
965 if (mRawLastResetTime == 0) { // first launch.
966 // TODO Randomize??
967 mRawLastResetTime = now;
968 } else if (now < mRawLastResetTime) {
969 // Clock rewound.
Makoto Onuki4554d0e2016-03-14 15:51:41 -0700970 if (isClockValid(now)) {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700971 Slog.w(TAG, "Clock rewound");
Makoto Onuki4554d0e2016-03-14 15:51:41 -0700972 // TODO Randomize??
973 mRawLastResetTime = now;
974 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800975 } else {
Makoto Onukiaa8b94a2016-03-17 13:14:05 -0700976 if ((mRawLastResetTime + mResetInterval) <= now) {
977 final long offset = mRawLastResetTime % mResetInterval;
978 mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800979 }
980 }
981 if (prevLastResetTime != mRawLastResetTime) {
982 scheduleSaveBaseState();
983 }
984 }
985
Makoto Onukicdc78f72016-03-21 15:47:52 -0700986 @GuardedBy("mLock")
987 @NonNull
Makoto Onuki2e210c42016-03-30 08:30:36 -0700988 private boolean isUserLoadedLocked(@UserIdInt int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -0700989 return mUsers.get(userId) != null;
990 }
991
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800992 /** Return the per-user state. */
993 @GuardedBy("mLock")
994 @NonNull
Makoto Onuki31459242016-03-22 11:12:18 -0700995 ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
996 ShortcutUser userPackages = mUsers.get(userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800997 if (userPackages == null) {
998 userPackages = loadUserLocked(userId);
999 if (userPackages == null) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001000 userPackages = new ShortcutUser(this, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001001 }
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08001002 mUsers.put(userId, userPackages);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001003 }
1004 return userPackages;
1005 }
1006
Makoto Onuki2e210c42016-03-30 08:30:36 -07001007 void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
1008 for (int i = mUsers.size() - 1; i >= 0; i--) {
1009 c.accept(mUsers.valueAt(i));
1010 }
1011 }
1012
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001013 /** Return the per-user per-package state. */
1014 @GuardedBy("mLock")
1015 @NonNull
Makoto Onuki31459242016-03-22 11:12:18 -07001016 ShortcutPackage getPackageShortcutsLocked(
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001017 @NonNull String packageName, @UserIdInt int userId) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001018 return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
Makoto Onukide667372016-03-15 14:29:20 -07001019 }
1020
1021 @GuardedBy("mLock")
1022 @NonNull
Makoto Onuki2e210c42016-03-30 08:30:36 -07001023 ShortcutLauncher getLauncherShortcutsLocked(
1024 @NonNull String packageName, @UserIdInt int ownerUserId,
1025 @UserIdInt int launcherUserId) {
1026 return getUserShortcutsLocked(ownerUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07001027 .getLauncherShortcuts(packageName, launcherUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001028 }
1029
1030 // === Caller validation ===
1031
Makoto Onuki55046222016-03-08 10:49:47 -08001032 void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) {
1033 if (shortcut.getBitmapPath() != null) {
1034 if (DEBUG) {
1035 Slog.d(TAG, "Removing " + shortcut.getBitmapPath());
1036 }
1037 new File(shortcut.getBitmapPath()).delete();
1038
1039 shortcut.setBitmapPath(null);
1040 shortcut.setIconResourceId(0);
1041 shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES);
1042 }
1043 }
1044
Makoto Onuki0033b2a2016-04-14 17:19:16 -07001045 public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) {
1046 final File packagePath = new File(getUserBitmapFilePath(userId), packageName);
1047 if (!packagePath.isDirectory()) {
1048 return;
1049 }
1050 if (!(FileUtils.deleteContents(packagePath) && packagePath.delete())) {
1051 Slog.w(TAG, "Unable to remove directory " + packagePath);
1052 }
1053 }
1054
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001055 private void cleanupDanglingBitmapDirectoriesLocked(
1056 @UserIdInt int userId, @NonNull ShortcutUser user) {
1057 if (DEBUG) {
1058 Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
1059 }
1060 final long start = injectElapsedRealtime();
1061
1062 final File bitmapDir = getUserBitmapFilePath(userId);
1063 final File[] children = bitmapDir.listFiles();
1064 if (children == null) {
1065 return;
1066 }
1067 for (File child : children) {
1068 if (!child.isDirectory()) {
1069 continue;
1070 }
1071 final String packageName = child.getName();
1072 if (DEBUG) {
1073 Slog.d(TAG, "cleanupDanglingBitmaps: Found directory=" + packageName);
1074 }
1075 if (!user.hasPackage(packageName)) {
1076 if (DEBUG) {
1077 Slog.d(TAG, "Removing dangling bitmap directory: " + packageName);
1078 }
1079 cleanupBitmapsForPackage(userId, packageName);
1080 } else {
1081 cleanupDanglingBitmapFilesLocked(userId, user, packageName, child);
1082 }
1083 }
1084 logDurationStat(Stats.CLEANUP_DANGLING_BITMAPS, start);
1085 }
1086
1087 private void cleanupDanglingBitmapFilesLocked(@UserIdInt int userId, @NonNull ShortcutUser user,
1088 @NonNull String packageName, @NonNull File path) {
1089 final ArraySet<String> usedFiles =
Makoto Onukic51b2872016-05-04 15:24:50 -07001090 user.getPackageShortcuts(packageName).getUsedBitmapFiles();
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001091
1092 for (File child : path.listFiles()) {
1093 if (!child.isFile()) {
1094 continue;
1095 }
1096 final String name = child.getName();
1097 if (!usedFiles.contains(name)) {
1098 if (DEBUG) {
1099 Slog.d(TAG, "Removing dangling bitmap file: " + child.getAbsolutePath());
1100 }
1101 child.delete();
1102 }
1103 }
1104 }
1105
Makoto Onuki55046222016-03-08 10:49:47 -08001106 @VisibleForTesting
1107 static class FileOutputStreamWithPath extends FileOutputStream {
1108 private final File mFile;
1109
1110 public FileOutputStreamWithPath(File file) throws FileNotFoundException {
1111 super(file);
1112 mFile = file;
1113 }
1114
1115 public File getFile() {
1116 return mFile;
1117 }
1118 }
1119
1120 /**
1121 * Build the cached bitmap filename for a shortcut icon.
1122 *
1123 * The filename will be based on the ID, except certain characters will be escaped.
1124 */
1125 @VisibleForTesting
1126 FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
1127 throws IOException {
1128 final File packagePath = new File(getUserBitmapFilePath(userId),
Makoto Onuki22fcc682016-05-17 14:52:19 -07001129 shortcut.getPackage());
Makoto Onuki55046222016-03-08 10:49:47 -08001130 if (!packagePath.isDirectory()) {
1131 packagePath.mkdirs();
1132 if (!packagePath.isDirectory()) {
1133 throw new IOException("Unable to create directory " + packagePath);
1134 }
1135 SELinux.restorecon(packagePath);
1136 }
1137
1138 final String baseName = String.valueOf(injectCurrentTimeMillis());
1139 for (int suffix = 0;; suffix++) {
1140 final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
1141 final File file = new File(packagePath, filename);
1142 if (!file.exists()) {
1143 if (DEBUG) {
1144 Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
1145 }
1146 return new FileOutputStreamWithPath(file);
1147 }
1148 }
1149 }
1150
1151 void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) {
1152 if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
1153 return;
1154 }
1155
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001156 final long token = injectClearCallingIdentity();
Makoto Onuki55046222016-03-08 10:49:47 -08001157 try {
1158 // Clear icon info on the shortcut.
1159 shortcut.setIconResourceId(0);
1160 shortcut.setBitmapPath(null);
1161
1162 final Icon icon = shortcut.getIcon();
1163 if (icon == null) {
1164 return; // has no icon
1165 }
1166
Makoto Onukiabe84422016-04-07 09:41:19 -07001167 Bitmap bitmap;
Makoto Onuki55046222016-03-08 10:49:47 -08001168 try {
1169 switch (icon.getType()) {
1170 case Icon.TYPE_RESOURCE: {
1171 injectValidateIconResPackage(shortcut, icon);
1172
1173 shortcut.setIconResourceId(icon.getResId());
1174 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
1175 return;
1176 }
1177 case Icon.TYPE_BITMAP: {
Makoto Onukiabe84422016-04-07 09:41:19 -07001178 bitmap = icon.getBitmap(); // Don't recycle in this case.
Makoto Onuki55046222016-03-08 10:49:47 -08001179 break;
1180 }
Makoto Onuki55046222016-03-08 10:49:47 -08001181 default:
1182 // This shouldn't happen because we've already validated the icon, but
1183 // just in case.
1184 throw ShortcutInfo.getInvalidIconException();
1185 }
1186 if (bitmap == null) {
1187 Slog.e(TAG, "Null bitmap detected");
1188 return;
1189 }
1190 // Shrink and write to the file.
1191 File path = null;
1192 try {
1193 final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut);
1194 try {
1195 path = out.getFile();
1196
Makoto Onukiabe84422016-04-07 09:41:19 -07001197 Bitmap shrunk = shrinkBitmap(bitmap, mMaxIconDimension);
1198 try {
1199 shrunk.compress(mIconPersistFormat, mIconPersistQuality, out);
1200 } finally {
1201 if (bitmap != shrunk) {
1202 shrunk.recycle();
1203 }
1204 }
Makoto Onuki55046222016-03-08 10:49:47 -08001205
1206 shortcut.setBitmapPath(out.getFile().getAbsolutePath());
1207 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
1208 } finally {
1209 IoUtils.closeQuietly(out);
1210 }
1211 } catch (IOException|RuntimeException e) {
1212 // STOPSHIP Change wtf to e
1213 Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
1214 if (path != null && path.exists()) {
1215 path.delete();
1216 }
1217 }
1218 } finally {
Makoto Onuki55046222016-03-08 10:49:47 -08001219 // Once saved, we won't use the original icon information, so null it out.
1220 shortcut.clearIcon();
1221 }
1222 } finally {
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001223 injectRestoreCallingIdentity(token);
Makoto Onuki55046222016-03-08 10:49:47 -08001224 }
1225 }
1226
1227 // Unfortunately we can't do this check in unit tests because we fake creator package names,
1228 // so override in unit tests.
1229 // TODO CTS this case.
1230 void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001231 if (!shortcut.getPackage().equals(icon.getResPackage())) {
Makoto Onuki55046222016-03-08 10:49:47 -08001232 throw new IllegalArgumentException(
1233 "Icon resource must reside in shortcut owner package");
1234 }
1235 }
1236
1237 @VisibleForTesting
1238 static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
1239 // Original width/height.
1240 final int ow = in.getWidth();
1241 final int oh = in.getHeight();
1242 if ((ow <= maxSize) && (oh <= maxSize)) {
1243 if (DEBUG) {
1244 Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
1245 }
1246 return in;
1247 }
1248 final int longerDimension = Math.max(ow, oh);
1249
1250 // New width and height.
1251 final int nw = ow * maxSize / longerDimension;
1252 final int nh = oh * maxSize / longerDimension;
1253 if (DEBUG) {
1254 Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
1255 ow, oh, nw, nh));
1256 }
1257
1258 final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
1259 final Canvas c = new Canvas(scaledBitmap);
1260
1261 final RectF dst = new RectF(0, 0, nw, nh);
1262
1263 c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
1264
Makoto Onuki55046222016-03-08 10:49:47 -08001265 return scaledBitmap;
1266 }
1267
1268 // === Caller validation ===
1269
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001270 private boolean isCallerSystem() {
1271 final int callingUid = injectBinderCallingUid();
1272 return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
1273 }
1274
1275 private boolean isCallerShell() {
1276 final int callingUid = injectBinderCallingUid();
1277 return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
1278 }
1279
1280 private void enforceSystemOrShell() {
1281 Preconditions.checkState(isCallerSystem() || isCallerShell(),
1282 "Caller must be system or shell");
1283 }
1284
1285 private void enforceShell() {
1286 Preconditions.checkState(isCallerShell(), "Caller must be shell");
1287 }
1288
Makoto Onuki9da23fc2016-03-29 11:14:42 -07001289 private void enforceSystem() {
1290 Preconditions.checkState(isCallerSystem(), "Caller must be system");
1291 }
1292
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001293 private void enforceResetThrottlingPermission() {
1294 if (isCallerSystem()) {
1295 return;
1296 }
1297 injectEnforceCallingPermission(
1298 android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null);
1299 }
1300
1301 /**
1302 * Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse
1303 * mockito. So instead we extracted it here and override it in the tests.
1304 */
1305 @VisibleForTesting
1306 void injectEnforceCallingPermission(
1307 @NonNull String permission, @Nullable String message) {
1308 mContext.enforceCallingPermission(permission, message);
1309 }
1310
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001311 private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
1312 Preconditions.checkStringNotEmpty(packageName, "packageName");
1313
1314 if (isCallerSystem()) {
1315 return; // no check
1316 }
1317
1318 final int callingUid = injectBinderCallingUid();
1319
1320 // Otherwise, make sure the arguments are valid.
1321 if (UserHandle.getUserId(callingUid) != userId) {
1322 throw new SecurityException("Invalid user-ID");
1323 }
Makoto Onuki55046222016-03-08 10:49:47 -08001324 if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001325 return; // Caller is valid.
1326 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001327 throw new SecurityException("Calling package name mismatch");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001328 }
1329
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001330 void postToHandler(Runnable r) {
1331 mHandler.post(r);
1332 }
1333
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001334 /**
Makoto Onuki7001a612016-05-27 13:24:28 -07001335 * @throws IllegalArgumentException if {@code numShortcuts} is bigger than
1336 * {@link #getMaxActivityShortcuts()}.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001337 */
Makoto Onuki7001a612016-05-27 13:24:28 -07001338 void enforceMaxActivityShortcuts(int numShortcuts) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001339 if (numShortcuts > mMaxDynamicShortcuts) {
1340 throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
1341 }
1342 }
1343
1344 /**
Makoto Onuki7001a612016-05-27 13:24:28 -07001345 * Return the max number of dynamic + manifest shortcuts for each launcher icon.
1346 */
1347 int getMaxActivityShortcuts() {
1348 return mMaxDynamicShortcuts;
1349 }
1350
1351 /**
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001352 * - Sends a notification to LauncherApps
1353 * - Write to file
1354 */
Makoto Onuki39686e82016-04-13 18:03:00 -07001355 void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) {
1356 if (DEBUG) {
1357 Slog.d(TAG, String.format(
1358 "Shortcut changes: package=%s, user=%d", packageName, userId));
1359 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001360 notifyListeners(packageName, userId);
1361 scheduleSaveUser(userId);
1362 }
1363
1364 private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
Makoto Onuki85694522016-05-04 12:53:37 -07001365 final long token = injectClearCallingIdentity();
1366 try {
1367 if (!mUserManager.isUserRunning(userId)) {
1368 return;
1369 }
1370 } finally {
1371 injectRestoreCallingIdentity(token);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001372 }
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07001373 postToHandler(() -> {
1374 final ArrayList<ShortcutChangeListener> copy;
1375 synchronized (mLock) {
1376 copy = new ArrayList<>(mListeners);
1377 }
1378 // Note onShortcutChanged() needs to be called with the system service permissions.
1379 for (int i = copy.size() - 1; i >= 0; i--) {
1380 copy.get(i).onShortcutChanged(packageName, userId);
1381 }
1382 });
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001383 }
1384
1385 /**
1386 * Clean up / validate an incoming shortcut.
1387 * - Make sure all mandatory fields are set.
1388 * - Make sure the intent's extras are persistable, and them to set
1389 * {@link ShortcutInfo#mIntentPersistableExtras}. Also clear its extras.
1390 * - Clear flags.
Makoto Onuki55046222016-03-08 10:49:47 -08001391 *
1392 * TODO Detailed unit tests
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001393 */
Makoto Onuki55046222016-03-08 10:49:47 -08001394 private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001395 Preconditions.checkNotNull(shortcut, "Null shortcut detected");
Makoto Onuki22fcc682016-05-17 14:52:19 -07001396 if (shortcut.getActivity() != null) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001397 Preconditions.checkState(
Makoto Onuki22fcc682016-05-17 14:52:19 -07001398 shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001399 "Activity package name mismatch");
1400 }
1401
Makoto Onuki55046222016-03-08 10:49:47 -08001402 if (!forUpdate) {
1403 shortcut.enforceMandatoryFields();
1404 }
1405 if (shortcut.getIcon() != null) {
1406 ShortcutInfo.validateIcon(shortcut.getIcon());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001407 }
1408
Makoto Onuki55046222016-03-08 10:49:47 -08001409 validateForXml(shortcut.getId());
1410 validateForXml(shortcut.getTitle());
1411 validatePersistableBundleForXml(shortcut.getIntentPersistableExtras());
1412 validatePersistableBundleForXml(shortcut.getExtras());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001413
Makoto Onukide667372016-03-15 14:29:20 -07001414 shortcut.replaceFlags(0);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001415 }
1416
Makoto Onuki55046222016-03-08 10:49:47 -08001417 // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those
1418 // characters.
1419
1420 private static void validatePersistableBundleForXml(PersistableBundle b) {
1421 if (b == null || b.size() == 0) {
1422 return;
1423 }
1424 for (String key : b.keySet()) {
1425 validateForXml(key);
1426 final Object value = b.get(key);
1427 if (value == null) {
1428 continue;
1429 } else if (value instanceof String) {
1430 validateForXml((String) value);
1431 } else if (value instanceof String[]) {
1432 for (String v : (String[]) value) {
1433 validateForXml(v);
1434 }
1435 } else if (value instanceof PersistableBundle) {
1436 validatePersistableBundleForXml((PersistableBundle) value);
1437 }
1438 }
1439 }
1440
Makoto Onuki22fcc682016-05-17 14:52:19 -07001441 private static void validateForXml(CharSequence s) {
Makoto Onuki55046222016-03-08 10:49:47 -08001442 if (TextUtils.isEmpty(s)) {
1443 return;
1444 }
1445 for (int i = s.length() - 1; i >= 0; i--) {
1446 if (!isAllowedInXml(s.charAt(i))) {
1447 throw new IllegalArgumentException("Unsupported character detected in: " + s);
1448 }
1449 }
1450 }
1451
1452 private static boolean isAllowedInXml(char c) {
1453 return (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
1454 }
1455
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001456 // === APIs ===
1457
1458 @Override
1459 public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
1460 @UserIdInt int userId) {
1461 verifyCaller(packageName, userId);
1462
1463 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
1464 final int size = newShortcuts.size();
1465
1466 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07001467 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001468
Makoto Onuki22fcc682016-05-17 14:52:19 -07001469 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1470
Makoto Onuki7001a612016-05-27 13:24:28 -07001471 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
1472
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001473 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001474 if (!ps.tryApiCall()) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001475 return false;
1476 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001477
1478 // Validate the shortcuts.
1479 for (int i = 0; i < size; i++) {
Makoto Onuki55046222016-03-08 10:49:47 -08001480 fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001481 }
1482
1483 // First, remove all un-pinned; dynamic shortcuts
Makoto Onukic51b2872016-05-04 15:24:50 -07001484 ps.deleteAllDynamicShortcuts();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001485
1486 // Then, add/update all. We need to make sure to take over "pinned" flag.
1487 for (int i = 0; i < size; i++) {
1488 final ShortcutInfo newShortcut = newShortcuts.get(i);
Makoto Onuki22fcc682016-05-17 14:52:19 -07001489 ps.addOrUpdateDynamicShortcut(newShortcut);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001490 }
1491 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001492 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001493
1494 verifyStates();
1495
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001496 return true;
1497 }
1498
1499 @Override
1500 public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
1501 @UserIdInt int userId) {
1502 verifyCaller(packageName, userId);
1503
1504 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
Makoto Onuki55046222016-03-08 10:49:47 -08001505 final int size = newShortcuts.size();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001506
1507 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07001508 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001509
Makoto Onuki22fcc682016-05-17 14:52:19 -07001510 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1511
Makoto Onuki7001a612016-05-27 13:24:28 -07001512 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
1513
Makoto Onuki55046222016-03-08 10:49:47 -08001514 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001515 if (!ps.tryApiCall()) {
Makoto Onuki55046222016-03-08 10:49:47 -08001516 return false;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001517 }
1518
Makoto Onuki55046222016-03-08 10:49:47 -08001519 for (int i = 0; i < size; i++) {
1520 final ShortcutInfo source = newShortcuts.get(i);
1521 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
1522
1523 final ShortcutInfo target = ps.findShortcutById(source.getId());
1524 if (target != null) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001525 if (target.isEnabled() != source.isEnabled()) {
1526 Slog.w(TAG,
1527 "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
1528 }
1529
Makoto Onuki55046222016-03-08 10:49:47 -08001530 final boolean replacingIcon = (source.getIcon() != null);
1531 if (replacingIcon) {
1532 removeIcon(userId, target);
1533 }
1534
Makoto Onuki22fcc682016-05-17 14:52:19 -07001535 if (source.getActivity() != null &&
1536 !source.getActivity().equals(target.getActivity())) {
1537 // TODO When activity is changing, check the dynamic count.
1538 }
1539
1540 // Note copyNonNullFieldsFrom() does the "udpatable with?" check too.
Makoto Onuki55046222016-03-08 10:49:47 -08001541 target.copyNonNullFieldsFrom(source);
1542
1543 if (replacingIcon) {
1544 saveIconAndFixUpShortcut(userId, target);
1545 }
1546 }
1547 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001548 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001549 packageShortcutsChanged(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001550
Makoto Onuki7001a612016-05-27 13:24:28 -07001551 verifyStates();
1552
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001553 return true;
1554 }
1555
1556 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001557 public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001558 @UserIdInt int userId) {
1559 verifyCaller(packageName, userId);
1560
Makoto Onukib6d35232016-04-04 15:57:17 -07001561 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
1562 final int size = newShortcuts.size();
1563
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001564 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07001565 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001566
Makoto Onuki22fcc682016-05-17 14:52:19 -07001567 ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
1568
Makoto Onuki7001a612016-05-27 13:24:28 -07001569 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
1570
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001571 // Throttling.
Makoto Onukic51b2872016-05-04 15:24:50 -07001572 if (!ps.tryApiCall()) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001573 return false;
1574 }
Makoto Onukib6d35232016-04-04 15:57:17 -07001575 for (int i = 0; i < size; i++) {
1576 final ShortcutInfo newShortcut = newShortcuts.get(i);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001577
Makoto Onukib6d35232016-04-04 15:57:17 -07001578 // Validate the shortcut.
1579 fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001580
Makoto Onukib6d35232016-04-04 15:57:17 -07001581 // Add it.
Makoto Onuki22fcc682016-05-17 14:52:19 -07001582 ps.addOrUpdateDynamicShortcut(newShortcut);
Makoto Onukib6d35232016-04-04 15:57:17 -07001583 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001584 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001585 packageShortcutsChanged(packageName, userId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001586
Makoto Onuki7001a612016-05-27 13:24:28 -07001587 verifyStates();
1588
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001589 return true;
1590 }
1591
1592 @Override
Makoto Onuki20c95f82016-05-11 16:51:01 -07001593 public void disableShortcuts(String packageName, List shortcutIds,
1594 String disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
1595 verifyCaller(packageName, userId);
1596 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
1597
1598 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001599 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
1600
1601 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1602
1603 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
1604 ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)),
1605 disabledMessage, disabledMessageResId,
1606 /* overrideImmutable=*/ false);
1607 }
1608 }
1609 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001610
1611 verifyStates();
Makoto Onuki22fcc682016-05-17 14:52:19 -07001612 }
1613
1614 @Override
1615 public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
1616 verifyCaller(packageName, userId);
1617 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
1618
1619 synchronized (mLock) {
1620 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
1621
1622 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1623
1624 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
1625 ps.enableWithId((String) shortcutIds.get(i));
1626 }
Makoto Onuki20c95f82016-05-11 16:51:01 -07001627 }
1628 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001629
1630 verifyStates();
Makoto Onuki20c95f82016-05-11 16:51:01 -07001631 }
1632
1633 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001634 public void removeDynamicShortcuts(String packageName, List shortcutIds,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001635 @UserIdInt int userId) {
1636 verifyCaller(packageName, userId);
Makoto Onukib6d35232016-04-04 15:57:17 -07001637 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001638
1639 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001640 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
1641
1642 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
1643
Makoto Onukib6d35232016-04-04 15:57:17 -07001644 for (int i = shortcutIds.size() - 1; i >= 0; i--) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001645 ps.deleteDynamicWithId(
Makoto Onukib6d35232016-04-04 15:57:17 -07001646 Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
1647 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001648 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001649 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001650
1651 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001652 }
1653
1654 @Override
Makoto Onukib6d35232016-04-04 15:57:17 -07001655 public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001656 verifyCaller(packageName, userId);
1657
1658 synchronized (mLock) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001659 getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001660 }
Makoto Onuki39686e82016-04-13 18:03:00 -07001661 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07001662
1663 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001664 }
1665
1666 @Override
1667 public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
1668 @UserIdInt int userId) {
1669 verifyCaller(packageName, userId);
1670 synchronized (mLock) {
1671 return getShortcutsWithQueryLocked(
1672 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
1673 ShortcutInfo::isDynamic);
1674 }
1675 }
1676
1677 @Override
Makoto Onuki22fcc682016-05-17 14:52:19 -07001678 public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
1679 @UserIdInt int userId) {
1680 verifyCaller(packageName, userId);
1681 synchronized (mLock) {
1682 return getShortcutsWithQueryLocked(
1683 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
1684 ShortcutInfo::isManifestShortcut);
1685 }
1686 }
1687
1688 @Override
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001689 public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
1690 @UserIdInt int userId) {
1691 verifyCaller(packageName, userId);
1692 synchronized (mLock) {
1693 return getShortcutsWithQueryLocked(
1694 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
1695 ShortcutInfo::isPinned);
1696 }
1697 }
1698
1699 private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
1700 @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
1701
1702 final ArrayList<ShortcutInfo> ret = new ArrayList<>();
1703
Makoto Onukic51b2872016-05-04 15:24:50 -07001704 getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001705
1706 return new ParceledListSlice<>(ret);
1707 }
1708
1709 @Override
1710 public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
1711 throws RemoteException {
1712 verifyCaller(packageName, userId);
1713
1714 return mMaxDynamicShortcuts;
1715 }
1716
1717 @Override
1718 public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
1719 verifyCaller(packageName, userId);
1720
1721 synchronized (mLock) {
Makoto Onukib6d35232016-04-04 15:57:17 -07001722 return mMaxUpdatesPerInterval
Makoto Onukic51b2872016-05-04 15:24:50 -07001723 - getPackageShortcutsLocked(packageName, userId).getApiCallCount();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001724 }
1725 }
1726
1727 @Override
1728 public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
1729 verifyCaller(packageName, userId);
1730
1731 synchronized (mLock) {
1732 return getNextResetTimeLocked();
1733 }
1734 }
1735
Makoto Onuki55046222016-03-08 10:49:47 -08001736 @Override
Makoto Onuki20c95f82016-05-11 16:51:01 -07001737 public int getIconMaxDimensions(String packageName, int userId) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001738 verifyCaller(packageName, userId);
1739
Makoto Onuki55046222016-03-08 10:49:47 -08001740 synchronized (mLock) {
1741 return mMaxIconDimension;
1742 }
1743 }
1744
Makoto Onuki20c95f82016-05-11 16:51:01 -07001745 @Override
1746 public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
1747 verifyCaller(packageName, userId);
1748
Makoto Onukiac042502016-05-20 16:39:42 -07001749 Preconditions.checkNotNull(shortcutId);
1750
1751 if (DEBUG) {
1752 Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
1753 shortcutId, packageName, userId));
1754 }
1755
1756 synchronized (mLock) {
1757 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
1758 if (ps.findShortcutById(shortcutId) == null) {
1759 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s",
1760 packageName, shortcutId));
1761 return;
1762 }
1763 }
1764
1765 final long token = injectClearCallingIdentity();
1766 try {
1767 mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
1768 } finally {
1769 injectRestoreCallingIdentity(token);
1770 }
Makoto Onuki20c95f82016-05-11 16:51:01 -07001771 }
1772
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001773 /**
1774 * Reset all throttling, for developer options and command line. Only system/shell can call it.
1775 */
1776 @Override
1777 public void resetThrottling() {
1778 enforceSystemOrShell();
1779
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001780 resetThrottlingInner(getCallingUserId());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001781 }
1782
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001783 void resetThrottlingInner(@UserIdInt int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001784 synchronized (mLock) {
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001785 getUserShortcutsLocked(userId).resetThrottling();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001786 }
Makoto Onuki4554d0e2016-03-14 15:51:41 -07001787 scheduleSaveUser(userId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001788 Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
1789 }
1790
1791 void resetAllThrottlingInner() {
1792 synchronized (mLock) {
1793 mRawLastResetTime = injectCurrentTimeMillis();
1794 }
1795 scheduleSaveBaseState();
1796 Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001797 }
1798
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001799 void resetPackageThrottling(String packageName, int userId) {
1800 synchronized (mLock) {
1801 getPackageShortcutsLocked(packageName, userId)
1802 .resetRateLimitingForCommandLineNoSaving();
1803 saveUserLocked(userId);
1804 }
1805 }
1806
1807 @Override
1808 public void onApplicationActive(String packageName, int userId) {
1809 if (DEBUG) {
1810 Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
1811 }
1812 enforceResetThrottlingPermission();
1813 resetPackageThrottling(packageName, userId);
1814 }
1815
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001816 // We override this method in unit tests to do a simpler check.
1817 boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
1818 return hasShortcutHostPermissionInner(callingPackage, userId);
1819 }
1820
1821 // This method is extracted so we can directly call this method from unit tests,
1822 // even when hasShortcutPermission() is overridden.
1823 @VisibleForTesting
1824 boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
1825 synchronized (mLock) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001826 final long start = injectElapsedRealtime();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001827
Makoto Onuki31459242016-03-22 11:12:18 -07001828 final ShortcutUser user = getUserShortcutsLocked(userId);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001829
1830 final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
1831
1832 // Default launcher from package manager.
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07001833 final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001834 final ComponentName defaultLauncher = injectPackageManagerInternal()
1835 .getHomeActivitiesAsUser(allHomeCandidates, userId);
Makoto Onuki2e210c42016-03-30 08:30:36 -07001836 logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001837
1838 ComponentName detected;
1839 if (defaultLauncher != null) {
1840 detected = defaultLauncher;
1841 if (DEBUG) {
1842 Slog.v(TAG, "Default launcher from PM: " + detected);
1843 }
1844 } else {
Makoto Onukic51b2872016-05-04 15:24:50 -07001845 detected = user.getDefaultLauncherComponent();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001846
1847 // TODO: Make sure it's still enabled.
1848 if (DEBUG) {
1849 Slog.v(TAG, "Cached launcher: " + detected);
1850 }
1851 }
1852
1853 if (detected == null) {
1854 // If we reach here, that means it's the first check since the user was created,
1855 // and there's already multiple launchers and there's no default set.
1856 // Find the system one with the highest priority.
1857 // (We need to check the priority too because of FallbackHome in Settings.)
1858 // If there's no system launcher yet, then no one can access shortcuts, until
1859 // the user explicitly
1860 final int size = allHomeCandidates.size();
1861
1862 int lastPriority = Integer.MIN_VALUE;
1863 for (int i = 0; i < size; i++) {
1864 final ResolveInfo ri = allHomeCandidates.get(i);
1865 if (!ri.activityInfo.applicationInfo.isSystemApp()) {
1866 continue;
1867 }
1868 if (DEBUG) {
1869 Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
1870 ri.activityInfo.getComponentName(), ri.priority));
1871 }
1872 if (ri.priority < lastPriority) {
1873 continue;
1874 }
1875 detected = ri.activityInfo.getComponentName();
1876 lastPriority = ri.priority;
1877 }
1878 }
Makoto Onuki2e210c42016-03-30 08:30:36 -07001879 logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
1880
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001881 if (detected != null) {
1882 if (DEBUG) {
1883 Slog.v(TAG, "Detected launcher: " + detected);
1884 }
Makoto Onukic51b2872016-05-04 15:24:50 -07001885 user.setDefaultLauncherComponent(detected);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08001886 return detected.getPackageName().equals(callingPackage);
1887 } else {
1888 // Default launcher not found.
1889 return false;
1890 }
1891 }
1892 }
1893
Makoto Onukicdc78f72016-03-21 15:47:52 -07001894 // === House keeping ===
1895
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001896 private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId) {
1897 synchronized (mLock) {
1898 forEachLoadedUserLocked(user ->
1899 cleanUpPackageLocked(packageName, user.getUserId(), packageUserId));
1900 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001901 }
1902
Makoto Onuki2e210c42016-03-30 08:30:36 -07001903 /**
1904 * Remove all the information associated with a package. This will really remove all the
1905 * information, including the restore information (i.e. it'll remove packages even if they're
1906 * shadow).
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001907 *
1908 * This is called when an app is uninstalled, or an app gets "clear data"ed.
Makoto Onuki2e210c42016-03-30 08:30:36 -07001909 */
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001910 @VisibleForTesting
1911 void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07001912 final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001913
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001914 final ShortcutUser user = getUserShortcutsLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001915 boolean doNotify = false;
1916
1917 // First, remove the package from the package list (if the package is a publisher).
Makoto Onukid99c6f02016-03-28 11:02:54 -07001918 if (packageUserId == owningUserId) {
Makoto Onukic51b2872016-05-04 15:24:50 -07001919 if (user.removePackage(packageName) != null) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07001920 doNotify = true;
1921 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07001922 }
Makoto Onukid99c6f02016-03-28 11:02:54 -07001923
Makoto Onukicdc78f72016-03-21 15:47:52 -07001924 // Also remove from the launcher list (if the package is a launcher).
Makoto Onuki9ac59d02016-04-26 11:23:14 -07001925 user.removeLauncher(packageUserId, packageName);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001926
1927 // Then remove pinned shortcuts from all launchers.
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001928 user.forAllLaunchers(l -> l.cleanUpPackage(packageName, packageUserId));
1929
1930 // Now there may be orphan shortcuts because we removed pinned shortcuts at the previous
Makoto Onukicdc78f72016-03-21 15:47:52 -07001931 // step. Remove them too.
Makoto Onukic51b2872016-05-04 15:24:50 -07001932 user.forAllPackages(p -> p.refreshPinnedFlags());
Makoto Onukicdc78f72016-03-21 15:47:52 -07001933
Makoto Onukid99c6f02016-03-28 11:02:54 -07001934 scheduleSaveUser(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001935
1936 if (doNotify) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07001937 notifyListeners(packageName, owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001938 }
1939
1940 if (!wasUserLoaded) {
1941 // Note this will execute the scheduled save.
Makoto Onukid99c6f02016-03-28 11:02:54 -07001942 unloadUserLocked(owningUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07001943 }
1944 }
1945
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001946 /**
1947 * Entry point from {@link LauncherApps}.
1948 */
1949 private class LocalService extends ShortcutServiceInternal {
Makoto Onuki2e210c42016-03-30 08:30:36 -07001950
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001951 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07001952 public List<ShortcutInfo> getShortcuts(int launcherUserId,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001953 @NonNull String callingPackage, long changedSince,
Makoto Onukiabe84422016-04-07 09:41:19 -07001954 @Nullable String packageName, @Nullable List<String> shortcutIds,
1955 @Nullable ComponentName componentName,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001956 int queryFlags, int userId) {
1957 final ArrayList<ShortcutInfo> ret = new ArrayList<>();
Makoto Onuki20c95f82016-05-11 16:51:01 -07001958 final boolean cloneKeyFieldOnly =
1959 ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
1960 final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO
1961 : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
Makoto Onukiabe84422016-04-07 09:41:19 -07001962 if (packageName == null) {
1963 shortcutIds = null; // LauncherAppsService already threw for it though.
1964 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001965
1966 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07001967 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07001968 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07001969
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001970 if (packageName != null) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07001971 getShortcutsInnerLocked(launcherUserId,
Makoto Onukiabe84422016-04-07 09:41:19 -07001972 callingPackage, packageName, shortcutIds, changedSince,
Makoto Onukide667372016-03-15 14:29:20 -07001973 componentName, queryFlags, userId, ret, cloneFlag);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001974 } else {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001975 final List<String> shortcutIdsF = shortcutIds;
1976 getUserShortcutsLocked(userId).forAllPackages(p -> {
Makoto Onukid99c6f02016-03-28 11:02:54 -07001977 getShortcutsInnerLocked(launcherUserId,
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001978 callingPackage, p.getPackageName(), shortcutIdsF, changedSince,
Makoto Onukide667372016-03-15 14:29:20 -07001979 componentName, queryFlags, userId, ret, cloneFlag);
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001980 });
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001981 }
1982 }
Makoto Onuki20c95f82016-05-11 16:51:01 -07001983 // Resolve all strings if needed.
1984 if (!cloneKeyFieldOnly) {
1985 final long token = injectClearCallingIdentity();
1986 try {
1987 for (int i = ret.size() - 1; i >= 0; i--) {
1988 try {
1989 ret.get(i).resolveStringsRequiringCrossUser(mContext);
1990 } catch (NameNotFoundException e) {
1991 continue;
1992 }
1993 }
1994 } finally {
1995 injectRestoreCallingIdentity(token);
1996 }
1997 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001998 return ret;
1999 }
2000
Makoto Onukid99c6f02016-03-28 11:02:54 -07002001 private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
Makoto Onukiabe84422016-04-07 09:41:19 -07002002 @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002003 @Nullable ComponentName componentName, int queryFlags,
2004 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
Makoto Onukiabe84422016-04-07 09:41:19 -07002005 final ArraySet<String> ids = shortcutIds == null ? null
2006 : new ArraySet<>(shortcutIds);
2007
Makoto Onukic51b2872016-05-04 15:24:50 -07002008 final ShortcutPackage p = getUserShortcutsLocked(userId)
2009 .getPackageShortcutsIfExists(packageName);
2010 if (p == null) {
2011 return; // No need to instantiate ShortcutPackage.
2012 }
2013
2014 p.findAll(ret,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002015 (ShortcutInfo si) -> {
2016 if (si.getLastChangedTimestamp() < changedSince) {
2017 return false;
2018 }
Makoto Onukiabe84422016-04-07 09:41:19 -07002019 if (ids != null && !ids.contains(si.getId())) {
2020 return false;
2021 }
Makoto Onuki85694522016-05-04 12:53:37 -07002022 if (componentName != null) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002023 if (si.getActivity() != null
2024 && !si.getActivity().equals(componentName)) {
Makoto Onuki85694522016-05-04 12:53:37 -07002025 return false;
2026 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002027 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002028 if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
2029 && si.isDynamic()) {
2030 return true;
2031 }
2032 if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
2033 && si.isPinned()) {
2034 return true;
2035 }
2036 if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0)
2037 && si.isManifestShortcut()) {
2038 return true;
2039 }
2040 return false;
Makoto Onukid99c6f02016-03-28 11:02:54 -07002041 }, cloneFlag, callingPackage, launcherUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002042 }
2043
2044 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002045 public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
2046 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2047 Preconditions.checkStringNotEmpty(packageName, "packageName");
2048 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
2049
2050 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002051 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002052 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002053
Makoto Onukid99c6f02016-03-28 11:02:54 -07002054 final ShortcutInfo si = getShortcutInfoLocked(
2055 launcherUserId, callingPackage, packageName, shortcutId, userId);
2056 return si != null && si.isPinned();
2057 }
2058 }
2059
Makoto Onuki2e210c42016-03-30 08:30:36 -07002060 private ShortcutInfo getShortcutInfoLocked(
Makoto Onukid99c6f02016-03-28 11:02:54 -07002061 int launcherUserId, @NonNull String callingPackage,
2062 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2063 Preconditions.checkStringNotEmpty(packageName, "packageName");
2064 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
2065
Makoto Onukic51b2872016-05-04 15:24:50 -07002066 final ShortcutPackage p = getUserShortcutsLocked(userId)
2067 .getPackageShortcutsIfExists(packageName);
2068 if (p == null) {
2069 return null;
2070 }
2071
Makoto Onukid99c6f02016-03-28 11:02:54 -07002072 final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
Makoto Onukic51b2872016-05-04 15:24:50 -07002073 p.findAll(list,
Makoto Onukid99c6f02016-03-28 11:02:54 -07002074 (ShortcutInfo si) -> shortcutId.equals(si.getId()),
2075 /* clone flags=*/ 0, callingPackage, launcherUserId);
2076 return list.size() == 0 ? null : list.get(0);
2077 }
2078
2079 @Override
2080 public void pinShortcuts(int launcherUserId,
2081 @NonNull String callingPackage, @NonNull String packageName,
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002082 @NonNull List<String> shortcutIds, int userId) {
2083 // Calling permission must be checked by LauncherAppsImpl.
2084 Preconditions.checkStringNotEmpty(packageName, "packageName");
2085 Preconditions.checkNotNull(shortcutIds, "shortcutIds");
2086
2087 synchronized (mLock) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002088 final ShortcutLauncher launcher =
Makoto Onuki2e210c42016-03-30 08:30:36 -07002089 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
Makoto Onukic51b2872016-05-04 15:24:50 -07002090 launcher.attemptToRestoreIfNeededAndSave();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002091
Makoto Onukic51b2872016-05-04 15:24:50 -07002092 launcher.pinShortcuts(userId, packageName, shortcutIds);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002093 }
Makoto Onuki39686e82016-04-13 18:03:00 -07002094 packageShortcutsChanged(packageName, userId);
Makoto Onuki7001a612016-05-27 13:24:28 -07002095
2096 verifyStates();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002097 }
2098
2099 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002100 public Intent createShortcutIntent(int launcherUserId,
2101 @NonNull String callingPackage,
Makoto Onuki43204b82016-03-08 16:16:44 -08002102 @NonNull String packageName, @NonNull String shortcutId, int userId) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002103 // Calling permission must be checked by LauncherAppsImpl.
Makoto Onuki43204b82016-03-08 16:16:44 -08002104 Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
2105 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002106
2107 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002108 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002109 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002110
Makoto Onukid99c6f02016-03-28 11:02:54 -07002111 // Make sure the shortcut is actually visible to the launcher.
2112 final ShortcutInfo si = getShortcutInfoLocked(
2113 launcherUserId, callingPackage, packageName, shortcutId, userId);
2114 // "si == null" should suffice here, but check the flags too just to make sure.
Makoto Onuki22fcc682016-05-17 14:52:19 -07002115 if (si == null || !si.isEnabled() || !si.isAlive()) {
Makoto Onukid99c6f02016-03-28 11:02:54 -07002116 return null;
2117 }
2118 return si.getIntent();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002119 }
2120 }
2121
2122 @Override
2123 public void addListener(@NonNull ShortcutChangeListener listener) {
2124 synchronized (mLock) {
2125 mListeners.add(Preconditions.checkNotNull(listener));
2126 }
2127 }
Makoto Onuki55046222016-03-08 10:49:47 -08002128
2129 @Override
Makoto Onukiabe84422016-04-07 09:41:19 -07002130 public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
2131 @NonNull String packageName, @NonNull String shortcutId, int userId) {
2132 Preconditions.checkNotNull(callingPackage, "callingPackage");
2133 Preconditions.checkNotNull(packageName, "packageName");
2134 Preconditions.checkNotNull(shortcutId, "shortcutId");
Makoto Onuki55046222016-03-08 10:49:47 -08002135
2136 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002137 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002138 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002139
Makoto Onukic51b2872016-05-04 15:24:50 -07002140 final ShortcutPackage p = getUserShortcutsLocked(userId)
2141 .getPackageShortcutsIfExists(packageName);
2142 if (p == null) {
2143 return 0;
2144 }
2145
2146 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
Makoto Onuki55046222016-03-08 10:49:47 -08002147 return (shortcutInfo != null && shortcutInfo.hasIconResource())
2148 ? shortcutInfo.getIconResourceId() : 0;
2149 }
2150 }
2151
2152 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002153 public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
Makoto Onukiabe84422016-04-07 09:41:19 -07002154 @NonNull String callingPackage, @NonNull String packageName,
2155 @NonNull String shortcutId, int userId) {
2156 Preconditions.checkNotNull(callingPackage, "callingPackage");
2157 Preconditions.checkNotNull(packageName, "packageName");
2158 Preconditions.checkNotNull(shortcutId, "shortcutId");
Makoto Onuki55046222016-03-08 10:49:47 -08002159
2160 synchronized (mLock) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002161 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Makoto Onukic51b2872016-05-04 15:24:50 -07002162 .attemptToRestoreIfNeededAndSave();
Makoto Onuki2e210c42016-03-30 08:30:36 -07002163
Makoto Onukic51b2872016-05-04 15:24:50 -07002164 final ShortcutPackage p = getUserShortcutsLocked(userId)
2165 .getPackageShortcutsIfExists(packageName);
2166 if (p == null) {
2167 return null;
2168 }
2169
2170 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
Makoto Onuki55046222016-03-08 10:49:47 -08002171 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
2172 return null;
2173 }
2174 try {
Makoto Onuki34d1c912016-03-10 14:24:58 -08002175 if (shortcutInfo.getBitmapPath() == null) {
2176 Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
2177 return null;
2178 }
Makoto Onuki55046222016-03-08 10:49:47 -08002179 return ParcelFileDescriptor.open(
2180 new File(shortcutInfo.getBitmapPath()),
2181 ParcelFileDescriptor.MODE_READ_ONLY);
2182 } catch (FileNotFoundException e) {
2183 Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath());
2184 return null;
2185 }
2186 }
2187 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002188
2189 @Override
Makoto Onukid99c6f02016-03-28 11:02:54 -07002190 public boolean hasShortcutHostPermission(int launcherUserId,
2191 @NonNull String callingPackage) {
2192 return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002193 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002194
2195 /**
2196 * Called by AM when the system locale changes *within the AM lock. ABSOLUTELY do not take
2197 * any locks in this method.
2198 */
2199 @Override
2200 public void onSystemLocaleChangedNoLock() {
2201 // DO NOT HOLD ANY LOCKS HERE.
2202
2203 // We want to reset throttling for all packages for all users. But we can't just do so
2204 // here because:
2205 // - We can't load/save users that are locked.
2206 // - Even for loaded users, resetting the counters would require us to hold mLock.
2207 //
2208 // So we use a "pull" model instead. In here, we just increment the "locale change
2209 // sequence number". Each ShortcutUser has the "last known locale change sequence".
2210 //
2211 // This allows ShortcutUser's to detect the system locale change, so they can reset
2212 // counters.
2213
Makoto Onukic51b2872016-05-04 15:24:50 -07002214 // Ignore all callback during system boot.
2215 if (mBootCompleted.get()) {
2216 mLocaleChangeSequenceNumber.incrementAndGet();
2217 if (DEBUG) {
2218 Slog.d(TAG, "onSystemLocaleChangedNoLock: " + mLocaleChangeSequenceNumber.get());
2219 }
2220 postToHandler(() -> scheduleSaveBaseState());
2221 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002222 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002223 }
2224
Makoto Onuki0acbb142016-03-22 17:02:57 -07002225 /**
2226 * Package event callbacks.
2227 */
2228 @VisibleForTesting
2229 final PackageMonitor mPackageMonitor = new PackageMonitor() {
2230 @Override
2231 public void onPackageAdded(String packageName, int uid) {
2232 handlePackageAdded(packageName, getChangingUserId());
2233 }
2234
Makoto Onukicdc78f72016-03-21 15:47:52 -07002235 @Override
2236 public void onPackageUpdateFinished(String packageName, int uid) {
2237 handlePackageUpdateFinished(packageName, getChangingUserId());
2238 }
2239
2240 @Override
2241 public void onPackageRemoved(String packageName, int uid) {
2242 handlePackageRemoved(packageName, getChangingUserId());
2243 }
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002244
2245 @Override
2246 public void onPackageDataCleared(String packageName, int uid) {
2247 handlePackageDataCleared(packageName, getChangingUserId());
2248 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07002249 };
2250
Makoto Onuki0acbb142016-03-22 17:02:57 -07002251 /**
Makoto Onuki39686e82016-04-13 18:03:00 -07002252 * Called when a user is unlocked.
2253 * - Check all known packages still exist, and otherwise perform cleanup.
2254 * - If a package still exists, check the version code. If it's been updated, may need to
2255 * update timestamps of its shortcuts.
Makoto Onuki0acbb142016-03-22 17:02:57 -07002256 */
Makoto Onukid99c6f02016-03-28 11:02:54 -07002257 @VisibleForTesting
Makoto Onuki39686e82016-04-13 18:03:00 -07002258 void checkPackageChanges(@UserIdInt int ownerUserId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -07002259 if (DEBUG) {
Makoto Onuki39686e82016-04-13 18:03:00 -07002260 Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002261 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002262
Makoto Onuki22fcc682016-05-17 14:52:19 -07002263 final long start = injectElapsedRealtime();
2264 try {
2265 final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002266
Makoto Onuki22fcc682016-05-17 14:52:19 -07002267 synchronized (mLock) {
2268 final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
2269
2270 // Find packages that have been uninstalled.
2271 user.forAllPackageItems(spi -> {
2272 if (spi.getPackageInfo().isShadow()) {
2273 return; // Don't delete shadow information.
2274 }
2275 if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
2276 gonePackages.add(PackageWithUser.of(spi));
2277 }
2278 });
2279 if (gonePackages.size() > 0) {
2280 for (int i = gonePackages.size() - 1; i >= 0; i--) {
2281 final PackageWithUser pu = gonePackages.get(i);
2282 cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId);
2283 }
Makoto Onukid99c6f02016-03-28 11:02:54 -07002284 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002285
2286 // Then for each installed app, publish manifest shortcuts when needed.
2287 forInstalledApplications(ownerUserId, ai -> {
2288 user.handlePackageAddedOrUpdated(ai.packageName);
2289 });
Makoto Onuki0acbb142016-03-22 17:02:57 -07002290 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002291 } finally {
2292 logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002293 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07002294 }
2295
Makoto Onuki0acbb142016-03-22 17:02:57 -07002296 private void handlePackageAdded(String packageName, @UserIdInt int userId) {
Makoto Onukicdc78f72016-03-21 15:47:52 -07002297 if (DEBUG) {
Makoto Onuki0acbb142016-03-22 17:02:57 -07002298 Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
2299 }
2300 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002301 final ShortcutUser user = getUserShortcutsLocked(userId);
2302 user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
2303 user.handlePackageAddedOrUpdated(packageName);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002304 }
2305 }
2306
2307 private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
Makoto Onuki905e8852016-03-28 10:40:58 -07002308 if (DEBUG) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002309 Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
2310 packageName, userId));
Makoto Onuki0acbb142016-03-22 17:02:57 -07002311 }
2312 synchronized (mLock) {
Makoto Onuki22fcc682016-05-17 14:52:19 -07002313 final ShortcutUser user = getUserShortcutsLocked(userId);
2314 user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
Makoto Onuki39686e82016-04-13 18:03:00 -07002315
Makoto Onuki22fcc682016-05-17 14:52:19 -07002316 if (isPackageInstalled(packageName, userId)) {
2317 user.handlePackageAddedOrUpdated(packageName);
Makoto Onuki39686e82016-04-13 18:03:00 -07002318 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002319 }
2320 }
2321
Makoto Onuki2e210c42016-03-30 08:30:36 -07002322 private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
Makoto Onuki0acbb142016-03-22 17:02:57 -07002323 if (DEBUG) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002324 Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
2325 packageUserId));
Makoto Onukicdc78f72016-03-21 15:47:52 -07002326 }
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002327 cleanUpPackageForAllLoadedUsers(packageName, packageUserId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002328 }
2329
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002330 private void handlePackageDataCleared(String packageName, int packageUserId) {
2331 if (DEBUG) {
2332 Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
2333 packageUserId));
Makoto Onukicdc78f72016-03-21 15:47:52 -07002334 }
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002335 cleanUpPackageForAllLoadedUsers(packageName, packageUserId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002336 }
2337
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002338 // === PackageManager interaction ===
Makoto Onuki0acbb142016-03-22 17:02:57 -07002339
Makoto Onuki22fcc682016-05-17 14:52:19 -07002340 @Nullable
Makoto Onuki905e8852016-03-28 10:40:58 -07002341 PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
2342 return injectPackageInfo(packageName, userId, true);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002343 }
2344
Makoto Onuki22fcc682016-05-17 14:52:19 -07002345 @Nullable
2346 PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) {
2347 return injectPackageInfo(packageName, userId, false);
2348 }
2349
Makoto Onuki905e8852016-03-28 10:40:58 -07002350 int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002351 final long token = injectClearCallingIdentity();
Makoto Onuki905e8852016-03-28 10:40:58 -07002352 try {
2353 return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS
2354 , userId);
2355 } catch (RemoteException e) {
2356 // Shouldn't happen.
2357 Slog.wtf(TAG, "RemoteException", e);
2358 return -1;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002359 } finally {
2360 injectRestoreCallingIdentity(token);
Makoto Onuki905e8852016-03-28 10:40:58 -07002361 }
Makoto Onuki0acbb142016-03-22 17:02:57 -07002362 }
2363
Makoto Onuki22fcc682016-05-17 14:52:19 -07002364 @Nullable
Makoto Onuki0acbb142016-03-22 17:02:57 -07002365 @VisibleForTesting
2366 PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
2367 boolean getSignatures) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07002368 final long start = injectElapsedRealtime();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002369 final long token = injectClearCallingIdentity();
Makoto Onuki0acbb142016-03-22 17:02:57 -07002370 try {
Makoto Onuki905e8852016-03-28 10:40:58 -07002371 return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS
Makoto Onuki0acbb142016-03-22 17:02:57 -07002372 | (getSignatures ? PackageManager.GET_SIGNATURES : 0)
2373 , userId);
2374 } catch (RemoteException e) {
2375 // Shouldn't happen.
2376 Slog.wtf(TAG, "RemoteException", e);
2377 return null;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002378 } finally {
2379 injectRestoreCallingIdentity(token);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002380
2381 logDurationStat(
2382 (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO),
2383 start);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002384 }
2385 }
2386
Makoto Onuki22fcc682016-05-17 14:52:19 -07002387 @Nullable
Makoto Onuki905e8852016-03-28 10:40:58 -07002388 @VisibleForTesting
2389 ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07002390 final long start = injectElapsedRealtime();
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002391 final long token = injectClearCallingIdentity();
Makoto Onuki905e8852016-03-28 10:40:58 -07002392 try {
2393 return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
2394 } catch (RemoteException e) {
2395 // Shouldn't happen.
2396 Slog.wtf(TAG, "RemoteException", e);
2397 return null;
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002398 } finally {
2399 injectRestoreCallingIdentity(token);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002400
2401 logDurationStat(Stats.GET_APPLICATION_INFO, start);
Makoto Onuki905e8852016-03-28 10:40:58 -07002402 }
2403 }
2404
Makoto Onuki22fcc682016-05-17 14:52:19 -07002405 @Nullable
2406 @VisibleForTesting
2407 PackageInfo injectGetActivitiesWithMetadata(String packageName, @UserIdInt int userId) {
2408 final long start = injectElapsedRealtime();
2409 final long token = injectClearCallingIdentity();
2410 try {
2411 return mIPackageManager.getPackageInfo(packageName,
2412 PACKAGE_MATCH_FLAGS | PackageManager.GET_ACTIVITIES
2413 | PackageManager.GET_META_DATA, userId);
2414 } catch (RemoteException e) {
2415 // Shouldn't happen.
2416 Slog.wtf(TAG, "RemoteException", e);
2417 return null;
2418 } finally {
2419 injectRestoreCallingIdentity(token);
2420
2421 logDurationStat(Stats.GET_ACTIVITIES_WITH_METADATA, start);
2422 }
2423 }
2424
2425 @Nullable
2426 @VisibleForTesting
2427 List<ApplicationInfo> injectInstalledApplications(@UserIdInt int userId) {
2428 final long start = injectElapsedRealtime();
2429 final long token = injectClearCallingIdentity();
2430 try {
2431 final ParceledListSlice<ApplicationInfo> parceledList =
2432 mIPackageManager.getInstalledApplications(PACKAGE_MATCH_FLAGS, userId);
2433 if (parceledList == null) {
2434 return Collections.emptyList();
2435 }
2436 return parceledList.getList();
2437 } catch (RemoteException e) {
2438 // Shouldn't happen.
2439 Slog.wtf(TAG, "RemoteException", e);
2440 return null;
2441 } finally {
2442 injectRestoreCallingIdentity(token);
2443
2444 logDurationStat(Stats.GET_INSTALLED_APPLICATIONS, start);
2445 }
2446 }
2447
2448 private void forInstalledApplications(@UserIdInt int userId,
2449 Consumer<ApplicationInfo> callback) {
2450 final List<ApplicationInfo> list = injectInstalledApplications(userId);
2451 for (int i = list.size() - 1; i >= 0; i--) {
2452 final ApplicationInfo ai = list.get(i);
2453
2454 if ((ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
2455 callback.accept(ai);
2456 }
2457 }
2458 }
2459
Makoto Onuki905e8852016-03-28 10:40:58 -07002460 private boolean isApplicationFlagSet(String packageName, int userId, int flags) {
2461 final ApplicationInfo ai = injectApplicationInfo(packageName, userId);
2462 return (ai != null) && ((ai.flags & flags) == flags);
2463 }
2464
Makoto Onuki2e210c42016-03-30 08:30:36 -07002465 boolean isPackageInstalled(String packageName, int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002466 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
2467 }
2468
Makoto Onuki22fcc682016-05-17 14:52:19 -07002469 @Nullable
2470 XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
2471// TODO Doesn't seem like ACROSS_USER is needed, but double check.
2472 return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
Makoto Onuki39686e82016-04-13 18:03:00 -07002473 }
2474
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002475 // === Backup & restore ===
2476
Makoto Onuki0acbb142016-03-22 17:02:57 -07002477 boolean shouldBackupApp(String packageName, int userId) {
Makoto Onuki905e8852016-03-28 10:40:58 -07002478 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
Makoto Onuki0acbb142016-03-22 17:02:57 -07002479 }
2480
Makoto Onuki2e210c42016-03-30 08:30:36 -07002481 boolean shouldBackupApp(PackageInfo pi) {
2482 return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
2483 }
2484
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002485 @Override
Makoto Onuki2e210c42016-03-30 08:30:36 -07002486 public byte[] getBackupPayload(@UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002487 enforceSystem();
2488 if (DEBUG) {
2489 Slog.d(TAG, "Backing up user " + userId);
2490 }
2491 synchronized (mLock) {
2492 final ShortcutUser user = getUserShortcutsLocked(userId);
2493 if (user == null) {
2494 Slog.w(TAG, "Can't backup: user not found: id=" + userId);
2495 return null;
2496 }
2497
Makoto Onukic51b2872016-05-04 15:24:50 -07002498 user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave());
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002499
2500 // Then save.
2501 final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024);
2502 try {
2503 saveUserInternalLocked(userId, os, /* forBackup */ true);
2504 } catch (XmlPullParserException|IOException e) {
2505 // Shouldn't happen.
2506 Slog.w(TAG, "Backup failed.", e);
2507 return null;
2508 }
2509 return os.toByteArray();
2510 }
2511 }
2512
2513 @Override
Makoto Onuki2e210c42016-03-30 08:30:36 -07002514 public void applyRestore(byte[] payload, @UserIdInt int userId) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002515 enforceSystem();
2516 if (DEBUG) {
2517 Slog.d(TAG, "Restoring user " + userId);
2518 }
2519 final ShortcutUser user;
2520 final ByteArrayInputStream is = new ByteArrayInputStream(payload);
2521 try {
2522 user = loadUserInternal(userId, is, /* fromBackup */ true);
2523 } catch (XmlPullParserException|IOException e) {
2524 Slog.w(TAG, "Restoration failed.", e);
2525 return;
2526 }
2527 synchronized (mLock) {
2528 mUsers.put(userId, user);
Makoto Onuki2e210c42016-03-30 08:30:36 -07002529
2530 // Then purge all the save images.
2531 final File bitmapPath = getUserBitmapFilePath(userId);
2532 final boolean success = FileUtils.deleteContents(bitmapPath);
2533 if (!success) {
2534 Slog.w(TAG, "Failed to delete " + bitmapPath);
2535 }
2536
2537 saveUserLocked(userId);
Makoto Onuki9da23fc2016-03-29 11:14:42 -07002538 }
Makoto Onukicdc78f72016-03-21 15:47:52 -07002539 }
2540
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002541 // === Dump ===
2542
2543 @Override
2544 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2545 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2546 != PackageManager.PERMISSION_GRANTED) {
2547 pw.println("Permission Denial: can't dump UserManager from from pid="
2548 + Binder.getCallingPid()
2549 + ", uid=" + Binder.getCallingUid()
2550 + " without permission "
2551 + android.Manifest.permission.DUMP);
2552 return;
2553 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002554 dumpInner(pw, args);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002555 }
2556
2557 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002558 void dumpInner(PrintWriter pw, String[] args) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002559 synchronized (mLock) {
2560 final long now = injectCurrentTimeMillis();
2561 pw.print("Now: [");
2562 pw.print(now);
2563 pw.print("] ");
2564 pw.print(formatTime(now));
Makoto Onuki55046222016-03-08 10:49:47 -08002565
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002566 pw.print(" Raw last reset: [");
2567 pw.print(mRawLastResetTime);
2568 pw.print("] ");
2569 pw.print(formatTime(mRawLastResetTime));
2570
2571 final long last = getLastResetTimeLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002572 pw.print(" Last reset: [");
2573 pw.print(last);
2574 pw.print("] ");
2575 pw.print(formatTime(last));
2576
Makoto Onuki55046222016-03-08 10:49:47 -08002577 final long next = getNextResetTimeLocked();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002578 pw.print(" Next reset: [");
2579 pw.print(next);
2580 pw.print("] ");
2581 pw.print(formatTime(next));
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002582
2583 pw.print(" Locale change seq#: ");
2584 pw.print(mLocaleChangeSequenceNumber.get());
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002585 pw.println();
2586
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002587 pw.print(" Config:");
2588 pw.print(" Max icon dim: ");
2589 pw.println(mMaxIconDimension);
2590 pw.print(" Icon format: ");
2591 pw.println(mIconPersistFormat);
2592 pw.print(" Icon quality: ");
Makoto Onuki2e210c42016-03-30 08:30:36 -07002593 pw.println(mIconPersistQuality);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07002594 pw.print(" saveDelayMillis: ");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002595 pw.println(mSaveDelayMillis);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07002596 pw.print(" resetInterval: ");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002597 pw.println(mResetInterval);
Makoto Onuki0033b2a2016-04-14 17:19:16 -07002598 pw.print(" maxUpdatesPerInterval: ");
Makoto Onukib6d35232016-04-04 15:57:17 -07002599 pw.println(mMaxUpdatesPerInterval);
Makoto Onuki39686e82016-04-13 18:03:00 -07002600 pw.print(" maxDynamicShortcuts: ");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002601 pw.println(mMaxDynamicShortcuts);
Makoto Onuki55046222016-03-08 10:49:47 -08002602 pw.println();
2603
Makoto Onuki2e210c42016-03-30 08:30:36 -07002604 pw.println(" Stats:");
2605 synchronized (mStatLock) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002606 final String p = " ";
Makoto Onuki2e210c42016-03-30 08:30:36 -07002607 dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()");
2608 dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check");
2609
2610 dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()");
2611 dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)");
2612 dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
Makoto Onuki6c1dbd52016-05-02 15:19:32 -07002613 dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002614 dumpStatLS(pw, p, Stats.GET_ACTIVITIES_WITH_METADATA, "getActivities+metadata");
2615 dumpStatLS(pw, p, Stats.GET_INSTALLED_APPLICATIONS, "getInstalledApplications");
2616 dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges");
Makoto Onuki2e210c42016-03-30 08:30:36 -07002617 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002618
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08002619 for (int i = 0; i < mUsers.size(); i++) {
2620 pw.println();
Makoto Onukic51b2872016-05-04 15:24:50 -07002621 mUsers.valueAt(i).dump(pw, " ");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002622 }
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002623
2624 pw.println();
2625 pw.println(" UID state:");
2626
2627 for (int i = 0; i < mUidState.size(); i++) {
2628 final int uid = mUidState.keyAt(i);
2629 final int state = mUidState.valueAt(i);
2630 pw.print(" UID=");
2631 pw.print(uid);
2632 pw.print(" state=");
2633 pw.print(state);
2634 if (isProcessStateForeground(state)) {
2635 pw.print(" [FG]");
2636 }
2637 pw.print(" last FG=");
2638 pw.print(mUidLastForegroundElapsedTime.get(uid));
2639 pw.println();
2640 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002641 }
2642 }
2643
Makoto Onuki41066a62016-03-09 16:18:44 -08002644 static String formatTime(long time) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002645 Time tobj = new Time();
2646 tobj.set(time);
2647 return tobj.format("%Y-%m-%d %H:%M:%S");
2648 }
2649
Makoto Onuki2e210c42016-03-30 08:30:36 -07002650 private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) {
2651 pw.print(prefix);
2652 final int count = mCountStats[statId];
2653 final long dur = mDurationStats[statId];
2654 pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
2655 label, count, dur,
2656 (count == 0 ? 0 : ((double) dur) / count)));
2657 }
2658
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002659 // === Shell support ===
2660
2661 @Override
2662 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
2663 String[] args, ResultReceiver resultReceiver) throws RemoteException {
2664
2665 enforceShell();
2666
2667 (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
2668 }
2669
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002670 static class CommandException extends Exception {
2671 public CommandException(String message) {
2672 super(message);
2673 }
2674 }
2675
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002676 /**
2677 * Handle "adb shell cmd".
2678 */
2679 private class MyShellCommand extends ShellCommand {
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002680
2681 private int mUserId = UserHandle.USER_SYSTEM;
2682
2683 private void parseOptions(boolean takeUser)
2684 throws CommandException {
2685 String opt;
2686 while ((opt = getNextOption()) != null) {
2687 switch (opt) {
2688 case "--user":
2689 if (takeUser) {
2690 mUserId = UserHandle.parseUserArg(getNextArgRequired());
2691 break;
2692 }
2693 // fallthrough
2694 default:
2695 throw new CommandException("Unknown option: " + opt);
2696 }
2697 }
2698 }
2699
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002700 @Override
2701 public int onCommand(String cmd) {
2702 if (cmd == null) {
2703 return handleDefaultCommands(cmd);
2704 }
2705 final PrintWriter pw = getOutPrintWriter();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002706 try {
2707 switch (cmd) {
2708 case "reset-package-throttling":
2709 handleResetPackageThrottling();
2710 break;
2711 case "reset-throttling":
2712 handleResetThrottling();
2713 break;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002714 case "reset-all-throttling":
2715 handleResetAllThrottling();
2716 break;
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002717 case "override-config":
2718 handleOverrideConfig();
2719 break;
2720 case "reset-config":
2721 handleResetConfig();
2722 break;
2723 case "clear-default-launcher":
2724 handleClearDefaultLauncher();
2725 break;
2726 case "get-default-launcher":
2727 handleGetDefaultLauncher();
2728 break;
2729 case "refresh-default-launcher":
2730 handleRefreshDefaultLauncher();
2731 break;
Makoto Onukiac214972016-04-04 10:19:45 -07002732 case "unload-user":
2733 handleUnloadUser();
2734 break;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002735 case "clear-shortcuts":
2736 handleClearShortcuts();
2737 break;
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002738 default:
2739 return handleDefaultCommands(cmd);
2740 }
2741 } catch (CommandException e) {
2742 pw.println("Error: " + e.getMessage());
2743 return 1;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002744 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002745 pw.println("Success");
2746 return 0;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002747 }
2748
2749 @Override
2750 public void onHelp() {
2751 final PrintWriter pw = getOutPrintWriter();
2752 pw.println("Usage: cmd shortcut COMMAND [options ...]");
2753 pw.println();
2754 pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
2755 pw.println(" Reset throttling for a package");
2756 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002757 pw.println("cmd shortcut reset-throttling [--user USER_ID]");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002758 pw.println(" Reset throttling for all packages and users");
2759 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002760 pw.println("cmd shortcut reset-all-throttling");
2761 pw.println(" Reset the throttling state for all users");
2762 pw.println();
Makoto Onuki4362a662016-03-08 18:59:09 -08002763 pw.println("cmd shortcut override-config CONFIG");
2764 pw.println(" Override the configuration for testing (will last until reboot)");
2765 pw.println();
2766 pw.println("cmd shortcut reset-config");
2767 pw.println(" Reset the configuration set with \"update-config\"");
2768 pw.println();
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002769 pw.println("cmd shortcut clear-default-launcher [--user USER_ID]");
2770 pw.println(" Clear the cached default launcher");
2771 pw.println();
2772 pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
2773 pw.println(" Show the cached default launcher");
2774 pw.println();
2775 pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]");
2776 pw.println(" Refresh the cached default launcher");
2777 pw.println();
Makoto Onukiac214972016-04-04 10:19:45 -07002778 pw.println("cmd shortcut unload-user [--user USER_ID]");
2779 pw.println(" Unload a user from the memory");
2780 pw.println(" (This should not affect any observable behavior)");
2781 pw.println();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002782 pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
2783 pw.println(" Remove all shortcuts from a package, including pinned shortcuts");
2784 pw.println();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002785 }
2786
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002787 private void handleResetThrottling() throws CommandException {
Makoto Onuki4554d0e2016-03-14 15:51:41 -07002788 parseOptions(/* takeUser =*/ true);
2789
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002790 Slog.i(TAG, "cmd: handleResetThrottling");
2791
Makoto Onuki4554d0e2016-03-14 15:51:41 -07002792 resetThrottlingInner(mUserId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002793 }
2794
2795 private void handleResetAllThrottling() {
2796 Slog.i(TAG, "cmd: handleResetAllThrottling");
2797
2798 resetAllThrottlingInner();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002799 }
2800
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002801 private void handleResetPackageThrottling() throws CommandException {
2802 parseOptions(/* takeUser =*/ true);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002803
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002804 final String packageName = getNextArgRequired();
2805
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002806 Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName);
2807
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002808 resetPackageThrottling(packageName, mUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002809 }
Makoto Onuki4362a662016-03-08 18:59:09 -08002810
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002811 private void handleOverrideConfig() throws CommandException {
Makoto Onuki4362a662016-03-08 18:59:09 -08002812 final String config = getNextArgRequired();
2813
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002814 Slog.i(TAG, "cmd: handleOverrideConfig: " + config);
2815
Makoto Onuki4362a662016-03-08 18:59:09 -08002816 synchronized (mLock) {
2817 if (!updateConfigurationLocked(config)) {
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002818 throw new CommandException("override-config failed. See logcat for details.");
Makoto Onuki4362a662016-03-08 18:59:09 -08002819 }
2820 }
Makoto Onuki4362a662016-03-08 18:59:09 -08002821 }
2822
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002823 private void handleResetConfig() {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002824 Slog.i(TAG, "cmd: handleResetConfig");
2825
Makoto Onuki4362a662016-03-08 18:59:09 -08002826 synchronized (mLock) {
2827 loadConfigurationLocked();
2828 }
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002829 }
2830
2831 private void clearLauncher() {
2832 synchronized (mLock) {
Makoto Onukic51b2872016-05-04 15:24:50 -07002833 getUserShortcutsLocked(mUserId).setDefaultLauncherComponent(null);
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002834 }
2835 }
2836
2837 private void showLauncher() {
2838 synchronized (mLock) {
2839 // This ensures to set the cached launcher. Package name doesn't matter.
2840 hasShortcutHostPermissionInner("-", mUserId);
2841
2842 getOutPrintWriter().println("Launcher: "
Makoto Onukic51b2872016-05-04 15:24:50 -07002843 + getUserShortcutsLocked(mUserId).getDefaultLauncherComponent());
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002844 }
2845 }
2846
2847 private void handleClearDefaultLauncher() throws CommandException {
2848 parseOptions(/* takeUser =*/ true);
2849
2850 clearLauncher();
2851 }
2852
2853 private void handleGetDefaultLauncher() throws CommandException {
2854 parseOptions(/* takeUser =*/ true);
2855
2856 showLauncher();
2857 }
2858
2859 private void handleRefreshDefaultLauncher() throws CommandException {
2860 parseOptions(/* takeUser =*/ true);
2861
2862 clearLauncher();
2863 showLauncher();
Makoto Onuki4362a662016-03-08 18:59:09 -08002864 }
Makoto Onukiac214972016-04-04 10:19:45 -07002865
2866 private void handleUnloadUser() throws CommandException {
2867 parseOptions(/* takeUser =*/ true);
2868
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002869 Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId);
2870
Makoto Onukiac214972016-04-04 10:19:45 -07002871 ShortcutService.this.handleCleanupUser(mUserId);
2872 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002873
2874 private void handleClearShortcuts() throws CommandException {
2875 parseOptions(/* takeUser =*/ true);
2876 final String packageName = getNextArgRequired();
2877
2878 Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName);
2879
Makoto Onuki9ac59d02016-04-26 11:23:14 -07002880 ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07002881 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002882 }
2883
2884 // === Unit test support ===
2885
2886 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07002887 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002888 long injectCurrentTimeMillis() {
2889 return System.currentTimeMillis();
2890 }
2891
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002892 @VisibleForTesting
2893 long injectElapsedRealtime() {
2894 return SystemClock.elapsedRealtime();
2895 }
2896
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002897 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07002898 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002899 int injectBinderCallingUid() {
2900 return getCallingUid();
2901 }
2902
Makoto Onuki31459242016-03-22 11:12:18 -07002903 private int getCallingUserId() {
Makoto Onuki4554d0e2016-03-14 15:51:41 -07002904 return UserHandle.getUserId(injectBinderCallingUid());
2905 }
2906
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07002907 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07002908 @VisibleForTesting
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07002909 long injectClearCallingIdentity() {
2910 return Binder.clearCallingIdentity();
2911 }
2912
2913 // Injection point.
Makoto Onuki31459242016-03-22 11:12:18 -07002914 @VisibleForTesting
Makoto Onuki4dbe0de2016-03-14 17:31:49 -07002915 void injectRestoreCallingIdentity(long token) {
2916 Binder.restoreCallingIdentity(token);
2917 }
2918
Makoto Onukide667372016-03-15 14:29:20 -07002919 final void wtf(String message) {
Makoto Onuki2e210c42016-03-30 08:30:36 -07002920 wtf( message, /* exception= */ null);
Makoto Onukide667372016-03-15 14:29:20 -07002921 }
2922
Makoto Onuki2e210c42016-03-30 08:30:36 -07002923 // Injection point.
Makoto Onukide667372016-03-15 14:29:20 -07002924 void wtf(String message, Exception e) {
2925 Slog.wtf(TAG, message, e);
2926 }
2927
Makoto Onuki31459242016-03-22 11:12:18 -07002928 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002929 File injectSystemDataPath() {
2930 return Environment.getDataSystemDirectory();
2931 }
2932
Makoto Onuki31459242016-03-22 11:12:18 -07002933 @VisibleForTesting
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002934 File injectUserDataPath(@UserIdInt int userId) {
Makoto Onuki55046222016-03-08 10:49:47 -08002935 return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
2936 }
2937
Makoto Onuki4362a662016-03-08 18:59:09 -08002938 @VisibleForTesting
Makoto Onuki55046222016-03-08 10:49:47 -08002939 boolean injectIsLowRamDevice() {
2940 return ActivityManager.isLowRamDeviceStatic();
2941 }
2942
Makoto Onuki31459242016-03-22 11:12:18 -07002943 @VisibleForTesting
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07002944 void injectRegisterUidObserver(IUidObserver observer, int which) {
2945 try {
2946 ActivityManagerNative.getDefault().registerUidObserver(observer, which);
2947 } catch (RemoteException shouldntHappen) {
2948 }
2949 }
2950
2951 @VisibleForTesting
Makoto Onuki2d5b4652016-03-11 16:09:54 -08002952 PackageManagerInternal injectPackageManagerInternal() {
2953 return mPackageManagerInternal;
2954 }
2955
Makoto Onuki55046222016-03-08 10:49:47 -08002956 File getUserBitmapFilePath(@UserIdInt int userId) {
2957 return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002958 }
2959
2960 @VisibleForTesting
Makoto Onuki31459242016-03-22 11:12:18 -07002961 SparseArray<ShortcutUser> getShortcutsForTest() {
Makoto Onuki3f4b1ca2016-03-11 13:44:32 -08002962 return mUsers;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002963 }
2964
2965 @VisibleForTesting
Makoto Onuki4362a662016-03-08 18:59:09 -08002966 int getMaxDynamicShortcutsForTest() {
2967 return mMaxDynamicShortcuts;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002968 }
2969
2970 @VisibleForTesting
Makoto Onukib6d35232016-04-04 15:57:17 -07002971 int getMaxUpdatesPerIntervalForTest() {
2972 return mMaxUpdatesPerInterval;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002973 }
2974
2975 @VisibleForTesting
Makoto Onuki4362a662016-03-08 18:59:09 -08002976 long getResetIntervalForTest() {
2977 return mResetInterval;
Makoto Onuki55046222016-03-08 10:49:47 -08002978 }
2979
2980 @VisibleForTesting
Makoto Onuki4362a662016-03-08 18:59:09 -08002981 int getMaxIconDimensionForTest() {
2982 return mMaxIconDimension;
2983 }
2984
2985 @VisibleForTesting
2986 CompressFormat getIconPersistFormatForTest() {
2987 return mIconPersistFormat;
2988 }
2989
2990 @VisibleForTesting
2991 int getIconPersistQualityForTest() {
2992 return mIconPersistQuality;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002993 }
Makoto Onuki41066a62016-03-09 16:18:44 -08002994
2995 @VisibleForTesting
Makoto Onuki22fcc682016-05-17 14:52:19 -07002996 ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
Makoto Onuki41066a62016-03-09 16:18:44 -08002997 synchronized (mLock) {
Makoto Onuki31459242016-03-22 11:12:18 -07002998 final ShortcutUser user = mUsers.get(userId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07002999 if (user == null) return null;
3000
Makoto Onuki22fcc682016-05-17 14:52:19 -07003001 return user.getAllPackagesForTest().get(packageName);
3002 }
3003 }
3004
3005 @VisibleForTesting
3006 ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
3007 synchronized (mLock) {
3008 final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
Makoto Onukicdc78f72016-03-21 15:47:52 -07003009 if (pkg == null) return null;
3010
3011 return pkg.findShortcutById(shortcutId);
Makoto Onuki41066a62016-03-09 16:18:44 -08003012 }
3013 }
Makoto Onuki7001a612016-05-27 13:24:28 -07003014
3015 /**
3016 * Control whether {@link #verifyStates} should be performed. We always perform it during unit
3017 * tests.
3018 */
3019 @VisibleForTesting
3020 boolean injectShouldPerformVerification() {
3021 return DEBUG;
3022 }
3023
3024 /**
3025 * Check various internal states and throws if there's any inconsistency.
3026 * This is normally only enabled during unit tests.
3027 */
3028 final void verifyStates() {
3029 if (injectShouldPerformVerification()) {
3030 verifyStatesInner();
3031 }
3032 }
3033
3034 private void verifyStatesInner() {
3035 synchronized (this) {
3036 forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
3037 }
3038 }
Makoto Onuki41066a62016-03-09 16:18:44 -08003039}