Home | History | Annotate | Download | only in pm
      1 /*
      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  */
     16 package com.android.server.pm;
     17 
     18 import android.annotation.IntDef;
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.annotation.UserIdInt;
     22 import android.app.ActivityManager;
     23 import android.app.ActivityManagerInternal;
     24 import android.app.AppGlobals;
     25 import android.app.IUidObserver;
     26 import android.app.usage.UsageStatsManagerInternal;
     27 import android.appwidget.AppWidgetProviderInfo;
     28 import android.content.BroadcastReceiver;
     29 import android.content.ComponentName;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.content.IntentFilter;
     33 import android.content.IntentSender;
     34 import android.content.IntentSender.SendIntentException;
     35 import android.content.pm.ActivityInfo;
     36 import android.content.pm.ApplicationInfo;
     37 import android.content.pm.IPackageManager;
     38 import android.content.pm.IShortcutService;
     39 import android.content.pm.LauncherApps;
     40 import android.content.pm.LauncherApps.ShortcutQuery;
     41 import android.content.pm.PackageInfo;
     42 import android.content.pm.PackageManager;
     43 import android.content.pm.PackageManager.NameNotFoundException;
     44 import android.content.pm.PackageManagerInternal;
     45 import android.content.pm.ParceledListSlice;
     46 import android.content.pm.ResolveInfo;
     47 import android.content.pm.ShortcutInfo;
     48 import android.content.pm.ShortcutServiceInternal;
     49 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
     50 import android.content.pm.UserInfo;
     51 import android.content.res.Resources;
     52 import android.content.res.XmlResourceParser;
     53 import android.graphics.Bitmap;
     54 import android.graphics.Bitmap.CompressFormat;
     55 import android.graphics.Canvas;
     56 import android.graphics.RectF;
     57 import android.graphics.drawable.AdaptiveIconDrawable;
     58 import android.graphics.drawable.Icon;
     59 import android.net.Uri;
     60 import android.os.Binder;
     61 import android.os.Build;
     62 import android.os.Bundle;
     63 import android.os.Environment;
     64 import android.os.FileUtils;
     65 import android.os.Handler;
     66 import android.os.LocaleList;
     67 import android.os.Looper;
     68 import android.os.ParcelFileDescriptor;
     69 import android.os.PersistableBundle;
     70 import android.os.Process;
     71 import android.os.RemoteException;
     72 import android.os.ResultReceiver;
     73 import android.os.SELinux;
     74 import android.os.ServiceManager;
     75 import android.os.ShellCallback;
     76 import android.os.ShellCommand;
     77 import android.os.SystemClock;
     78 import android.os.UserHandle;
     79 import android.os.UserManager;
     80 import android.text.TextUtils;
     81 import android.text.format.Time;
     82 import android.util.ArraySet;
     83 import android.util.AtomicFile;
     84 import android.util.KeyValueListParser;
     85 import android.util.Log;
     86 import android.util.Slog;
     87 import android.util.SparseArray;
     88 import android.util.SparseBooleanArray;
     89 import android.util.SparseIntArray;
     90 import android.util.SparseLongArray;
     91 import android.util.TypedValue;
     92 import android.util.Xml;
     93 import android.view.IWindowManager;
     94 
     95 import com.android.internal.annotations.GuardedBy;
     96 import com.android.internal.annotations.VisibleForTesting;
     97 import com.android.internal.os.BackgroundThread;
     98 import com.android.internal.util.DumpUtils;
     99 import com.android.internal.util.FastXmlSerializer;
    100 import com.android.internal.util.Preconditions;
    101 import com.android.server.LocalServices;
    102 import com.android.server.SystemService;
    103 import com.android.server.pm.ShortcutUser.PackageWithUser;
    104 
    105 import libcore.io.IoUtils;
    106 
    107 import org.json.JSONArray;
    108 import org.json.JSONException;
    109 import org.json.JSONObject;
    110 import org.xmlpull.v1.XmlPullParser;
    111 import org.xmlpull.v1.XmlPullParserException;
    112 import org.xmlpull.v1.XmlSerializer;
    113 
    114 import java.io.BufferedInputStream;
    115 import java.io.BufferedOutputStream;
    116 import java.io.ByteArrayInputStream;
    117 import java.io.ByteArrayOutputStream;
    118 import java.io.File;
    119 import java.io.FileDescriptor;
    120 import java.io.FileInputStream;
    121 import java.io.FileNotFoundException;
    122 import java.io.FileOutputStream;
    123 import java.io.IOException;
    124 import java.io.InputStream;
    125 import java.io.OutputStream;
    126 import java.io.PrintWriter;
    127 import java.lang.annotation.Retention;
    128 import java.lang.annotation.RetentionPolicy;
    129 import java.net.URISyntaxException;
    130 import java.nio.charset.StandardCharsets;
    131 import java.util.ArrayList;
    132 import java.util.Collections;
    133 import java.util.List;
    134 import java.util.concurrent.atomic.AtomicBoolean;
    135 import java.util.function.Consumer;
    136 import java.util.function.Predicate;
    137 
    138 /**
    139  * TODO:
    140  * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
    141  *   -> But TypedValue.applyDimension() doesn't differentiate x and y..?
    142  *
    143  * - Detect when already registered instances are passed to APIs again, which might break
    144  * internal bitmap handling.
    145  */
    146 public class ShortcutService extends IShortcutService.Stub {
    147     static final String TAG = "ShortcutService";
    148 
    149     static final boolean DEBUG = false; // STOPSHIP if true
    150     static final boolean DEBUG_LOAD = false; // STOPSHIP if true
    151     static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
    152 
    153     @VisibleForTesting
    154     static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
    155 
    156     @VisibleForTesting
    157     static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10;
    158 
    159     @VisibleForTesting
    160     static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
    161 
    162     @VisibleForTesting
    163     static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
    164 
    165     @VisibleForTesting
    166     static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
    167 
    168     @VisibleForTesting
    169     static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
    170 
    171     @VisibleForTesting
    172     static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
    173 
    174     @VisibleForTesting
    175     static final int DEFAULT_SAVE_DELAY_MS = 3000;
    176 
    177     @VisibleForTesting
    178     static final String FILENAME_BASE_STATE = "shortcut_service.xml";
    179 
    180     @VisibleForTesting
    181     static final String DIRECTORY_PER_USER = "shortcut_service";
    182 
    183     @VisibleForTesting
    184     static final String DIRECTORY_DUMP = "shortcut_dump";
    185 
    186     @VisibleForTesting
    187     static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
    188 
    189     static final String DIRECTORY_BITMAPS = "bitmaps";
    190 
    191     private static final String TAG_ROOT = "root";
    192     private static final String TAG_LAST_RESET_TIME = "last_reset_time";
    193 
    194     private static final String ATTR_VALUE = "value";
    195 
    196     private static final String LAUNCHER_INTENT_CATEGORY = Intent.CATEGORY_LAUNCHER;
    197 
    198     private static final String KEY_SHORTCUT = "shortcut";
    199     private static final String KEY_LOW_RAM = "lowRam";
    200     private static final String KEY_ICON_SIZE = "iconSize";
    201 
    202     private static final String DUMMY_MAIN_ACTIVITY = "android.__dummy__";
    203 
    204     @VisibleForTesting
    205     interface ConfigConstants {
    206         /**
    207          * Key name for the save delay, in milliseconds. (int)
    208          */
    209         String KEY_SAVE_DELAY_MILLIS = "save_delay_ms";
    210 
    211         /**
    212          * Key name for the throttling reset interval, in seconds. (long)
    213          */
    214         String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
    215 
    216         /**
    217          * Key name for the max number of modifying API calls per app for every interval. (int)
    218          */
    219         String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval";
    220 
    221         /**
    222          * Key name for the max icon dimensions in DP, for non-low-memory devices.
    223          */
    224         String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
    225 
    226         /**
    227          * Key name for the max icon dimensions in DP, for low-memory devices.
    228          */
    229         String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
    230 
    231         /**
    232          * Key name for the max dynamic shortcuts per activity. (int)
    233          */
    234         String KEY_MAX_SHORTCUTS = "max_shortcuts";
    235 
    236         /**
    237          * Key name for icon compression quality, 0-100.
    238          */
    239         String KEY_ICON_QUALITY = "icon_quality";
    240 
    241         /**
    242          * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
    243          */
    244         String KEY_ICON_FORMAT = "icon_format";
    245     }
    246 
    247     final Context mContext;
    248 
    249     private final Object mLock = new Object();
    250 
    251     private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
    252 
    253     // Temporarily reverted to anonymous inner class form due to: b/32554459
    254     private static Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED = new Predicate<ResolveInfo>() {
    255         public boolean test(ResolveInfo ri) {
    256             return !ri.activityInfo.exported;
    257         }
    258     };
    259 
    260     // Temporarily reverted to anonymous inner class form due to: b/32554459
    261     private static Predicate<PackageInfo> PACKAGE_NOT_INSTALLED = new Predicate<PackageInfo>() {
    262         public boolean test(PackageInfo pi) {
    263             return !isInstalled(pi);
    264         }
    265     };
    266 
    267     private final Handler mHandler;
    268 
    269     @GuardedBy("mLock")
    270     private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
    271 
    272     @GuardedBy("mLock")
    273     private long mRawLastResetTime;
    274 
    275     /**
    276      * User ID -> UserShortcuts
    277      */
    278     @GuardedBy("mLock")
    279     private final SparseArray<ShortcutUser> mUsers = new SparseArray<>();
    280 
    281     /**
    282      * Max number of dynamic + manifest shortcuts that each application can have at a time.
    283      */
    284     private int mMaxShortcuts;
    285 
    286     /**
    287      * Max number of updating API calls that each application can make during the interval.
    288      */
    289     int mMaxUpdatesPerInterval;
    290 
    291     /**
    292      * Actual throttling-reset interval.  By default it's a day.
    293      */
    294     private long mResetInterval;
    295 
    296     /**
    297      * Icon max width/height in pixels.
    298      */
    299     private int mMaxIconDimension;
    300 
    301     private CompressFormat mIconPersistFormat;
    302     private int mIconPersistQuality;
    303 
    304     private int mSaveDelayMillis;
    305 
    306     private final IPackageManager mIPackageManager;
    307     private final PackageManagerInternal mPackageManagerInternal;
    308     private final UserManager mUserManager;
    309     private final UsageStatsManagerInternal mUsageStatsManagerInternal;
    310     private final ActivityManagerInternal mActivityManagerInternal;
    311 
    312     private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor;
    313     private final ShortcutBitmapSaver mShortcutBitmapSaver;
    314     private final ShortcutDumpFiles mShortcutDumpFiles;
    315 
    316     @GuardedBy("mLock")
    317     final SparseIntArray mUidState = new SparseIntArray();
    318 
    319     @GuardedBy("mLock")
    320     final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray();
    321 
    322     @GuardedBy("mLock")
    323     private List<Integer> mDirtyUserIds = new ArrayList<>();
    324 
    325     private final AtomicBoolean mBootCompleted = new AtomicBoolean();
    326 
    327     private static final int PACKAGE_MATCH_FLAGS =
    328             PackageManager.MATCH_DIRECT_BOOT_AWARE
    329                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
    330                     | PackageManager.MATCH_UNINSTALLED_PACKAGES;
    331 
    332     @GuardedBy("mLock")
    333     final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
    334 
    335     // Stats
    336     @VisibleForTesting
    337     interface Stats {
    338         int GET_DEFAULT_HOME = 0;
    339         int GET_PACKAGE_INFO = 1;
    340         int GET_PACKAGE_INFO_WITH_SIG = 2;
    341         int GET_APPLICATION_INFO = 3;
    342         int LAUNCHER_PERMISSION_CHECK = 4;
    343         int CLEANUP_DANGLING_BITMAPS = 5;
    344         int GET_ACTIVITY_WITH_METADATA = 6;
    345         int GET_INSTALLED_PACKAGES = 7;
    346         int CHECK_PACKAGE_CHANGES = 8;
    347         int GET_APPLICATION_RESOURCES = 9;
    348         int RESOURCE_NAME_LOOKUP = 10;
    349         int GET_LAUNCHER_ACTIVITY = 11;
    350         int CHECK_LAUNCHER_ACTIVITY = 12;
    351         int IS_ACTIVITY_ENABLED = 13;
    352         int PACKAGE_UPDATE_CHECK = 14;
    353         int ASYNC_PRELOAD_USER_DELAY = 15;
    354         int GET_DEFAULT_LAUNCHER = 16;
    355 
    356         int COUNT = GET_DEFAULT_LAUNCHER + 1;
    357     }
    358 
    359     private static final String[] STAT_LABELS = {
    360             "getHomeActivities()",
    361             "Launcher permission check",
    362             "getPackageInfo()",
    363             "getPackageInfo(SIG)",
    364             "getApplicationInfo",
    365             "cleanupDanglingBitmaps",
    366             "getActivity+metadata",
    367             "getInstalledPackages",
    368             "checkPackageChanges",
    369             "getApplicationResources",
    370             "resourceNameLookup",
    371             "getLauncherActivity",
    372             "checkLauncherActivity",
    373             "isActivityEnabled",
    374             "packageUpdateCheck",
    375             "asyncPreloadUserDelay",
    376             "getDefaultLauncher()"
    377     };
    378 
    379     final Object mStatLock = new Object();
    380 
    381     @GuardedBy("mStatLock")
    382     private final int[] mCountStats = new int[Stats.COUNT];
    383 
    384     @GuardedBy("mStatLock")
    385     private final long[] mDurationStats = new long[Stats.COUNT];
    386 
    387     private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
    388             ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
    389 
    390     static final int OPERATION_SET = 0;
    391     static final int OPERATION_ADD = 1;
    392     static final int OPERATION_UPDATE = 2;
    393 
    394     /** @hide */
    395     @IntDef(value = {
    396             OPERATION_SET,
    397             OPERATION_ADD,
    398             OPERATION_UPDATE
    399     })
    400     @Retention(RetentionPolicy.SOURCE)
    401     @interface ShortcutOperation {
    402     }
    403 
    404     @GuardedBy("mLock")
    405     private int mWtfCount = 0;
    406 
    407     @GuardedBy("mLock")
    408     private Exception mLastWtfStacktrace;
    409 
    410     static class InvalidFileFormatException extends Exception {
    411         public InvalidFileFormatException(String message, Throwable cause) {
    412             super(message, cause);
    413         }
    414     }
    415 
    416     public ShortcutService(Context context) {
    417         this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false);
    418     }
    419 
    420     @VisibleForTesting
    421     ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) {
    422         mContext = Preconditions.checkNotNull(context);
    423         LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
    424         mHandler = new Handler(looper);
    425         mIPackageManager = AppGlobals.getPackageManager();
    426         mPackageManagerInternal = Preconditions.checkNotNull(
    427                 LocalServices.getService(PackageManagerInternal.class));
    428         mUserManager = Preconditions.checkNotNull(context.getSystemService(UserManager.class));
    429         mUsageStatsManagerInternal = Preconditions.checkNotNull(
    430                 LocalServices.getService(UsageStatsManagerInternal.class));
    431         mActivityManagerInternal = Preconditions.checkNotNull(
    432                 LocalServices.getService(ActivityManagerInternal.class));
    433 
    434         mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
    435         mShortcutBitmapSaver = new ShortcutBitmapSaver(this);
    436         mShortcutDumpFiles = new ShortcutDumpFiles(this);
    437 
    438         if (onlyForPackageManagerApis) {
    439             return; // Don't do anything further.  For unit tests only.
    440         }
    441 
    442         // Register receivers.
    443 
    444         // We need to set a priority, so let's just not use PackageMonitor for now.
    445         // TODO Refactor PackageMonitor to support priorities.
    446         final IntentFilter packageFilter = new IntentFilter();
    447         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    448         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    449         packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    450         packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
    451         packageFilter.addDataScheme("package");
    452         packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
    453         mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL,
    454                 packageFilter, null, mHandler);
    455 
    456         final IntentFilter preferedActivityFilter = new IntentFilter();
    457         preferedActivityFilter.addAction(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
    458         preferedActivityFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
    459         mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL,
    460                 preferedActivityFilter, null, mHandler);
    461 
    462         final IntentFilter localeFilter = new IntentFilter();
    463         localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
    464         localeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
    465         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL,
    466                 localeFilter, null, mHandler);
    467 
    468         injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
    469                 | ActivityManager.UID_OBSERVER_GONE);
    470     }
    471 
    472     void logDurationStat(int statId, long start) {
    473         synchronized (mStatLock) {
    474             mCountStats[statId]++;
    475             mDurationStats[statId] += (injectElapsedRealtime() - start);
    476         }
    477     }
    478 
    479     public String injectGetLocaleTagsForUser(@UserIdInt int userId) {
    480         // TODO This should get the per-user locale.  b/30123329 b/30119489
    481         return LocaleList.getDefault().toLanguageTags();
    482     }
    483 
    484     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
    485         @Override
    486         public void onUidStateChanged(int uid, int procState, long procStateSeq) {
    487             injectPostToHandler(() -> handleOnUidStateChanged(uid, procState));
    488         }
    489 
    490         @Override
    491         public void onUidGone(int uid, boolean disabled) {
    492             injectPostToHandler(() ->
    493                     handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT));
    494         }
    495 
    496         @Override
    497         public void onUidActive(int uid) {
    498         }
    499 
    500         @Override
    501         public void onUidIdle(int uid, boolean disabled) {
    502         }
    503 
    504         @Override public void onUidCachedChanged(int uid, boolean cached) {
    505         }
    506     };
    507 
    508     void handleOnUidStateChanged(int uid, int procState) {
    509         if (DEBUG_PROCSTATE) {
    510             Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
    511         }
    512         synchronized (mLock) {
    513             mUidState.put(uid, procState);
    514 
    515             // We need to keep track of last time an app comes to foreground.
    516             // See ShortcutPackage.getApiCallCount() for how it's used.
    517             // It doesn't have to be persisted, but it needs to be the elapsed time.
    518             if (isProcessStateForeground(procState)) {
    519                 mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
    520             }
    521         }
    522     }
    523 
    524     private boolean isProcessStateForeground(int processState) {
    525         return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
    526     }
    527 
    528     boolean isUidForegroundLocked(int uid) {
    529         if (uid == Process.SYSTEM_UID) {
    530             // IUidObserver doesn't report the state of SYSTEM, but it always has bound services,
    531             // so it's foreground anyway.
    532             return true;
    533         }
    534         // First, check with the local cache.
    535         if (isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE))) {
    536             return true;
    537         }
    538         // If the cache says background, reach out to AM.  Since it'll internally need to hold
    539         // the AM lock, we use it as a last resort.
    540         return isProcessStateForeground(mActivityManagerInternal.getUidProcessState(uid));
    541     }
    542 
    543     long getUidLastForegroundElapsedTimeLocked(int uid) {
    544         return mUidLastForegroundElapsedTime.get(uid);
    545     }
    546 
    547     /**
    548      * System service lifecycle.
    549      */
    550     public static final class Lifecycle extends SystemService {
    551         final ShortcutService mService;
    552 
    553         public Lifecycle(Context context) {
    554             super(context);
    555             mService = new ShortcutService(context);
    556         }
    557 
    558         @Override
    559         public void onStart() {
    560             publishBinderService(Context.SHORTCUT_SERVICE, mService);
    561         }
    562 
    563         @Override
    564         public void onBootPhase(int phase) {
    565             mService.onBootPhase(phase);
    566         }
    567 
    568         @Override
    569         public void onStopUser(int userHandle) {
    570             mService.handleStopUser(userHandle);
    571         }
    572 
    573         @Override
    574         public void onUnlockUser(int userId) {
    575             mService.handleUnlockUser(userId);
    576         }
    577     }
    578 
    579     /** lifecycle event */
    580     void onBootPhase(int phase) {
    581         if (DEBUG) {
    582             Slog.d(TAG, "onBootPhase: " + phase);
    583         }
    584         switch (phase) {
    585             case SystemService.PHASE_LOCK_SETTINGS_READY:
    586                 initialize();
    587                 break;
    588             case SystemService.PHASE_BOOT_COMPLETED:
    589                 mBootCompleted.set(true);
    590                 break;
    591         }
    592     }
    593 
    594     /** lifecycle event */
    595     void handleUnlockUser(int userId) {
    596         if (DEBUG) {
    597         Slog.d(TAG, "handleUnlockUser: user=" + userId);
    598         }
    599         synchronized (mLock) {
    600             mUnlockedUsers.put(userId, true);
    601         }
    602 
    603         // Preload the user data.
    604         // Note, we don't use mHandler here but instead just start a new thread.
    605         // This is because mHandler (which uses com.android.internal.os.BackgroundThread) is very
    606         // busy at this point and this could take hundreds of milliseconds, which would be too
    607         // late since the launcher would already have started.
    608         // So we just create a new thread.  This code runs rarely, so we don't use a thread pool
    609         // or anything.
    610         final long start = injectElapsedRealtime();
    611         injectRunOnNewThread(() -> {
    612             synchronized (mLock) {
    613                 logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
    614                 getUserShortcutsLocked(userId);
    615             }
    616         });
    617     }
    618 
    619     /** lifecycle event */
    620     void handleStopUser(int userId) {
    621         if (DEBUG) {
    622             Slog.d(TAG, "handleStopUser: user=" + userId);
    623         }
    624         synchronized (mLock) {
    625             unloadUserLocked(userId);
    626 
    627             mUnlockedUsers.put(userId, false);
    628         }
    629     }
    630 
    631     private void unloadUserLocked(int userId) {
    632         if (DEBUG) {
    633             Slog.d(TAG, "unloadUserLocked: user=" + userId);
    634         }
    635         // Save all dirty information.
    636         saveDirtyInfo();
    637 
    638         // Unload
    639         mUsers.delete(userId);
    640     }
    641 
    642     /** Return the base state file name */
    643     private AtomicFile getBaseStateFile() {
    644         final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
    645         path.mkdirs();
    646         return new AtomicFile(path);
    647     }
    648 
    649     /**
    650      * Init the instance. (load the state file, etc)
    651      */
    652     private void initialize() {
    653         synchronized (mLock) {
    654             loadConfigurationLocked();
    655             loadBaseStateLocked();
    656         }
    657     }
    658 
    659     /**
    660      * Load the configuration from Settings.
    661      */
    662     private void loadConfigurationLocked() {
    663         updateConfigurationLocked(injectShortcutManagerConstants());
    664     }
    665 
    666     /**
    667      * Load the configuration from Settings.
    668      */
    669     @VisibleForTesting
    670     boolean updateConfigurationLocked(String config) {
    671         boolean result = true;
    672 
    673         final KeyValueListParser parser = new KeyValueListParser(',');
    674         try {
    675             parser.setString(config);
    676         } catch (IllegalArgumentException e) {
    677             // Failed to parse the settings string, log this and move on
    678             // with defaults.
    679             Slog.e(TAG, "Bad shortcut manager settings", e);
    680             result = false;
    681         }
    682 
    683         mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
    684                 DEFAULT_SAVE_DELAY_MS));
    685 
    686         mResetInterval = Math.max(1, parser.getLong(
    687                 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
    688                 * 1000L);
    689 
    690         mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong(
    691                 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL));
    692 
    693         mMaxShortcuts = Math.max(0, (int) parser.getLong(
    694                 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP));
    695 
    696         final int iconDimensionDp = Math.max(1, injectIsLowRamDevice()
    697                 ? (int) parser.getLong(
    698                 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
    699                 DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
    700                 : (int) parser.getLong(
    701                 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
    702                 DEFAULT_MAX_ICON_DIMENSION_DP));
    703 
    704         mMaxIconDimension = injectDipToPixel(iconDimensionDp);
    705 
    706         mIconPersistFormat = CompressFormat.valueOf(
    707                 parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
    708 
    709         mIconPersistQuality = (int) parser.getLong(
    710                 ConfigConstants.KEY_ICON_QUALITY,
    711                 DEFAULT_ICON_PERSIST_QUALITY);
    712 
    713         return result;
    714     }
    715 
    716     @VisibleForTesting
    717     String injectShortcutManagerConstants() {
    718         return android.provider.Settings.Global.getString(
    719                 mContext.getContentResolver(),
    720                 android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
    721     }
    722 
    723     @VisibleForTesting
    724     int injectDipToPixel(int dip) {
    725         return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
    726                 mContext.getResources().getDisplayMetrics());
    727     }
    728 
    729     // === Persisting ===
    730 
    731     @Nullable
    732     static String parseStringAttribute(XmlPullParser parser, String attribute) {
    733         return parser.getAttributeValue(null, attribute);
    734     }
    735 
    736     static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) {
    737         return parseLongAttribute(parser, attribute) == 1;
    738     }
    739 
    740     static int parseIntAttribute(XmlPullParser parser, String attribute) {
    741         return (int) parseLongAttribute(parser, attribute);
    742     }
    743 
    744     static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
    745         return (int) parseLongAttribute(parser, attribute, def);
    746     }
    747 
    748     static long parseLongAttribute(XmlPullParser parser, String attribute) {
    749         return parseLongAttribute(parser, attribute, 0);
    750     }
    751 
    752     static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
    753         final String value = parseStringAttribute(parser, attribute);
    754         if (TextUtils.isEmpty(value)) {
    755             return def;
    756         }
    757         try {
    758             return Long.parseLong(value);
    759         } catch (NumberFormatException e) {
    760             Slog.e(TAG, "Error parsing long " + value);
    761             return def;
    762         }
    763     }
    764 
    765     @Nullable
    766     static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
    767         final String value = parseStringAttribute(parser, attribute);
    768         if (TextUtils.isEmpty(value)) {
    769             return null;
    770         }
    771         return ComponentName.unflattenFromString(value);
    772     }
    773 
    774     @Nullable
    775     static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) {
    776         final String value = parseStringAttribute(parser, attribute);
    777         Intent parsed = null;
    778         if (!TextUtils.isEmpty(value)) {
    779             try {
    780                 parsed = Intent.parseUri(value, /* flags =*/ 0);
    781             } catch (URISyntaxException e) {
    782                 Slog.e(TAG, "Error parsing intent", e);
    783             }
    784         }
    785         return parsed;
    786     }
    787 
    788     @Nullable
    789     static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
    790         Intent parsed = parseIntentAttributeNoDefault(parser, attribute);
    791         if (parsed == null) {
    792             // Default intent.
    793             parsed = new Intent(Intent.ACTION_VIEW);
    794         }
    795         return parsed;
    796     }
    797 
    798     static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
    799         if (TextUtils.isEmpty(value)) return;
    800 
    801         out.startTag(null, tag);
    802         out.attribute(null, ATTR_VALUE, value);
    803         out.endTag(null, tag);
    804     }
    805 
    806     static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
    807         writeTagValue(out, tag, Long.toString(value));
    808     }
    809 
    810     static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
    811         if (name == null) return;
    812         writeTagValue(out, tag, name.flattenToString());
    813     }
    814 
    815     static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
    816             throws IOException, XmlPullParserException {
    817         if (bundle == null) return;
    818 
    819         out.startTag(null, tag);
    820         bundle.saveToXml(out);
    821         out.endTag(null, tag);
    822     }
    823 
    824     static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException {
    825         if (TextUtils.isEmpty(value)) return;
    826 
    827         out.attribute(null, name, value.toString());
    828     }
    829 
    830     static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
    831         writeAttr(out, name, String.valueOf(value));
    832     }
    833 
    834     static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
    835         if (value) {
    836             writeAttr(out, name, "1");
    837         }
    838     }
    839 
    840     static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
    841         if (comp == null) return;
    842         writeAttr(out, name, comp.flattenToString());
    843     }
    844 
    845     static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
    846         if (intent == null) return;
    847 
    848         writeAttr(out, name, intent.toUri(/* flags =*/ 0));
    849     }
    850 
    851     @VisibleForTesting
    852     void saveBaseStateLocked() {
    853         final AtomicFile file = getBaseStateFile();
    854         if (DEBUG) {
    855             Slog.d(TAG, "Saving to " + file.getBaseFile());
    856         }
    857 
    858         FileOutputStream outs = null;
    859         try {
    860             outs = file.startWrite();
    861 
    862             // Write to XML
    863             XmlSerializer out = new FastXmlSerializer();
    864             out.setOutput(outs, StandardCharsets.UTF_8.name());
    865             out.startDocument(null, true);
    866             out.startTag(null, TAG_ROOT);
    867 
    868             // Body.
    869             writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
    870 
    871             // Epilogue.
    872             out.endTag(null, TAG_ROOT);
    873             out.endDocument();
    874 
    875             // Close.
    876             file.finishWrite(outs);
    877         } catch (IOException e) {
    878             Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
    879             file.failWrite(outs);
    880         }
    881     }
    882 
    883     private void loadBaseStateLocked() {
    884         mRawLastResetTime = 0;
    885 
    886         final AtomicFile file = getBaseStateFile();
    887         if (DEBUG) {
    888             Slog.d(TAG, "Loading from " + file.getBaseFile());
    889         }
    890         try (FileInputStream in = file.openRead()) {
    891             XmlPullParser parser = Xml.newPullParser();
    892             parser.setInput(in, StandardCharsets.UTF_8.name());
    893 
    894             int type;
    895             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
    896                 if (type != XmlPullParser.START_TAG) {
    897                     continue;
    898                 }
    899                 final int depth = parser.getDepth();
    900                 // Check the root tag
    901                 final String tag = parser.getName();
    902                 if (depth == 1) {
    903                     if (!TAG_ROOT.equals(tag)) {
    904                         Slog.e(TAG, "Invalid root tag: " + tag);
    905                         return;
    906                     }
    907                     continue;
    908                 }
    909                 // Assume depth == 2
    910                 switch (tag) {
    911                     case TAG_LAST_RESET_TIME:
    912                         mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
    913                         break;
    914                     default:
    915                         Slog.e(TAG, "Invalid tag: " + tag);
    916                         break;
    917                 }
    918             }
    919         } catch (FileNotFoundException e) {
    920             // Use the default
    921         } catch (IOException | XmlPullParserException e) {
    922             Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
    923 
    924             mRawLastResetTime = 0;
    925         }
    926         // Adjust the last reset time.
    927         getLastResetTimeLocked();
    928     }
    929 
    930     @VisibleForTesting
    931     final File getUserFile(@UserIdInt int userId) {
    932         return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
    933     }
    934 
    935     private void saveUserLocked(@UserIdInt int userId) {
    936         final File path = getUserFile(userId);
    937         if (DEBUG) {
    938             Slog.d(TAG, "Saving to " + path);
    939         }
    940 
    941         mShortcutBitmapSaver.waitForAllSavesLocked();
    942 
    943         path.getParentFile().mkdirs();
    944         final AtomicFile file = new AtomicFile(path);
    945         FileOutputStream os = null;
    946         try {
    947             os = file.startWrite();
    948 
    949             saveUserInternalLocked(userId, os, /* forBackup= */ false);
    950 
    951             file.finishWrite(os);
    952 
    953             // Remove all dangling bitmap files.
    954             cleanupDanglingBitmapDirectoriesLocked(userId);
    955         } catch (XmlPullParserException | IOException e) {
    956             Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
    957             file.failWrite(os);
    958         }
    959     }
    960 
    961     private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
    962             boolean forBackup) throws IOException, XmlPullParserException {
    963 
    964         final BufferedOutputStream bos = new BufferedOutputStream(os);
    965 
    966         // Write to XML
    967         XmlSerializer out = new FastXmlSerializer();
    968         out.setOutput(bos, StandardCharsets.UTF_8.name());
    969         out.startDocument(null, true);
    970 
    971         getUserShortcutsLocked(userId).saveToXml(out, forBackup);
    972 
    973         out.endDocument();
    974 
    975         bos.flush();
    976         os.flush();
    977     }
    978 
    979     static IOException throwForInvalidTag(int depth, String tag) throws IOException {
    980         throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
    981     }
    982 
    983     static void warnForInvalidTag(int depth, String tag) throws IOException {
    984         Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
    985     }
    986 
    987     @Nullable
    988     private ShortcutUser loadUserLocked(@UserIdInt int userId) {
    989         final File path = getUserFile(userId);
    990         if (DEBUG) {
    991             Slog.d(TAG, "Loading from " + path);
    992         }
    993         final AtomicFile file = new AtomicFile(path);
    994 
    995         final FileInputStream in;
    996         try {
    997             in = file.openRead();
    998         } catch (FileNotFoundException e) {
    999             if (DEBUG) {
   1000                 Slog.d(TAG, "Not found " + path);
   1001             }
   1002             return null;
   1003         }
   1004         try {
   1005             final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false);
   1006             return ret;
   1007         } catch (IOException | XmlPullParserException | InvalidFileFormatException e) {
   1008             Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
   1009             return null;
   1010         } finally {
   1011             IoUtils.closeQuietly(in);
   1012         }
   1013     }
   1014 
   1015     private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
   1016             boolean fromBackup) throws XmlPullParserException, IOException,
   1017             InvalidFileFormatException {
   1018 
   1019         final BufferedInputStream bis = new BufferedInputStream(is);
   1020 
   1021         ShortcutUser ret = null;
   1022         XmlPullParser parser = Xml.newPullParser();
   1023         parser.setInput(bis, StandardCharsets.UTF_8.name());
   1024 
   1025         int type;
   1026         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
   1027             if (type != XmlPullParser.START_TAG) {
   1028                 continue;
   1029             }
   1030             final int depth = parser.getDepth();
   1031 
   1032             final String tag = parser.getName();
   1033             if (DEBUG_LOAD) {
   1034                 Slog.d(TAG, String.format("depth=%d type=%d name=%s",
   1035                         depth, type, tag));
   1036             }
   1037             if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
   1038                 ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup);
   1039                 continue;
   1040             }
   1041             throwForInvalidTag(depth, tag);
   1042         }
   1043         return ret;
   1044     }
   1045 
   1046     private void scheduleSaveBaseState() {
   1047         scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
   1048     }
   1049 
   1050     void scheduleSaveUser(@UserIdInt int userId) {
   1051         scheduleSaveInner(userId);
   1052     }
   1053 
   1054     // In order to re-schedule, we need to reuse the same instance, so keep it in final.
   1055     private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo;
   1056 
   1057     private void scheduleSaveInner(@UserIdInt int userId) {
   1058         if (DEBUG) {
   1059             Slog.d(TAG, "Scheduling to save for " + userId);
   1060         }
   1061         synchronized (mLock) {
   1062             if (!mDirtyUserIds.contains(userId)) {
   1063                 mDirtyUserIds.add(userId);
   1064             }
   1065         }
   1066         // If already scheduled, remove that and re-schedule in N seconds.
   1067         mHandler.removeCallbacks(mSaveDirtyInfoRunner);
   1068         mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis);
   1069     }
   1070 
   1071     @VisibleForTesting
   1072     void saveDirtyInfo() {
   1073         if (DEBUG) {
   1074             Slog.d(TAG, "saveDirtyInfo");
   1075         }
   1076         try {
   1077             synchronized (mLock) {
   1078                 for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
   1079                     final int userId = mDirtyUserIds.get(i);
   1080                     if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
   1081                         saveBaseStateLocked();
   1082                     } else {
   1083                         saveUserLocked(userId);
   1084                     }
   1085                 }
   1086                 mDirtyUserIds.clear();
   1087             }
   1088         } catch (Exception e) {
   1089             wtf("Exception in saveDirtyInfo", e);
   1090         }
   1091     }
   1092 
   1093     /** Return the last reset time. */
   1094     long getLastResetTimeLocked() {
   1095         updateTimesLocked();
   1096         return mRawLastResetTime;
   1097     }
   1098 
   1099     /** Return the next reset time. */
   1100     long getNextResetTimeLocked() {
   1101         updateTimesLocked();
   1102         return mRawLastResetTime + mResetInterval;
   1103     }
   1104 
   1105     static boolean isClockValid(long time) {
   1106         return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT
   1107     }
   1108 
   1109     /**
   1110      * Update the last reset time.
   1111      */
   1112     private void updateTimesLocked() {
   1113 
   1114         final long now = injectCurrentTimeMillis();
   1115 
   1116         final long prevLastResetTime = mRawLastResetTime;
   1117 
   1118         if (mRawLastResetTime == 0) { // first launch.
   1119             // TODO Randomize??
   1120             mRawLastResetTime = now;
   1121         } else if (now < mRawLastResetTime) {
   1122             // Clock rewound.
   1123             if (isClockValid(now)) {
   1124                 Slog.w(TAG, "Clock rewound");
   1125                 // TODO Randomize??
   1126                 mRawLastResetTime = now;
   1127             }
   1128         } else {
   1129             if ((mRawLastResetTime + mResetInterval) <= now) {
   1130                 final long offset = mRawLastResetTime % mResetInterval;
   1131                 mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
   1132             }
   1133         }
   1134         if (prevLastResetTime != mRawLastResetTime) {
   1135             scheduleSaveBaseState();
   1136         }
   1137     }
   1138 
   1139     // Requires mLock held, but "Locked" prefix would look weired so we just say "L".
   1140     protected boolean isUserUnlockedL(@UserIdInt int userId) {
   1141         // First, check the local copy.
   1142         if (mUnlockedUsers.get(userId)) {
   1143             return true;
   1144         }
   1145         // If the local copy says the user is locked, check with AM for the actual state, since
   1146         // the user might just have been unlocked.
   1147         // Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
   1148         // when the user is STOPPING, which we still want to consider as "unlocked".
   1149         final long token = injectClearCallingIdentity();
   1150         try {
   1151             return mUserManager.isUserUnlockingOrUnlocked(userId);
   1152         } finally {
   1153             injectRestoreCallingIdentity(token);
   1154         }
   1155     }
   1156 
   1157     // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L".
   1158     void throwIfUserLockedL(@UserIdInt int userId) {
   1159         if (!isUserUnlockedL(userId)) {
   1160             throw new IllegalStateException("User " + userId + " is locked or not running");
   1161         }
   1162     }
   1163 
   1164     @GuardedBy("mLock")
   1165     @NonNull
   1166     private boolean isUserLoadedLocked(@UserIdInt int userId) {
   1167         return mUsers.get(userId) != null;
   1168     }
   1169 
   1170     /** Return the per-user state. */
   1171     @GuardedBy("mLock")
   1172     @NonNull
   1173     ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
   1174         if (!isUserUnlockedL(userId)) {
   1175             wtf("User still locked");
   1176         }
   1177 
   1178         ShortcutUser userPackages = mUsers.get(userId);
   1179         if (userPackages == null) {
   1180             userPackages = loadUserLocked(userId);
   1181             if (userPackages == null) {
   1182                 userPackages = new ShortcutUser(this, userId);
   1183             }
   1184             mUsers.put(userId, userPackages);
   1185 
   1186             // Also when a user's data is first accessed, scan all packages.
   1187             checkPackageChanges(userId);
   1188         }
   1189         return userPackages;
   1190     }
   1191 
   1192     void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
   1193         for (int i = mUsers.size() - 1; i >= 0; i--) {
   1194             c.accept(mUsers.valueAt(i));
   1195         }
   1196     }
   1197 
   1198     /**
   1199      * Return the per-user per-package state.  If the caller is a publisher, use
   1200      * {@link #getPackageShortcutsForPublisherLocked} instead.
   1201      */
   1202     @GuardedBy("mLock")
   1203     @NonNull
   1204     ShortcutPackage getPackageShortcutsLocked(
   1205             @NonNull String packageName, @UserIdInt int userId) {
   1206         return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
   1207     }
   1208 
   1209     /** Return the per-user per-package state.  Use this when the caller is a publisher. */
   1210     @GuardedBy("mLock")
   1211     @NonNull
   1212     ShortcutPackage getPackageShortcutsForPublisherLocked(
   1213             @NonNull String packageName, @UserIdInt int userId) {
   1214         final ShortcutPackage ret = getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
   1215         ret.getUser().onCalledByPublisher(packageName);
   1216         return ret;
   1217     }
   1218 
   1219     @GuardedBy("mLock")
   1220     @NonNull
   1221     ShortcutLauncher getLauncherShortcutsLocked(
   1222             @NonNull String packageName, @UserIdInt int ownerUserId,
   1223             @UserIdInt int launcherUserId) {
   1224         return getUserShortcutsLocked(ownerUserId)
   1225                 .getLauncherShortcuts(packageName, launcherUserId);
   1226     }
   1227 
   1228     // === Caller validation ===
   1229 
   1230     void removeIconLocked(ShortcutInfo shortcut) {
   1231         mShortcutBitmapSaver.removeIcon(shortcut);
   1232     }
   1233 
   1234     public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) {
   1235         final File packagePath = new File(getUserBitmapFilePath(userId), packageName);
   1236         if (!packagePath.isDirectory()) {
   1237             return;
   1238         }
   1239         if (!(FileUtils.deleteContents(packagePath) && packagePath.delete())) {
   1240             Slog.w(TAG, "Unable to remove directory " + packagePath);
   1241         }
   1242     }
   1243 
   1244     /**
   1245      * Remove dangling bitmap files for a user.
   1246      *
   1247      * Note this method must be called with the lock held after calling
   1248      * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap
   1249      * saves are going on.
   1250      */
   1251     private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) {
   1252         if (DEBUG) {
   1253             Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
   1254         }
   1255         final long start = injectElapsedRealtime();
   1256 
   1257         final ShortcutUser user = getUserShortcutsLocked(userId);
   1258 
   1259         final File bitmapDir = getUserBitmapFilePath(userId);
   1260         final File[] children = bitmapDir.listFiles();
   1261         if (children == null) {
   1262             return;
   1263         }
   1264         for (File child : children) {
   1265             if (!child.isDirectory()) {
   1266                 continue;
   1267             }
   1268             final String packageName = child.getName();
   1269             if (DEBUG) {
   1270                 Slog.d(TAG, "cleanupDanglingBitmaps: Found directory=" + packageName);
   1271             }
   1272             if (!user.hasPackage(packageName)) {
   1273                 if (DEBUG) {
   1274                     Slog.d(TAG, "Removing dangling bitmap directory: " + packageName);
   1275                 }
   1276                 cleanupBitmapsForPackage(userId, packageName);
   1277             } else {
   1278                 cleanupDanglingBitmapFilesLocked(userId, user, packageName, child);
   1279             }
   1280         }
   1281         logDurationStat(Stats.CLEANUP_DANGLING_BITMAPS, start);
   1282     }
   1283 
   1284     /**
   1285      * Remove dangling bitmap files for a package.
   1286      *
   1287      * Note this method must be called with the lock held after calling
   1288      * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap
   1289      * saves are going on.
   1290      */
   1291     private void cleanupDanglingBitmapFilesLocked(@UserIdInt int userId, @NonNull ShortcutUser user,
   1292             @NonNull String packageName, @NonNull File path) {
   1293         final ArraySet<String> usedFiles =
   1294                 user.getPackageShortcuts(packageName).getUsedBitmapFiles();
   1295 
   1296         for (File child : path.listFiles()) {
   1297             if (!child.isFile()) {
   1298                 continue;
   1299             }
   1300             final String name = child.getName();
   1301             if (!usedFiles.contains(name)) {
   1302                 if (DEBUG) {
   1303                     Slog.d(TAG, "Removing dangling bitmap file: " + child.getAbsolutePath());
   1304                 }
   1305                 child.delete();
   1306             }
   1307         }
   1308     }
   1309 
   1310     @VisibleForTesting
   1311     static class FileOutputStreamWithPath extends FileOutputStream {
   1312         private final File mFile;
   1313 
   1314         public FileOutputStreamWithPath(File file) throws FileNotFoundException {
   1315             super(file);
   1316             mFile = file;
   1317         }
   1318 
   1319         public File getFile() {
   1320             return mFile;
   1321         }
   1322     }
   1323 
   1324     /**
   1325      * Build the cached bitmap filename for a shortcut icon.
   1326      *
   1327      * The filename will be based on the ID, except certain characters will be escaped.
   1328      */
   1329     FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
   1330             throws IOException {
   1331         final File packagePath = new File(getUserBitmapFilePath(userId),
   1332                 shortcut.getPackage());
   1333         if (!packagePath.isDirectory()) {
   1334             packagePath.mkdirs();
   1335             if (!packagePath.isDirectory()) {
   1336                 throw new IOException("Unable to create directory " + packagePath);
   1337             }
   1338             SELinux.restorecon(packagePath);
   1339         }
   1340 
   1341         final String baseName = String.valueOf(injectCurrentTimeMillis());
   1342         for (int suffix = 0; ; suffix++) {
   1343             final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
   1344             final File file = new File(packagePath, filename);
   1345             if (!file.exists()) {
   1346                 if (DEBUG) {
   1347                     Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
   1348                 }
   1349                 return new FileOutputStreamWithPath(file);
   1350             }
   1351         }
   1352     }
   1353 
   1354     void saveIconAndFixUpShortcutLocked(ShortcutInfo shortcut) {
   1355         if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
   1356             return;
   1357         }
   1358 
   1359         final long token = injectClearCallingIdentity();
   1360         try {
   1361             // Clear icon info on the shortcut.
   1362             removeIconLocked(shortcut);
   1363 
   1364             final Icon icon = shortcut.getIcon();
   1365             if (icon == null) {
   1366                 return; // has no icon
   1367             }
   1368             int maxIconDimension = mMaxIconDimension;
   1369             Bitmap bitmap;
   1370             try {
   1371                 switch (icon.getType()) {
   1372                     case Icon.TYPE_RESOURCE: {
   1373                         injectValidateIconResPackage(shortcut, icon);
   1374 
   1375                         shortcut.setIconResourceId(icon.getResId());
   1376                         shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
   1377                         return;
   1378                     }
   1379                     case Icon.TYPE_BITMAP:
   1380                         bitmap = icon.getBitmap(); // Don't recycle in this case.
   1381                         break;
   1382                     case Icon.TYPE_ADAPTIVE_BITMAP: {
   1383                         bitmap = icon.getBitmap(); // Don't recycle in this case.
   1384                         maxIconDimension *= (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
   1385                         break;
   1386                     }
   1387                     default:
   1388                         // This shouldn't happen because we've already validated the icon, but
   1389                         // just in case.
   1390                         throw ShortcutInfo.getInvalidIconException();
   1391                 }
   1392                 mShortcutBitmapSaver.saveBitmapLocked(shortcut,
   1393                         maxIconDimension, mIconPersistFormat, mIconPersistQuality);
   1394             } finally {
   1395                 // Once saved, we won't use the original icon information, so null it out.
   1396                 shortcut.clearIcon();
   1397             }
   1398         } finally {
   1399             injectRestoreCallingIdentity(token);
   1400         }
   1401     }
   1402 
   1403     // Unfortunately we can't do this check in unit tests because we fake creator package names,
   1404     // so override in unit tests.
   1405     // TODO CTS this case.
   1406     void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
   1407         if (!shortcut.getPackage().equals(icon.getResPackage())) {
   1408             throw new IllegalArgumentException(
   1409                     "Icon resource must reside in shortcut owner package");
   1410         }
   1411     }
   1412 
   1413     static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
   1414         // Original width/height.
   1415         final int ow = in.getWidth();
   1416         final int oh = in.getHeight();
   1417         if ((ow <= maxSize) && (oh <= maxSize)) {
   1418             if (DEBUG) {
   1419                 Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
   1420             }
   1421             return in;
   1422         }
   1423         final int longerDimension = Math.max(ow, oh);
   1424 
   1425         // New width and height.
   1426         final int nw = ow * maxSize / longerDimension;
   1427         final int nh = oh * maxSize / longerDimension;
   1428         if (DEBUG) {
   1429             Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
   1430                     ow, oh, nw, nh));
   1431         }
   1432 
   1433         final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
   1434         final Canvas c = new Canvas(scaledBitmap);
   1435 
   1436         final RectF dst = new RectF(0, 0, nw, nh);
   1437 
   1438         c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
   1439 
   1440         return scaledBitmap;
   1441     }
   1442 
   1443     /**
   1444      * For a shortcut, update all resource names from resource IDs, and also update all
   1445      * resource-based strings.
   1446      */
   1447     void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
   1448         final Resources publisherRes = injectGetResourcesForApplicationAsUser(
   1449                 si.getPackage(), si.getUserId());
   1450         if (publisherRes != null) {
   1451             final long start = injectElapsedRealtime();
   1452             try {
   1453                 si.lookupAndFillInResourceNames(publisherRes);
   1454             } finally {
   1455                 logDurationStat(Stats.RESOURCE_NAME_LOOKUP, start);
   1456             }
   1457             si.resolveResourceStrings(publisherRes);
   1458         }
   1459     }
   1460 
   1461     // === Caller validation ===
   1462 
   1463     private boolean isCallerSystem() {
   1464         final int callingUid = injectBinderCallingUid();
   1465         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
   1466     }
   1467 
   1468     private boolean isCallerShell() {
   1469         final int callingUid = injectBinderCallingUid();
   1470         return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
   1471     }
   1472 
   1473     private void enforceSystemOrShell() {
   1474         if (!(isCallerSystem() || isCallerShell())) {
   1475             throw new SecurityException("Caller must be system or shell");
   1476         }
   1477     }
   1478 
   1479     private void enforceShell() {
   1480         if (!isCallerShell()) {
   1481             throw new SecurityException("Caller must be shell");
   1482         }
   1483     }
   1484 
   1485     private void enforceSystem() {
   1486         if (!isCallerSystem()) {
   1487             throw new SecurityException("Caller must be system");
   1488         }
   1489     }
   1490 
   1491     private void enforceResetThrottlingPermission() {
   1492         if (isCallerSystem()) {
   1493             return;
   1494         }
   1495         enforceCallingOrSelfPermission(
   1496                 android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null);
   1497     }
   1498 
   1499     private void enforceCallingOrSelfPermission(
   1500             @NonNull String permission, @Nullable String message) {
   1501         if (isCallerSystem()) {
   1502             return;
   1503         }
   1504         injectEnforceCallingPermission(permission, message);
   1505     }
   1506 
   1507     /**
   1508      * Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse
   1509      * mockito.  So instead we extracted it here and override it in the tests.
   1510      */
   1511     @VisibleForTesting
   1512     void injectEnforceCallingPermission(
   1513             @NonNull String permission, @Nullable String message) {
   1514         mContext.enforceCallingPermission(permission, message);
   1515     }
   1516 
   1517     private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
   1518         Preconditions.checkStringNotEmpty(packageName, "packageName");
   1519 
   1520         if (isCallerSystem()) {
   1521             return; // no check
   1522         }
   1523 
   1524         final int callingUid = injectBinderCallingUid();
   1525 
   1526         // Otherwise, make sure the arguments are valid.
   1527         if (UserHandle.getUserId(callingUid) != userId) {
   1528             throw new SecurityException("Invalid user-ID");
   1529         }
   1530         if (injectGetPackageUid(packageName, userId) != callingUid) {
   1531             throw new SecurityException("Calling package name mismatch");
   1532         }
   1533         Preconditions.checkState(!isEphemeralApp(packageName, userId),
   1534                 "Ephemeral apps can't use ShortcutManager");
   1535     }
   1536 
   1537     // Overridden in unit tests to execute r synchronously.
   1538     void injectPostToHandler(Runnable r) {
   1539         mHandler.post(r);
   1540     }
   1541 
   1542     void injectRunOnNewThread(Runnable r) {
   1543         new Thread(r).start();
   1544     }
   1545 
   1546     /**
   1547      * @throws IllegalArgumentException if {@code numShortcuts} is bigger than
   1548      *                                  {@link #getMaxActivityShortcuts()}.
   1549      */
   1550     void enforceMaxActivityShortcuts(int numShortcuts) {
   1551         if (numShortcuts > mMaxShortcuts) {
   1552             throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
   1553         }
   1554     }
   1555 
   1556     /**
   1557      * Return the max number of dynamic + manifest shortcuts for each launcher icon.
   1558      */
   1559     int getMaxActivityShortcuts() {
   1560         return mMaxShortcuts;
   1561     }
   1562 
   1563     /**
   1564      * - Sends a notification to LauncherApps
   1565      * - Write to file
   1566      */
   1567     void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) {
   1568         notifyListeners(packageName, userId);
   1569         scheduleSaveUser(userId);
   1570     }
   1571 
   1572     private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
   1573         if (DEBUG) {
   1574             Slog.d(TAG, String.format(
   1575                     "Shortcut changes: package=%s, user=%d", packageName, userId));
   1576         }
   1577         injectPostToHandler(() -> {
   1578             try {
   1579                 final ArrayList<ShortcutChangeListener> copy;
   1580                 synchronized (mLock) {
   1581                     if (!isUserUnlockedL(userId)) {
   1582                         return;
   1583                     }
   1584 
   1585                     copy = new ArrayList<>(mListeners);
   1586                 }
   1587                 // Note onShortcutChanged() needs to be called with the system service permissions.
   1588                 for (int i = copy.size() - 1; i >= 0; i--) {
   1589                     copy.get(i).onShortcutChanged(packageName, userId);
   1590                 }
   1591             } catch (Exception ignore) {
   1592             }
   1593         });
   1594     }
   1595 
   1596     /**
   1597      * Clean up / validate an incoming shortcut.
   1598      * - Make sure all mandatory fields are set.
   1599      * - Make sure the intent's extras are persistable, and them to set
   1600      * {@link ShortcutInfo#mIntentPersistableExtrases}.  Also clear its extras.
   1601      * - Clear flags.
   1602      */
   1603     private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate,
   1604             boolean forPinRequest) {
   1605         if (shortcut.isReturnedByServer()) {
   1606             Log.w(TAG,
   1607                     "Re-publishing ShortcutInfo returned by server is not supported."
   1608                     + " Some information such as icon may lost from shortcut.");
   1609         }
   1610         Preconditions.checkNotNull(shortcut, "Null shortcut detected");
   1611         if (shortcut.getActivity() != null) {
   1612             Preconditions.checkState(
   1613                     shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
   1614                     "Cannot publish shortcut: activity " + shortcut.getActivity() + " does not"
   1615                     + " belong to package " + shortcut.getPackage());
   1616             Preconditions.checkState(
   1617                     injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
   1618                     "Cannot publish shortcut: activity " + shortcut.getActivity() + " is not"
   1619                             + " main activity");
   1620         }
   1621 
   1622         if (!forUpdate) {
   1623             shortcut.enforceMandatoryFields(/* forPinned= */ forPinRequest);
   1624             if (!forPinRequest) {
   1625                 Preconditions.checkState(shortcut.getActivity() != null,
   1626                         "Cannot publish shortcut: target activity is not set");
   1627             }
   1628         }
   1629         if (shortcut.getIcon() != null) {
   1630             ShortcutInfo.validateIcon(shortcut.getIcon());
   1631         }
   1632 
   1633         shortcut.replaceFlags(0);
   1634     }
   1635 
   1636     private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
   1637         fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false);
   1638     }
   1639 
   1640     public void validateShortcutForPinRequest(@NonNull ShortcutInfo shortcut) {
   1641         fixUpIncomingShortcutInfo(shortcut, /* forUpdate= */ false, /*forPinRequest=*/ true);
   1642     }
   1643 
   1644     /**
   1645      * When a shortcut has no target activity, set the default one from the package.
   1646      */
   1647     private void fillInDefaultActivity(List<ShortcutInfo> shortcuts) {
   1648         ComponentName defaultActivity = null;
   1649         for (int i = shortcuts.size() - 1; i >= 0; i--) {
   1650             final ShortcutInfo si = shortcuts.get(i);
   1651             if (si.getActivity() == null) {
   1652                 if (defaultActivity == null) {
   1653                     defaultActivity = injectGetDefaultMainActivity(
   1654                             si.getPackage(), si.getUserId());
   1655                     Preconditions.checkState(defaultActivity != null,
   1656                             "Launcher activity not found for package " + si.getPackage());
   1657                 }
   1658                 si.setActivity(defaultActivity);
   1659             }
   1660         }
   1661     }
   1662 
   1663     private void assignImplicitRanks(List<ShortcutInfo> shortcuts) {
   1664         for (int i = shortcuts.size() - 1; i >= 0; i--) {
   1665             shortcuts.get(i).setImplicitRank(i);
   1666         }
   1667     }
   1668 
   1669     private List<ShortcutInfo> setReturnedByServer(List<ShortcutInfo> shortcuts) {
   1670         for (int i = shortcuts.size() - 1; i >= 0; i--) {
   1671             shortcuts.get(i).setReturnedByServer();
   1672         }
   1673         return shortcuts;
   1674     }
   1675 
   1676     // === APIs ===
   1677 
   1678     @Override
   1679     public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
   1680             @UserIdInt int userId) {
   1681         verifyCaller(packageName, userId);
   1682 
   1683         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
   1684         final int size = newShortcuts.size();
   1685 
   1686         synchronized (mLock) {
   1687             throwIfUserLockedL(userId);
   1688 
   1689             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   1690 
   1691             ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
   1692 
   1693             fillInDefaultActivity(newShortcuts);
   1694 
   1695             ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
   1696 
   1697             // Throttling.
   1698             if (!ps.tryApiCall()) {
   1699                 return false;
   1700             }
   1701 
   1702             // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
   1703             ps.clearAllImplicitRanks();
   1704             assignImplicitRanks(newShortcuts);
   1705 
   1706             for (int i = 0; i < size; i++) {
   1707                 fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
   1708             }
   1709 
   1710             // First, remove all un-pinned; dynamic shortcuts
   1711             ps.deleteAllDynamicShortcuts();
   1712 
   1713             // Then, add/update all.  We need to make sure to take over "pinned" flag.
   1714             for (int i = 0; i < size; i++) {
   1715                 final ShortcutInfo newShortcut = newShortcuts.get(i);
   1716                 ps.addOrUpdateDynamicShortcut(newShortcut);
   1717             }
   1718 
   1719             // Lastly, adjust the ranks.
   1720             ps.adjustRanks();
   1721         }
   1722         packageShortcutsChanged(packageName, userId);
   1723 
   1724         verifyStates();
   1725 
   1726         return true;
   1727     }
   1728 
   1729     @Override
   1730     public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
   1731             @UserIdInt int userId) {
   1732         verifyCaller(packageName, userId);
   1733 
   1734         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
   1735         final int size = newShortcuts.size();
   1736 
   1737         synchronized (mLock) {
   1738             throwIfUserLockedL(userId);
   1739 
   1740             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   1741 
   1742             ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
   1743 
   1744             // For update, don't fill in the default activity.  Having null activity means
   1745             // "don't update the activity" here.
   1746 
   1747             ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
   1748 
   1749             // Throttling.
   1750             if (!ps.tryApiCall()) {
   1751                 return false;
   1752             }
   1753 
   1754             // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
   1755             ps.clearAllImplicitRanks();
   1756             assignImplicitRanks(newShortcuts);
   1757 
   1758             for (int i = 0; i < size; i++) {
   1759                 final ShortcutInfo source = newShortcuts.get(i);
   1760                 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
   1761 
   1762                 final ShortcutInfo target = ps.findShortcutById(source.getId());
   1763                 if (target == null) {
   1764                     continue;
   1765                 }
   1766 
   1767                 if (target.isEnabled() != source.isEnabled()) {
   1768                     Slog.w(TAG,
   1769                             "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
   1770                 }
   1771 
   1772                 // When updating the rank, we need to insert between existing ranks, so set
   1773                 // this setRankChanged, and also copy the implicit rank fo adjustRanks().
   1774                 if (source.hasRank()) {
   1775                     target.setRankChanged();
   1776                     target.setImplicitRank(source.getImplicitRank());
   1777                 }
   1778 
   1779                 final boolean replacingIcon = (source.getIcon() != null);
   1780                 if (replacingIcon) {
   1781                     removeIconLocked(target);
   1782                 }
   1783 
   1784                 // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
   1785                 target.copyNonNullFieldsFrom(source);
   1786                 target.setTimestamp(injectCurrentTimeMillis());
   1787 
   1788                 if (replacingIcon) {
   1789                     saveIconAndFixUpShortcutLocked(target);
   1790                 }
   1791 
   1792                 // When we're updating any resource related fields, re-extract the res names and
   1793                 // the values.
   1794                 if (replacingIcon || source.hasStringResources()) {
   1795                     fixUpShortcutResourceNamesAndValues(target);
   1796                 }
   1797             }
   1798 
   1799             // Lastly, adjust the ranks.
   1800             ps.adjustRanks();
   1801         }
   1802         packageShortcutsChanged(packageName, userId);
   1803 
   1804         verifyStates();
   1805 
   1806         return true;
   1807     }
   1808 
   1809     @Override
   1810     public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
   1811             @UserIdInt int userId) {
   1812         verifyCaller(packageName, userId);
   1813 
   1814         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
   1815         final int size = newShortcuts.size();
   1816 
   1817         synchronized (mLock) {
   1818             throwIfUserLockedL(userId);
   1819 
   1820             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   1821 
   1822             ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
   1823 
   1824             fillInDefaultActivity(newShortcuts);
   1825 
   1826             ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
   1827 
   1828             // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
   1829             ps.clearAllImplicitRanks();
   1830             assignImplicitRanks(newShortcuts);
   1831 
   1832             // Throttling.
   1833             if (!ps.tryApiCall()) {
   1834                 return false;
   1835             }
   1836             for (int i = 0; i < size; i++) {
   1837                 final ShortcutInfo newShortcut = newShortcuts.get(i);
   1838 
   1839                 // Validate the shortcut.
   1840                 fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
   1841 
   1842                 // When ranks are changing, we need to insert between ranks, so set the
   1843                 // "rank changed" flag.
   1844                 newShortcut.setRankChanged();
   1845 
   1846                 // Add it.
   1847                 ps.addOrUpdateDynamicShortcut(newShortcut);
   1848             }
   1849 
   1850             // Lastly, adjust the ranks.
   1851             ps.adjustRanks();
   1852         }
   1853         packageShortcutsChanged(packageName, userId);
   1854 
   1855         verifyStates();
   1856 
   1857         return true;
   1858     }
   1859 
   1860     @Override
   1861     public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
   1862             IntentSender resultIntent, int userId) {
   1863         Preconditions.checkNotNull(shortcut);
   1864         Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
   1865         return requestPinItem(packageName, userId, shortcut, null, null, resultIntent);
   1866     }
   1867 
   1868     @Override
   1869     public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)
   1870             throws RemoteException {
   1871         Preconditions.checkNotNull(shortcut);
   1872         Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
   1873         verifyCaller(packageName, userId);
   1874 
   1875         final Intent ret;
   1876         synchronized (mLock) {
   1877             throwIfUserLockedL(userId);
   1878 
   1879             // Send request to the launcher, if supported.
   1880             ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
   1881         }
   1882 
   1883         verifyStates();
   1884         return ret;
   1885     }
   1886 
   1887     /**
   1888      * Handles {@link #requestPinShortcut} and {@link ShortcutServiceInternal#requestPinAppWidget}.
   1889      * After validating the caller, it passes the request to {@link #mShortcutRequestPinProcessor}.
   1890      * Either {@param shortcut} or {@param appWidget} should be non-null.
   1891      */
   1892     private boolean requestPinItem(String packageName, int userId, ShortcutInfo shortcut,
   1893             AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent) {
   1894         verifyCaller(packageName, userId);
   1895 
   1896         final boolean ret;
   1897         synchronized (mLock) {
   1898             throwIfUserLockedL(userId);
   1899 
   1900             Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()),
   1901                     "Calling application must have a foreground activity or a foreground service");
   1902 
   1903             // Send request to the launcher, if supported.
   1904             ret = mShortcutRequestPinProcessor.requestPinItemLocked(shortcut, appWidget, extras,
   1905                     userId, resultIntent);
   1906         }
   1907 
   1908         verifyStates();
   1909 
   1910         return ret;
   1911     }
   1912 
   1913     @Override
   1914     public void disableShortcuts(String packageName, List shortcutIds,
   1915             CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
   1916         verifyCaller(packageName, userId);
   1917         Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
   1918 
   1919         synchronized (mLock) {
   1920             throwIfUserLockedL(userId);
   1921 
   1922             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   1923 
   1924             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
   1925 
   1926             final String disabledMessageString =
   1927                     (disabledMessage == null) ? null : disabledMessage.toString();
   1928 
   1929             for (int i = shortcutIds.size() - 1; i >= 0; i--) {
   1930                 ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)),
   1931                         disabledMessageString, disabledMessageResId,
   1932                         /* overrideImmutable=*/ false);
   1933             }
   1934 
   1935             // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
   1936             ps.adjustRanks();
   1937         }
   1938         packageShortcutsChanged(packageName, userId);
   1939 
   1940         verifyStates();
   1941     }
   1942 
   1943     @Override
   1944     public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
   1945         verifyCaller(packageName, userId);
   1946         Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
   1947 
   1948         synchronized (mLock) {
   1949             throwIfUserLockedL(userId);
   1950 
   1951             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   1952 
   1953             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
   1954 
   1955             for (int i = shortcutIds.size() - 1; i >= 0; i--) {
   1956                 ps.enableWithId((String) shortcutIds.get(i));
   1957             }
   1958         }
   1959         packageShortcutsChanged(packageName, userId);
   1960 
   1961         verifyStates();
   1962     }
   1963 
   1964     @Override
   1965     public void removeDynamicShortcuts(String packageName, List shortcutIds,
   1966             @UserIdInt int userId) {
   1967         verifyCaller(packageName, userId);
   1968         Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
   1969 
   1970         synchronized (mLock) {
   1971             throwIfUserLockedL(userId);
   1972 
   1973             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   1974 
   1975             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
   1976 
   1977             for (int i = shortcutIds.size() - 1; i >= 0; i--) {
   1978                 ps.deleteDynamicWithId(
   1979                         Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
   1980             }
   1981 
   1982             // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
   1983             ps.adjustRanks();
   1984         }
   1985         packageShortcutsChanged(packageName, userId);
   1986 
   1987         verifyStates();
   1988     }
   1989 
   1990     @Override
   1991     public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
   1992         verifyCaller(packageName, userId);
   1993 
   1994         synchronized (mLock) {
   1995             throwIfUserLockedL(userId);
   1996 
   1997             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   1998             ps.deleteAllDynamicShortcuts();
   1999         }
   2000         packageShortcutsChanged(packageName, userId);
   2001 
   2002         verifyStates();
   2003     }
   2004 
   2005     @Override
   2006     public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
   2007             @UserIdInt int userId) {
   2008         verifyCaller(packageName, userId);
   2009 
   2010         synchronized (mLock) {
   2011             throwIfUserLockedL(userId);
   2012 
   2013             return getShortcutsWithQueryLocked(
   2014                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
   2015                     ShortcutInfo::isDynamic);
   2016         }
   2017     }
   2018 
   2019     @Override
   2020     public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
   2021             @UserIdInt int userId) {
   2022         verifyCaller(packageName, userId);
   2023 
   2024         synchronized (mLock) {
   2025             throwIfUserLockedL(userId);
   2026 
   2027             return getShortcutsWithQueryLocked(
   2028                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
   2029                     ShortcutInfo::isManifestShortcut);
   2030         }
   2031     }
   2032 
   2033     @Override
   2034     public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
   2035             @UserIdInt int userId) {
   2036         verifyCaller(packageName, userId);
   2037 
   2038         synchronized (mLock) {
   2039             throwIfUserLockedL(userId);
   2040 
   2041             return getShortcutsWithQueryLocked(
   2042                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
   2043                     ShortcutInfo::isPinned);
   2044         }
   2045     }
   2046 
   2047     private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
   2048             @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
   2049 
   2050         final ArrayList<ShortcutInfo> ret = new ArrayList<>();
   2051 
   2052         final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   2053         ps.findAll(ret, query, cloneFlags);
   2054 
   2055         return new ParceledListSlice<>(setReturnedByServer(ret));
   2056     }
   2057 
   2058     @Override
   2059     public int getMaxShortcutCountPerActivity(String packageName, @UserIdInt int userId)
   2060             throws RemoteException {
   2061         verifyCaller(packageName, userId);
   2062 
   2063         return mMaxShortcuts;
   2064     }
   2065 
   2066     @Override
   2067     public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
   2068         verifyCaller(packageName, userId);
   2069 
   2070         synchronized (mLock) {
   2071             throwIfUserLockedL(userId);
   2072 
   2073             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   2074             return mMaxUpdatesPerInterval - ps.getApiCallCount();
   2075         }
   2076     }
   2077 
   2078     @Override
   2079     public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
   2080         verifyCaller(packageName, userId);
   2081 
   2082         synchronized (mLock) {
   2083             throwIfUserLockedL(userId);
   2084 
   2085             return getNextResetTimeLocked();
   2086         }
   2087     }
   2088 
   2089     @Override
   2090     public int getIconMaxDimensions(String packageName, int userId) {
   2091         verifyCaller(packageName, userId);
   2092 
   2093         synchronized (mLock) {
   2094             return mMaxIconDimension;
   2095         }
   2096     }
   2097 
   2098     @Override
   2099     public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
   2100         verifyCaller(packageName, userId);
   2101 
   2102         Preconditions.checkNotNull(shortcutId);
   2103 
   2104         if (DEBUG) {
   2105             Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
   2106                     shortcutId, packageName, userId));
   2107         }
   2108 
   2109         synchronized (mLock) {
   2110             throwIfUserLockedL(userId);
   2111 
   2112             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
   2113 
   2114             if (ps.findShortcutById(shortcutId) == null) {
   2115                 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s",
   2116                         packageName, shortcutId));
   2117                 return;
   2118             }
   2119         }
   2120 
   2121         final long token = injectClearCallingIdentity();
   2122         try {
   2123             mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
   2124         } finally {
   2125             injectRestoreCallingIdentity(token);
   2126         }
   2127     }
   2128 
   2129     @Override
   2130     public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
   2131         final long token = injectClearCallingIdentity();
   2132         try {
   2133             return mShortcutRequestPinProcessor
   2134                     .isRequestPinItemSupported(callingUserId, requestType);
   2135         } finally {
   2136             injectRestoreCallingIdentity(token);
   2137         }
   2138     }
   2139 
   2140     /**
   2141      * Reset all throttling, for developer options and command line.  Only system/shell can call
   2142      * it.
   2143      */
   2144     @Override
   2145     public void resetThrottling() {
   2146         enforceSystemOrShell();
   2147 
   2148         resetThrottlingInner(getCallingUserId());
   2149     }
   2150 
   2151     void resetThrottlingInner(@UserIdInt int userId) {
   2152         synchronized (mLock) {
   2153             if (!isUserUnlockedL(userId)) {
   2154                 Log.w(TAG, "User " + userId + " is locked or not running");
   2155                 return;
   2156             }
   2157 
   2158             getUserShortcutsLocked(userId).resetThrottling();
   2159         }
   2160         scheduleSaveUser(userId);
   2161         Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
   2162     }
   2163 
   2164     void resetAllThrottlingInner() {
   2165         synchronized (mLock) {
   2166             mRawLastResetTime = injectCurrentTimeMillis();
   2167         }
   2168         scheduleSaveBaseState();
   2169         Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
   2170     }
   2171 
   2172     @Override
   2173     public void onApplicationActive(String packageName, int userId) {
   2174         if (DEBUG) {
   2175             Slog.d(TAG, "onApplicationActive: package=" + packageName + "  userid=" + userId);
   2176         }
   2177         enforceResetThrottlingPermission();
   2178 
   2179         synchronized (mLock) {
   2180             if (!isUserUnlockedL(userId)) {
   2181                 // This is called by system UI, so no need to throw.  Just ignore.
   2182                 return;
   2183             }
   2184 
   2185             getPackageShortcutsLocked(packageName, userId)
   2186                     .resetRateLimitingForCommandLineNoSaving();
   2187             saveUserLocked(userId);
   2188         }
   2189     }
   2190 
   2191     // We override this method in unit tests to do a simpler check.
   2192     boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
   2193         final long start = injectElapsedRealtime();
   2194         try {
   2195             return hasShortcutHostPermissionInner(callingPackage, userId);
   2196         } finally {
   2197             logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
   2198         }
   2199     }
   2200 
   2201     // This method is extracted so we can directly call this method from unit tests,
   2202     // even when hasShortcutPermission() is overridden.
   2203     @VisibleForTesting
   2204     boolean hasShortcutHostPermissionInner(@NonNull String packageName, int userId) {
   2205         synchronized (mLock) {
   2206             throwIfUserLockedL(userId);
   2207 
   2208             final ShortcutUser user = getUserShortcutsLocked(userId);
   2209 
   2210             // Always trust the cached component.
   2211             final ComponentName cached = user.getCachedLauncher();
   2212             if (cached != null) {
   2213                 if (cached.getPackageName().equals(packageName)) {
   2214                     return true;
   2215                 }
   2216             }
   2217             // If the cached one doesn't match, then go ahead
   2218 
   2219             final ComponentName detected = getDefaultLauncher(userId);
   2220 
   2221             // Update the cache.
   2222             user.setLauncher(detected);
   2223             if (detected != null) {
   2224                 if (DEBUG) {
   2225                     Slog.v(TAG, "Detected launcher: " + detected);
   2226                 }
   2227                 return detected.getPackageName().equals(packageName);
   2228             } else {
   2229                 // Default launcher not found.
   2230                 return false;
   2231             }
   2232         }
   2233     }
   2234 
   2235     @Nullable
   2236     ComponentName getDefaultLauncher(@UserIdInt int userId) {
   2237         final long start = injectElapsedRealtime();
   2238         final long token = injectClearCallingIdentity();
   2239         try {
   2240             synchronized (mLock) {
   2241                 throwIfUserLockedL(userId);
   2242 
   2243                 final ShortcutUser user = getUserShortcutsLocked(userId);
   2244 
   2245                 final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
   2246 
   2247                 // Default launcher from package manager.
   2248                 final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
   2249                 final ComponentName defaultLauncher = mPackageManagerInternal
   2250                         .getHomeActivitiesAsUser(allHomeCandidates, userId);
   2251                 logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
   2252 
   2253                 ComponentName detected = null;
   2254                 if (defaultLauncher != null) {
   2255                     detected = defaultLauncher;
   2256                     if (DEBUG) {
   2257                         Slog.v(TAG, "Default launcher from PM: " + detected);
   2258                     }
   2259                 } else {
   2260                     detected = user.getLastKnownLauncher();
   2261 
   2262                     if (detected != null) {
   2263                         if (injectIsActivityEnabledAndExported(detected, userId)) {
   2264                             if (DEBUG) {
   2265                                 Slog.v(TAG, "Cached launcher: " + detected);
   2266                             }
   2267                         } else {
   2268                             Slog.w(TAG, "Cached launcher " + detected + " no longer exists");
   2269                             detected = null;
   2270                             user.clearLauncher();
   2271                         }
   2272                     }
   2273                 }
   2274 
   2275                 if (detected == null) {
   2276                     // If we reach here, that means it's the first check since the user was created,
   2277                     // and there's already multiple launchers and there's no default set.
   2278                     // Find the system one with the highest priority.
   2279                     // (We need to check the priority too because of FallbackHome in Settings.)
   2280                     // If there's no system launcher yet, then no one can access shortcuts, until
   2281                     // the user explicitly
   2282                     final int size = allHomeCandidates.size();
   2283 
   2284                     int lastPriority = Integer.MIN_VALUE;
   2285                     for (int i = 0; i < size; i++) {
   2286                         final ResolveInfo ri = allHomeCandidates.get(i);
   2287                         if (!ri.activityInfo.applicationInfo.isSystemApp()) {
   2288                             continue;
   2289                         }
   2290                         if (DEBUG) {
   2291                             Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
   2292                                     ri.activityInfo.getComponentName(), ri.priority));
   2293                         }
   2294                         if (ri.priority < lastPriority) {
   2295                             continue;
   2296                         }
   2297                         detected = ri.activityInfo.getComponentName();
   2298                         lastPriority = ri.priority;
   2299                     }
   2300                 }
   2301                 return detected;
   2302             }
   2303         } finally {
   2304             injectRestoreCallingIdentity(token);
   2305             logDurationStat(Stats.GET_DEFAULT_LAUNCHER, start);
   2306         }
   2307     }
   2308 
   2309     // === House keeping ===
   2310 
   2311     private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId,
   2312             boolean appStillExists) {
   2313         synchronized (mLock) {
   2314             forEachLoadedUserLocked(user ->
   2315                     cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
   2316                             appStillExists));
   2317         }
   2318     }
   2319 
   2320     /**
   2321      * Remove all the information associated with a package.  This will really remove all the
   2322      * information, including the restore information (i.e. it'll remove packages even if they're
   2323      * shadow).
   2324      *
   2325      * This is called when an app is uninstalled, or an app gets "clear data"ed.
   2326      */
   2327     @VisibleForTesting
   2328     void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
   2329             boolean appStillExists) {
   2330         final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
   2331 
   2332         final ShortcutUser user = getUserShortcutsLocked(owningUserId);
   2333         boolean doNotify = false;
   2334 
   2335         // First, remove the package from the package list (if the package is a publisher).
   2336         if (packageUserId == owningUserId) {
   2337             if (user.removePackage(packageName) != null) {
   2338                 doNotify = true;
   2339             }
   2340         }
   2341 
   2342         // Also remove from the launcher list (if the package is a launcher).
   2343         user.removeLauncher(packageUserId, packageName);
   2344 
   2345         // Then remove pinned shortcuts from all launchers.
   2346         user.forAllLaunchers(l -> l.cleanUpPackage(packageName, packageUserId));
   2347 
   2348         // Now there may be orphan shortcuts because we removed pinned shortcuts at the previous
   2349         // step.  Remove them too.
   2350         user.forAllPackages(p -> p.refreshPinnedFlags());
   2351 
   2352         scheduleSaveUser(owningUserId);
   2353 
   2354         if (doNotify) {
   2355             notifyListeners(packageName, owningUserId);
   2356         }
   2357 
   2358         // If the app still exists (i.e. data cleared), we need to re-publish manifest shortcuts.
   2359         if (appStillExists && (packageUserId == owningUserId)) {
   2360             // This will do the notification and save when needed, so do it after the above
   2361             // notifyListeners.
   2362             user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
   2363         }
   2364 
   2365         if (!wasUserLoaded) {
   2366             // Note this will execute the scheduled save.
   2367             unloadUserLocked(owningUserId);
   2368         }
   2369     }
   2370 
   2371     /**
   2372      * Entry point from {@link LauncherApps}.
   2373      */
   2374     private class LocalService extends ShortcutServiceInternal {
   2375 
   2376         @Override
   2377         public List<ShortcutInfo> getShortcuts(int launcherUserId,
   2378                 @NonNull String callingPackage, long changedSince,
   2379                 @Nullable String packageName, @Nullable List<String> shortcutIds,
   2380                 @Nullable ComponentName componentName,
   2381                 int queryFlags, int userId) {
   2382             final ArrayList<ShortcutInfo> ret = new ArrayList<>();
   2383 
   2384             final boolean cloneKeyFieldOnly =
   2385                     ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
   2386             final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO
   2387                     : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
   2388             if (packageName == null) {
   2389                 shortcutIds = null; // LauncherAppsService already threw for it though.
   2390             }
   2391 
   2392             synchronized (mLock) {
   2393                 throwIfUserLockedL(userId);
   2394                 throwIfUserLockedL(launcherUserId);
   2395 
   2396                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
   2397                         .attemptToRestoreIfNeededAndSave();
   2398 
   2399                 if (packageName != null) {
   2400                     getShortcutsInnerLocked(launcherUserId,
   2401                             callingPackage, packageName, shortcutIds, changedSince,
   2402                             componentName, queryFlags, userId, ret, cloneFlag);
   2403                 } else {
   2404                     final List<String> shortcutIdsF = shortcutIds;
   2405                     getUserShortcutsLocked(userId).forAllPackages(p -> {
   2406                         getShortcutsInnerLocked(launcherUserId,
   2407                                 callingPackage, p.getPackageName(), shortcutIdsF, changedSince,
   2408                                 componentName, queryFlags, userId, ret, cloneFlag);
   2409                     });
   2410                 }
   2411             }
   2412             return setReturnedByServer(ret);
   2413         }
   2414 
   2415         private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
   2416                 @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
   2417                 @Nullable ComponentName componentName, int queryFlags,
   2418                 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
   2419             final ArraySet<String> ids = shortcutIds == null ? null
   2420                     : new ArraySet<>(shortcutIds);
   2421 
   2422             final ShortcutPackage p = getUserShortcutsLocked(userId)
   2423                     .getPackageShortcutsIfExists(packageName);
   2424             if (p == null) {
   2425                 return; // No need to instantiate ShortcutPackage.
   2426             }
   2427 
   2428             p.findAll(ret,
   2429                     (ShortcutInfo si) -> {
   2430                         if (si.getLastChangedTimestamp() < changedSince) {
   2431                             return false;
   2432                         }
   2433                         if (ids != null && !ids.contains(si.getId())) {
   2434                             return false;
   2435                         }
   2436                         if (componentName != null) {
   2437                             if (si.getActivity() != null
   2438                                     && !si.getActivity().equals(componentName)) {
   2439                                 return false;
   2440                             }
   2441                         }
   2442                         if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
   2443                                 && si.isDynamic()) {
   2444                             return true;
   2445                         }
   2446                         if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
   2447                                 && si.isPinned()) {
   2448                             return true;
   2449                         }
   2450                         if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0)
   2451                                 && si.isManifestShortcut()) {
   2452                             return true;
   2453                         }
   2454                         return false;
   2455                     }, cloneFlag, callingPackage, launcherUserId);
   2456         }
   2457 
   2458         @Override
   2459         public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
   2460                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
   2461             Preconditions.checkStringNotEmpty(packageName, "packageName");
   2462             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
   2463 
   2464             synchronized (mLock) {
   2465                 throwIfUserLockedL(userId);
   2466                 throwIfUserLockedL(launcherUserId);
   2467 
   2468                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
   2469                         .attemptToRestoreIfNeededAndSave();
   2470 
   2471                 final ShortcutInfo si = getShortcutInfoLocked(
   2472                         launcherUserId, callingPackage, packageName, shortcutId, userId);
   2473                 return si != null && si.isPinned();
   2474             }
   2475         }
   2476 
   2477         private ShortcutInfo getShortcutInfoLocked(
   2478                 int launcherUserId, @NonNull String callingPackage,
   2479                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
   2480             Preconditions.checkStringNotEmpty(packageName, "packageName");
   2481             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
   2482 
   2483             throwIfUserLockedL(userId);
   2484             throwIfUserLockedL(launcherUserId);
   2485 
   2486             final ShortcutPackage p = getUserShortcutsLocked(userId)
   2487                     .getPackageShortcutsIfExists(packageName);
   2488             if (p == null) {
   2489                 return null;
   2490             }
   2491 
   2492             final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
   2493             p.findAll(list,
   2494                     (ShortcutInfo si) -> shortcutId.equals(si.getId()),
   2495                     /* clone flags=*/ 0, callingPackage, launcherUserId);
   2496             return list.size() == 0 ? null : list.get(0);
   2497         }
   2498 
   2499         @Override
   2500         public void pinShortcuts(int launcherUserId,
   2501                 @NonNull String callingPackage, @NonNull String packageName,
   2502                 @NonNull List<String> shortcutIds, int userId) {
   2503             // Calling permission must be checked by LauncherAppsImpl.
   2504             Preconditions.checkStringNotEmpty(packageName, "packageName");
   2505             Preconditions.checkNotNull(shortcutIds, "shortcutIds");
   2506 
   2507             synchronized (mLock) {
   2508                 throwIfUserLockedL(userId);
   2509                 throwIfUserLockedL(launcherUserId);
   2510 
   2511                 final ShortcutLauncher launcher =
   2512                         getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
   2513                 launcher.attemptToRestoreIfNeededAndSave();
   2514 
   2515                 launcher.pinShortcuts(userId, packageName, shortcutIds);
   2516             }
   2517             packageShortcutsChanged(packageName, userId);
   2518 
   2519             verifyStates();
   2520         }
   2521 
   2522         @Override
   2523         public Intent[] createShortcutIntents(int launcherUserId,
   2524                 @NonNull String callingPackage,
   2525                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
   2526             // Calling permission must be checked by LauncherAppsImpl.
   2527             Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
   2528             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
   2529 
   2530             synchronized (mLock) {
   2531                 throwIfUserLockedL(userId);
   2532                 throwIfUserLockedL(launcherUserId);
   2533 
   2534                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
   2535                         .attemptToRestoreIfNeededAndSave();
   2536 
   2537                 // Make sure the shortcut is actually visible to the launcher.
   2538                 final ShortcutInfo si = getShortcutInfoLocked(
   2539                         launcherUserId, callingPackage, packageName, shortcutId, userId);
   2540                 // "si == null" should suffice here, but check the flags too just to make sure.
   2541                 if (si == null || !si.isEnabled() || !si.isAlive()) {
   2542                     Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
   2543                     return null;
   2544                 }
   2545                 return si.getIntents();
   2546             }
   2547         }
   2548 
   2549         @Override
   2550         public void addListener(@NonNull ShortcutChangeListener listener) {
   2551             synchronized (mLock) {
   2552                 mListeners.add(Preconditions.checkNotNull(listener));
   2553             }
   2554         }
   2555 
   2556         @Override
   2557         public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
   2558                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
   2559             Preconditions.checkNotNull(callingPackage, "callingPackage");
   2560             Preconditions.checkNotNull(packageName, "packageName");
   2561             Preconditions.checkNotNull(shortcutId, "shortcutId");
   2562 
   2563             synchronized (mLock) {
   2564                 throwIfUserLockedL(userId);
   2565                 throwIfUserLockedL(launcherUserId);
   2566 
   2567                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
   2568                         .attemptToRestoreIfNeededAndSave();
   2569 
   2570                 final ShortcutPackage p = getUserShortcutsLocked(userId)
   2571                         .getPackageShortcutsIfExists(packageName);
   2572                 if (p == null) {
   2573                     return 0;
   2574                 }
   2575 
   2576                 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
   2577                 return (shortcutInfo != null && shortcutInfo.hasIconResource())
   2578                         ? shortcutInfo.getIconResourceId() : 0;
   2579             }
   2580         }
   2581 
   2582         @Override
   2583         public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
   2584                 @NonNull String callingPackage, @NonNull String packageName,
   2585                 @NonNull String shortcutId, int userId) {
   2586             Preconditions.checkNotNull(callingPackage, "callingPackage");
   2587             Preconditions.checkNotNull(packageName, "packageName");
   2588             Preconditions.checkNotNull(shortcutId, "shortcutId");
   2589 
   2590             synchronized (mLock) {
   2591                 throwIfUserLockedL(userId);
   2592                 throwIfUserLockedL(launcherUserId);
   2593 
   2594                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
   2595                         .attemptToRestoreIfNeededAndSave();
   2596 
   2597                 final ShortcutPackage p = getUserShortcutsLocked(userId)
   2598                         .getPackageShortcutsIfExists(packageName);
   2599                 if (p == null) {
   2600                     return null;
   2601                 }
   2602 
   2603                 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
   2604                 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
   2605                     return null;
   2606                 }
   2607                 final String path = mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcutInfo);
   2608                 if (path == null) {
   2609                     Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
   2610                     return null;
   2611                 }
   2612                 try {
   2613                     return ParcelFileDescriptor.open(
   2614                             new File(path),
   2615                             ParcelFileDescriptor.MODE_READ_ONLY);
   2616                 } catch (FileNotFoundException e) {
   2617                     Slog.e(TAG, "Icon file not found: " + path);
   2618                     return null;
   2619                 }
   2620             }
   2621         }
   2622 
   2623         @Override
   2624         public boolean hasShortcutHostPermission(int launcherUserId,
   2625                 @NonNull String callingPackage) {
   2626             return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
   2627         }
   2628 
   2629         @Override
   2630         public boolean requestPinAppWidget(@NonNull String callingPackage,
   2631                 @NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras,
   2632                 @Nullable IntentSender resultIntent, int userId) {
   2633             Preconditions.checkNotNull(appWidget);
   2634             return requestPinItem(callingPackage, userId, null, appWidget, extras, resultIntent);
   2635         }
   2636 
   2637         @Override
   2638         public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
   2639             return ShortcutService.this.isRequestPinItemSupported(callingUserId, requestType);
   2640         }
   2641     }
   2642 
   2643     final BroadcastReceiver mReceiver = new BroadcastReceiver() {
   2644         @Override
   2645         public void onReceive(Context context, Intent intent) {
   2646             if (!mBootCompleted.get()) {
   2647                 return; // Boot not completed, ignore the broadcast.
   2648             }
   2649             try {
   2650                 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
   2651                     handleLocaleChanged();
   2652                 }
   2653             } catch (Exception e) {
   2654                 wtf("Exception in mReceiver.onReceive", e);
   2655             }
   2656         }
   2657     };
   2658 
   2659     void handleLocaleChanged() {
   2660         if (DEBUG) {
   2661             Slog.d(TAG, "handleLocaleChanged");
   2662         }
   2663         scheduleSaveBaseState();
   2664 
   2665         synchronized (mLock) {
   2666             final long token = injectClearCallingIdentity();
   2667             try {
   2668                 forEachLoadedUserLocked(user -> user.detectLocaleChange());
   2669             } finally {
   2670                 injectRestoreCallingIdentity(token);
   2671             }
   2672         }
   2673     }
   2674 
   2675     /**
   2676      * Package event callbacks.
   2677      */
   2678     @VisibleForTesting
   2679     final BroadcastReceiver mPackageMonitor = new BroadcastReceiver() {
   2680         @Override
   2681         public void onReceive(Context context, Intent intent) {
   2682             final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
   2683             if (userId == UserHandle.USER_NULL) {
   2684                 Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
   2685                 return;
   2686             }
   2687 
   2688             final String action = intent.getAction();
   2689 
   2690             // This is normally called on Handler, so clearCallingIdentity() isn't needed,
   2691             // but we still check it in unit tests.
   2692             final long token = injectClearCallingIdentity();
   2693             try {
   2694                 synchronized (mLock) {
   2695                     if (!isUserUnlockedL(userId)) {
   2696                         if (DEBUG) {
   2697                             Slog.d(TAG, "Ignoring package broadcast " + action
   2698                                     + " for locked/stopped user " + userId);
   2699                         }
   2700                         return;
   2701                     }
   2702 
   2703                     // Whenever we get one of those package broadcasts, or get
   2704                     // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache.
   2705                     final ShortcutUser user = getUserShortcutsLocked(userId);
   2706                     user.clearLauncher();
   2707                 }
   2708                 if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) {
   2709                     // Nothing farther to do.
   2710                     return;
   2711                 }
   2712 
   2713                 final Uri intentUri = intent.getData();
   2714                 final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart()
   2715                         : null;
   2716                 if (packageName == null) {
   2717                     Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
   2718                     return;
   2719                 }
   2720 
   2721                 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
   2722 
   2723                 switch (action) {
   2724                     case Intent.ACTION_PACKAGE_ADDED:
   2725                         if (replacing) {
   2726                             handlePackageUpdateFinished(packageName, userId);
   2727                         } else {
   2728                             handlePackageAdded(packageName, userId);
   2729                         }
   2730                         break;
   2731                     case Intent.ACTION_PACKAGE_REMOVED:
   2732                         if (!replacing) {
   2733                             handlePackageRemoved(packageName, userId);
   2734                         }
   2735                         break;
   2736                     case Intent.ACTION_PACKAGE_CHANGED:
   2737                         handlePackageChanged(packageName, userId);
   2738 
   2739                         break;
   2740                     case Intent.ACTION_PACKAGE_DATA_CLEARED:
   2741                         handlePackageDataCleared(packageName, userId);
   2742                         break;
   2743                 }
   2744             } catch (Exception e) {
   2745                 wtf("Exception in mPackageMonitor.onReceive", e);
   2746             } finally {
   2747                 injectRestoreCallingIdentity(token);
   2748             }
   2749         }
   2750     };
   2751 
   2752     /**
   2753      * Called when a user is unlocked.
   2754      * - Check all known packages still exist, and otherwise perform cleanup.
   2755      * - If a package still exists, check the version code.  If it's been updated, may need to
   2756      * update timestamps of its shortcuts.
   2757      */
   2758     @VisibleForTesting
   2759     void checkPackageChanges(@UserIdInt int ownerUserId) {
   2760         if (DEBUG) {
   2761             Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId);
   2762         }
   2763         if (injectIsSafeModeEnabled()) {
   2764             Slog.i(TAG, "Safe mode, skipping checkPackageChanges()");
   2765             return;
   2766         }
   2767 
   2768         final long start = injectElapsedRealtime();
   2769         try {
   2770             final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
   2771 
   2772             synchronized (mLock) {
   2773                 final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
   2774 
   2775                 // Find packages that have been uninstalled.
   2776                 user.forAllPackageItems(spi -> {
   2777                     if (spi.getPackageInfo().isShadow()) {
   2778                         return; // Don't delete shadow information.
   2779                     }
   2780                     if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
   2781                         if (DEBUG) {
   2782                             Slog.d(TAG, "Uninstalled: " + spi.getPackageName()
   2783                                     + " user " + spi.getPackageUserId());
   2784                         }
   2785                         gonePackages.add(PackageWithUser.of(spi));
   2786                     }
   2787                 });
   2788                 if (gonePackages.size() > 0) {
   2789                     for (int i = gonePackages.size() - 1; i >= 0; i--) {
   2790                         final PackageWithUser pu = gonePackages.get(i);
   2791                         cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId,
   2792                                 /* appStillExists = */ false);
   2793                     }
   2794                 }
   2795 
   2796                 rescanUpdatedPackagesLocked(ownerUserId, user.getLastAppScanTime());
   2797             }
   2798         } finally {
   2799             logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start);
   2800         }
   2801         verifyStates();
   2802     }
   2803 
   2804     private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
   2805         final ShortcutUser user = getUserShortcutsLocked(userId);
   2806 
   2807         // Note after each OTA, we'll need to rescan all system apps, as their lastUpdateTime
   2808         // is not reliable.
   2809         final long now = injectCurrentTimeMillis();
   2810         final boolean afterOta =
   2811                 !injectBuildFingerprint().equals(user.getLastAppScanOsFingerprint());
   2812 
   2813         // Then for each installed app, publish manifest shortcuts when needed.
   2814         forUpdatedPackages(userId, lastScanTime, afterOta, ai -> {
   2815             user.attemptToRestoreIfNeededAndSave(this, ai.packageName, userId);
   2816 
   2817             user.rescanPackageIfNeeded(ai.packageName, /* forceRescan= */ true);
   2818         });
   2819 
   2820         // Write the time just before the scan, because there may be apps that have just
   2821         // been updated, and we want to catch them in the next time.
   2822         user.setLastAppScanTime(now);
   2823         user.setLastAppScanOsFingerprint(injectBuildFingerprint());
   2824         scheduleSaveUser(userId);
   2825     }
   2826 
   2827     private void handlePackageAdded(String packageName, @UserIdInt int userId) {
   2828         if (DEBUG) {
   2829             Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
   2830         }
   2831         synchronized (mLock) {
   2832             final ShortcutUser user = getUserShortcutsLocked(userId);
   2833             user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
   2834             user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
   2835         }
   2836         verifyStates();
   2837     }
   2838 
   2839     private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
   2840         if (DEBUG) {
   2841             Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
   2842                     packageName, userId));
   2843         }
   2844         synchronized (mLock) {
   2845             final ShortcutUser user = getUserShortcutsLocked(userId);
   2846             user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
   2847 
   2848             if (isPackageInstalled(packageName, userId)) {
   2849                 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
   2850             }
   2851         }
   2852         verifyStates();
   2853     }
   2854 
   2855     private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
   2856         if (DEBUG) {
   2857             Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
   2858                     packageUserId));
   2859         }
   2860         cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false);
   2861 
   2862         verifyStates();
   2863     }
   2864 
   2865     private void handlePackageDataCleared(String packageName, int packageUserId) {
   2866         if (DEBUG) {
   2867             Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
   2868                     packageUserId));
   2869         }
   2870         cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true);
   2871 
   2872         verifyStates();
   2873     }
   2874 
   2875     private void handlePackageChanged(String packageName, int packageUserId) {
   2876         if (!isPackageInstalled(packageName, packageUserId)) {
   2877             // Probably disabled, which is the same thing as uninstalled.
   2878             handlePackageRemoved(packageName, packageUserId);
   2879             return;
   2880         }
   2881         if (DEBUG) {
   2882             Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName,
   2883                     packageUserId));
   2884         }
   2885 
   2886         // Activities may be disabled or enabled.  Just rescan the package.
   2887         synchronized (mLock) {
   2888             final ShortcutUser user = getUserShortcutsLocked(packageUserId);
   2889 
   2890             user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
   2891         }
   2892 
   2893         verifyStates();
   2894     }
   2895 
   2896     // === PackageManager interaction ===
   2897 
   2898     /**
   2899      * Returns {@link PackageInfo} unless it's uninstalled or disabled.
   2900      */
   2901     @Nullable
   2902     final PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
   2903         return getPackageInfo(packageName, userId, true);
   2904     }
   2905 
   2906     /**
   2907      * Returns {@link PackageInfo} unless it's uninstalled or disabled.
   2908      */
   2909     @Nullable
   2910     final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) {
   2911         return getPackageInfo(packageName, userId, false);
   2912     }
   2913 
   2914     int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
   2915         final long token = injectClearCallingIdentity();
   2916         try {
   2917             return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS, userId);
   2918         } catch (RemoteException e) {
   2919             // Shouldn't happen.
   2920             Slog.wtf(TAG, "RemoteException", e);
   2921             return -1;
   2922         } finally {
   2923             injectRestoreCallingIdentity(token);
   2924         }
   2925     }
   2926 
   2927     /**
   2928      * Returns {@link PackageInfo} unless it's uninstalled or disabled.
   2929      */
   2930     @Nullable
   2931     @VisibleForTesting
   2932     final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId,
   2933             boolean getSignatures) {
   2934         return isInstalledOrNull(injectPackageInfoWithUninstalled(
   2935                 packageName, userId, getSignatures));
   2936     }
   2937 
   2938     /**
   2939      * Do not use directly; this returns uninstalled packages too.
   2940      */
   2941     @Nullable
   2942     @VisibleForTesting
   2943     PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
   2944             boolean getSignatures) {
   2945         final long start = injectElapsedRealtime();
   2946         final long token = injectClearCallingIdentity();
   2947         try {
   2948             return mIPackageManager.getPackageInfo(
   2949                     packageName, PACKAGE_MATCH_FLAGS
   2950                             | (getSignatures ? PackageManager.GET_SIGNATURES : 0), userId);
   2951         } catch (RemoteException e) {
   2952             // Shouldn't happen.
   2953             Slog.wtf(TAG, "RemoteException", e);
   2954             return null;
   2955         } finally {
   2956             injectRestoreCallingIdentity(token);
   2957 
   2958             logDurationStat(
   2959                     (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO),
   2960                     start);
   2961         }
   2962     }
   2963 
   2964     /**
   2965      * Returns {@link ApplicationInfo} unless it's uninstalled or disabled.
   2966      */
   2967     @Nullable
   2968     @VisibleForTesting
   2969     final ApplicationInfo getApplicationInfo(String packageName, @UserIdInt int userId) {
   2970         return isInstalledOrNull(injectApplicationInfoWithUninstalled(packageName, userId));
   2971     }
   2972 
   2973     /**
   2974      * Do not use directly; this returns uninstalled packages too.
   2975      */
   2976     @Nullable
   2977     @VisibleForTesting
   2978     ApplicationInfo injectApplicationInfoWithUninstalled(
   2979             String packageName, @UserIdInt int userId) {
   2980         final long start = injectElapsedRealtime();
   2981         final long token = injectClearCallingIdentity();
   2982         try {
   2983             return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
   2984         } catch (RemoteException e) {
   2985             // Shouldn't happen.
   2986             Slog.wtf(TAG, "RemoteException", e);
   2987             return null;
   2988         } finally {
   2989             injectRestoreCallingIdentity(token);
   2990 
   2991             logDurationStat(Stats.GET_APPLICATION_INFO, start);
   2992         }
   2993     }
   2994 
   2995     /**
   2996      * Returns {@link ActivityInfo} with its metadata unless it's uninstalled or disabled.
   2997      */
   2998     @Nullable
   2999     final ActivityInfo getActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) {
   3000         return isInstalledOrNull(injectGetActivityInfoWithMetadataWithUninstalled(
   3001                 activity, userId));
   3002     }
   3003 
   3004     /**
   3005      * Do not use directly; this returns uninstalled packages too.
   3006      */
   3007     @Nullable
   3008     @VisibleForTesting
   3009     ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled(
   3010             ComponentName activity, @UserIdInt int userId) {
   3011         final long start = injectElapsedRealtime();
   3012         final long token = injectClearCallingIdentity();
   3013         try {
   3014             return mIPackageManager.getActivityInfo(activity,
   3015                     (PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA), userId);
   3016         } catch (RemoteException e) {
   3017             // Shouldn't happen.
   3018             Slog.wtf(TAG, "RemoteException", e);
   3019             return null;
   3020         } finally {
   3021             injectRestoreCallingIdentity(token);
   3022 
   3023             logDurationStat(Stats.GET_ACTIVITY_WITH_METADATA, start);
   3024         }
   3025     }
   3026 
   3027     /**
   3028      * Return all installed and enabled packages.
   3029      */
   3030     @NonNull
   3031     @VisibleForTesting
   3032     final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) {
   3033         final long start = injectElapsedRealtime();
   3034         final long token = injectClearCallingIdentity();
   3035         try {
   3036             final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId);
   3037 
   3038             all.removeIf(PACKAGE_NOT_INSTALLED);
   3039 
   3040             return all;
   3041         } catch (RemoteException e) {
   3042             // Shouldn't happen.
   3043             Slog.wtf(TAG, "RemoteException", e);
   3044             return null;
   3045         } finally {
   3046             injectRestoreCallingIdentity(token);
   3047 
   3048             logDurationStat(Stats.GET_INSTALLED_PACKAGES, start);
   3049         }
   3050     }
   3051 
   3052     /**
   3053      * Do not use directly; this returns uninstalled packages too.
   3054      */
   3055     @NonNull
   3056     @VisibleForTesting
   3057     List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId)
   3058             throws RemoteException {
   3059         final ParceledListSlice<PackageInfo> parceledList =
   3060                 mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId);
   3061         if (parceledList == null) {
   3062             return Collections.emptyList();
   3063         }
   3064         return parceledList.getList();
   3065     }
   3066 
   3067     private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta,
   3068             Consumer<ApplicationInfo> callback) {
   3069         if (DEBUG) {
   3070             Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime
   3071                     + " afterOta=" + afterOta);
   3072         }
   3073         final List<PackageInfo> list = getInstalledPackages(userId);
   3074         for (int i = list.size() - 1; i >= 0; i--) {
   3075             final PackageInfo pi = list.get(i);
   3076 
   3077             // If the package has been updated since the last scan time, then scan it.
   3078             // Also if it's right after an OTA, always re-scan all apps anyway, since the
   3079             // shortcut parser might have changed.
   3080             if (afterOta || (pi.lastUpdateTime >= lastScanTime)) {
   3081                 if (DEBUG) {
   3082                     Slog.d(TAG, "Found updated package " + pi.packageName
   3083                             + " updateTime=" + pi.lastUpdateTime);
   3084                 }
   3085                 callback.accept(pi.applicationInfo);
   3086             }
   3087         }
   3088     }
   3089 
   3090     private boolean isApplicationFlagSet(@NonNull String packageName, int userId, int flags) {
   3091         final ApplicationInfo ai = injectApplicationInfoWithUninstalled(packageName, userId);
   3092         return (ai != null) && ((ai.flags & flags) == flags);
   3093     }
   3094 
   3095     private static boolean isInstalled(@Nullable ApplicationInfo ai) {
   3096         return (ai != null) && ai.enabled && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
   3097     }
   3098 
   3099     private static boolean isEphemeralApp(@Nullable ApplicationInfo ai) {
   3100         return (ai != null) && ai.isInstantApp();
   3101     }
   3102 
   3103     private static boolean isInstalled(@Nullable PackageInfo pi) {
   3104         return (pi != null) && isInstalled(pi.applicationInfo);
   3105     }
   3106 
   3107     private static boolean isInstalled(@Nullable ActivityInfo ai) {
   3108         return (ai != null) && isInstalled(ai.applicationInfo);
   3109     }
   3110 
   3111     private static ApplicationInfo isInstalledOrNull(ApplicationInfo ai) {
   3112         return isInstalled(ai) ? ai : null;
   3113     }
   3114 
   3115     private static PackageInfo isInstalledOrNull(PackageInfo pi) {
   3116         return isInstalled(pi) ? pi : null;
   3117     }
   3118 
   3119     private static ActivityInfo isInstalledOrNull(ActivityInfo ai) {
   3120         return isInstalled(ai) ? ai : null;
   3121     }
   3122 
   3123     boolean isPackageInstalled(String packageName, int userId) {
   3124         return getApplicationInfo(packageName, userId) != null;
   3125     }
   3126 
   3127     boolean isEphemeralApp(String packageName, int userId) {
   3128         return isEphemeralApp(getApplicationInfo(packageName, userId));
   3129     }
   3130 
   3131     @Nullable
   3132     XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
   3133         return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
   3134     }
   3135 
   3136     @Nullable
   3137     Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
   3138         final long start = injectElapsedRealtime();
   3139         final long token = injectClearCallingIdentity();
   3140         try {
   3141             return mContext.getPackageManager().getResourcesForApplicationAsUser(
   3142                     packageName, userId);
   3143         } catch (NameNotFoundException e) {
   3144             Slog.e(TAG, "Resources for package " + packageName + " not found");
   3145             return null;
   3146         } finally {
   3147             injectRestoreCallingIdentity(token);
   3148 
   3149             logDurationStat(Stats.GET_APPLICATION_RESOURCES, start);
   3150         }
   3151     }
   3152 
   3153     private Intent getMainActivityIntent() {
   3154         final Intent intent = new Intent(Intent.ACTION_MAIN);
   3155         intent.addCategory(LAUNCHER_INTENT_CATEGORY);
   3156         return intent;
   3157     }
   3158 
   3159     /**
   3160      * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed,
   3161      * and only returns exported activities.
   3162      */
   3163     @NonNull
   3164     @VisibleForTesting
   3165     List<ResolveInfo> queryActivities(@NonNull Intent baseIntent,
   3166             @NonNull String packageName, @Nullable ComponentName activity, int userId) {
   3167 
   3168         baseIntent.setPackage(Preconditions.checkNotNull(packageName));
   3169         if (activity != null) {
   3170             baseIntent.setComponent(activity);
   3171         }
   3172         return queryActivities(baseIntent, userId, /* exportedOnly =*/ true);
   3173     }
   3174 
   3175     @NonNull
   3176     List<ResolveInfo> queryActivities(@NonNull Intent intent, int userId,
   3177             boolean exportedOnly) {
   3178         final List<ResolveInfo> resolved;
   3179         final long token = injectClearCallingIdentity();
   3180         try {
   3181             resolved =
   3182                     mContext.getPackageManager().queryIntentActivitiesAsUser(
   3183                             intent, PACKAGE_MATCH_FLAGS, userId);
   3184         } finally {
   3185             injectRestoreCallingIdentity(token);
   3186         }
   3187         if (resolved == null || resolved.size() == 0) {
   3188             return EMPTY_RESOLVE_INFO;
   3189         }
   3190         // Make sure the package is installed.
   3191         if (!isInstalled(resolved.get(0).activityInfo)) {
   3192             return EMPTY_RESOLVE_INFO;
   3193         }
   3194         if (exportedOnly) {
   3195             resolved.removeIf(ACTIVITY_NOT_EXPORTED);
   3196         }
   3197         return resolved;
   3198     }
   3199 
   3200     /**
   3201      * Return the main activity that is enabled and exported.  If multiple activities are found,
   3202      * return the first one.
   3203      */
   3204     @Nullable
   3205     ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
   3206         final long start = injectElapsedRealtime();
   3207         try {
   3208             final List<ResolveInfo> resolved =
   3209                     queryActivities(getMainActivityIntent(), packageName, null, userId);
   3210             return resolved.size() == 0 ? null : resolved.get(0).activityInfo.getComponentName();
   3211         } finally {
   3212             logDurationStat(Stats.GET_LAUNCHER_ACTIVITY, start);
   3213         }
   3214     }
   3215 
   3216     /**
   3217      * Return whether an activity is enabled, exported and main.
   3218      */
   3219     boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
   3220         final long start = injectElapsedRealtime();
   3221         try {
   3222             if (activity == null) {
   3223                 wtf("null activity detected");
   3224                 return false;
   3225             }
   3226             if (DUMMY_MAIN_ACTIVITY.equals(activity.getClassName())) {
   3227                 return true;
   3228             }
   3229             final List<ResolveInfo> resolved = queryActivities(
   3230                     getMainActivityIntent(), activity.getPackageName(), activity, userId);
   3231             return resolved.size() > 0;
   3232         } finally {
   3233             logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
   3234         }
   3235     }
   3236 
   3237     /**
   3238      * Create a dummy "main activity" component name which is used to create a dynamic shortcut
   3239      * with no main activity temporarily.
   3240      */
   3241     @NonNull
   3242     ComponentName getDummyMainActivity(@NonNull String packageName) {
   3243         return new ComponentName(packageName, DUMMY_MAIN_ACTIVITY);
   3244     }
   3245 
   3246     boolean isDummyMainActivity(@Nullable ComponentName name) {
   3247         return name != null && DUMMY_MAIN_ACTIVITY.equals(name.getClassName());
   3248     }
   3249 
   3250     /**
   3251      * Return all the enabled, exported and main activities from a package.
   3252      */
   3253     @NonNull
   3254     List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
   3255         final long start = injectElapsedRealtime();
   3256         try {
   3257             return queryActivities(getMainActivityIntent(), packageName, null, userId);
   3258         } finally {
   3259             logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
   3260         }
   3261     }
   3262 
   3263     /**
   3264      * Return whether an activity is enabled and exported.
   3265      */
   3266     @VisibleForTesting
   3267     boolean injectIsActivityEnabledAndExported(
   3268             @NonNull ComponentName activity, @UserIdInt int userId) {
   3269         final long start = injectElapsedRealtime();
   3270         try {
   3271             return queryActivities(new Intent(), activity.getPackageName(), activity, userId)
   3272                     .size() > 0;
   3273         } finally {
   3274             logDurationStat(Stats.IS_ACTIVITY_ENABLED, start);
   3275         }
   3276     }
   3277 
   3278     /**
   3279      * Get the {@link LauncherApps#ACTION_CONFIRM_PIN_SHORTCUT} or
   3280      * {@link LauncherApps#ACTION_CONFIRM_PIN_APPWIDGET} activity in a given package depending on
   3281      * the requestType.
   3282      */
   3283     @Nullable
   3284     ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
   3285             int launcherUserId, int requestType) {
   3286         Preconditions.checkNotNull(launcherPackageName);
   3287         String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
   3288                 LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
   3289                 LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
   3290 
   3291         final Intent confirmIntent = new Intent(action).setPackage(launcherPackageName);
   3292         final List<ResolveInfo> candidates = queryActivities(
   3293                 confirmIntent, launcherUserId, /* exportedOnly =*/ false);
   3294         for (ResolveInfo ri : candidates) {
   3295             return ri.activityInfo.getComponentName();
   3296         }
   3297         return null;
   3298     }
   3299 
   3300     boolean injectIsSafeModeEnabled() {
   3301         final long token = injectClearCallingIdentity();
   3302         try {
   3303             return IWindowManager.Stub
   3304                     .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE))
   3305                     .isSafeModeEnabled();
   3306         } catch (RemoteException e) {
   3307             return false; // Shouldn't happen though.
   3308         } finally {
   3309             injectRestoreCallingIdentity(token);
   3310         }
   3311     }
   3312 
   3313     /**
   3314      * If {@code userId} is of a managed profile, return the parent user ID.  Otherwise return
   3315      * itself.
   3316      */
   3317     int getParentOrSelfUserId(int userId) {
   3318         final long token = injectClearCallingIdentity();
   3319         try {
   3320             final UserInfo parent = mUserManager.getProfileParent(userId);
   3321             return (parent != null) ? parent.id : userId;
   3322         } finally {
   3323             injectRestoreCallingIdentity(token);
   3324         }
   3325     }
   3326 
   3327     void injectSendIntentSender(IntentSender intentSender, Intent extras) {
   3328         if (intentSender == null) {
   3329             return;
   3330         }
   3331         try {
   3332             intentSender.sendIntent(mContext, /* code= */ 0, extras,
   3333                     /* onFinished=*/ null, /* handler= */ null);
   3334         } catch (SendIntentException e) {
   3335             Slog.w(TAG, "sendIntent failed().", e);
   3336         }
   3337     }
   3338 
   3339     // === Backup & restore ===
   3340 
   3341     boolean shouldBackupApp(String packageName, int userId) {
   3342         return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
   3343     }
   3344 
   3345     boolean shouldBackupApp(PackageInfo pi) {
   3346         return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
   3347     }
   3348 
   3349     @Override
   3350     public byte[] getBackupPayload(@UserIdInt int userId) {
   3351         enforceSystem();
   3352         if (DEBUG) {
   3353             Slog.d(TAG, "Backing up user " + userId);
   3354         }
   3355         synchronized (mLock) {
   3356             if (!isUserUnlockedL(userId)) {
   3357                 wtf("Can't backup: user " + userId + " is locked or not running");
   3358                 return null;
   3359             }
   3360 
   3361             final ShortcutUser user = getUserShortcutsLocked(userId);
   3362             if (user == null) {
   3363                 wtf("Can't backup: user not found: id=" + userId);
   3364                 return null;
   3365             }
   3366 
   3367             // Update the signatures for all packages.
   3368             user.forAllPackageItems(spi -> spi.refreshPackageSignatureAndSave());
   3369 
   3370             // Set the version code for the launchers.
   3371             // We shouldn't do this for publisher packages, because we don't want to update the
   3372             // version code without rescanning the manifest.
   3373             user.forAllLaunchers(launcher -> launcher.ensureVersionInfo());
   3374 
   3375             // Save to the filesystem.
   3376             scheduleSaveUser(userId);
   3377             saveDirtyInfo();
   3378 
   3379             // Note, in case of backup, we don't have to wait on bitmap saving, because we don't
   3380             // back up bitmaps anyway.
   3381 
   3382             // Then create the backup payload.
   3383             final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024);
   3384             try {
   3385                 saveUserInternalLocked(userId, os, /* forBackup */ true);
   3386             } catch (XmlPullParserException | IOException e) {
   3387                 // Shouldn't happen.
   3388                 Slog.w(TAG, "Backup failed.", e);
   3389                 return null;
   3390             }
   3391             return os.toByteArray();
   3392         }
   3393     }
   3394 
   3395     @Override
   3396     public void applyRestore(byte[] payload, @UserIdInt int userId) {
   3397         enforceSystem();
   3398         if (DEBUG) {
   3399             Slog.d(TAG, "Restoring user " + userId);
   3400         }
   3401         synchronized (mLock) {
   3402             if (!isUserUnlockedL(userId)) {
   3403                 wtf("Can't restore: user " + userId + " is locked or not running");
   3404                 return;
   3405             }
   3406 
   3407             // Note we print the file timestamps in dumpsys too, but also printing the timestamp
   3408             // in the files anyway.
   3409             mShortcutDumpFiles.save("restore-0-start.txt", pw -> {
   3410                 pw.print("Start time: ");
   3411                 dumpCurrentTime(pw);
   3412                 pw.println();
   3413             });
   3414             mShortcutDumpFiles.save("restore-1-payload.xml", payload);
   3415 
   3416             // Actually do restore.
   3417             final ShortcutUser restored;
   3418             final ByteArrayInputStream is = new ByteArrayInputStream(payload);
   3419             try {
   3420                 restored = loadUserInternal(userId, is, /* fromBackup */ true);
   3421             } catch (XmlPullParserException | IOException | InvalidFileFormatException e) {
   3422                 Slog.w(TAG, "Restoration failed.", e);
   3423                 return;
   3424             }
   3425             mShortcutDumpFiles.save("restore-2.txt", this::dumpInner);
   3426 
   3427             getUserShortcutsLocked(userId).mergeRestoredFile(restored);
   3428 
   3429             mShortcutDumpFiles.save("restore-3.txt", this::dumpInner);
   3430 
   3431             // Rescan all packages to re-publish manifest shortcuts and do other checks.
   3432             rescanUpdatedPackagesLocked(userId,
   3433                     0 // lastScanTime = 0; rescan all packages.
   3434                     );
   3435 
   3436             mShortcutDumpFiles.save("restore-4.txt", this::dumpInner);
   3437 
   3438             mShortcutDumpFiles.save("restore-5-finish.txt", pw -> {
   3439                 pw.print("Finish time: ");
   3440                 dumpCurrentTime(pw);
   3441                 pw.println();
   3442             });
   3443 
   3444             saveUserLocked(userId);
   3445         }
   3446     }
   3447 
   3448     // === Dump ===
   3449 
   3450     @Override
   3451     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   3452         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
   3453         dumpNoCheck(fd, pw, args);
   3454     }
   3455 
   3456     @VisibleForTesting
   3457     void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) {
   3458 
   3459         boolean dumpMain = true;
   3460         boolean checkin = false;
   3461         boolean clear = false;
   3462         boolean dumpUid = false;
   3463         boolean dumpFiles = false;
   3464 
   3465         if (args != null) {
   3466             for (String arg : args) {
   3467                 if ("-c".equals(arg)) {
   3468                     checkin = true;
   3469 
   3470                 } else if ("--checkin".equals(arg)) {
   3471                     checkin = true;
   3472                     clear = true;
   3473 
   3474                 } else if ("-a".equals(arg) || "--all".equals(arg)) {
   3475                     dumpUid = true;
   3476                     dumpFiles = true;
   3477 
   3478                 } else if ("-u".equals(arg) || "--uid".equals(arg)) {
   3479                     dumpUid = true;
   3480 
   3481                 } else if ("-f".equals(arg) || "--files".equals(arg)) {
   3482                     dumpFiles = true;
   3483 
   3484                 } else if ("-n".equals(arg) || "--no-main".equals(arg)) {
   3485                     dumpMain = false;
   3486                 }
   3487             }
   3488         }
   3489 
   3490         if (checkin) {
   3491             // Other flags are not supported for checkin.
   3492             dumpCheckin(pw, clear);
   3493         } else {
   3494             if (dumpMain) {
   3495                 dumpInner(pw);
   3496                 pw.println();
   3497             }
   3498             if (dumpUid) {
   3499                 dumpUid(pw);
   3500                 pw.println();
   3501             }
   3502             if (dumpFiles) {
   3503                 dumpDumpFiles(pw);
   3504                 pw.println();
   3505             }
   3506         }
   3507     }
   3508 
   3509     private void dumpInner(PrintWriter pw) {
   3510         synchronized (mLock) {
   3511             final long now = injectCurrentTimeMillis();
   3512             pw.print("Now: [");
   3513             pw.print(now);
   3514             pw.print("] ");
   3515             pw.print(formatTime(now));
   3516 
   3517             pw.print("  Raw last reset: [");
   3518             pw.print(mRawLastResetTime);
   3519             pw.print("] ");
   3520             pw.print(formatTime(mRawLastResetTime));
   3521 
   3522             final long last = getLastResetTimeLocked();
   3523             pw.print("  Last reset: [");
   3524             pw.print(last);
   3525             pw.print("] ");
   3526             pw.print(formatTime(last));
   3527 
   3528             final long next = getNextResetTimeLocked();
   3529             pw.print("  Next reset: [");
   3530             pw.print(next);
   3531             pw.print("] ");
   3532             pw.print(formatTime(next));
   3533 
   3534             pw.print("  Config:");
   3535             pw.print("    Max icon dim: ");
   3536             pw.println(mMaxIconDimension);
   3537             pw.print("    Icon format: ");
   3538             pw.println(mIconPersistFormat);
   3539             pw.print("    Icon quality: ");
   3540             pw.println(mIconPersistQuality);
   3541             pw.print("    saveDelayMillis: ");
   3542             pw.println(mSaveDelayMillis);
   3543             pw.print("    resetInterval: ");
   3544             pw.println(mResetInterval);
   3545             pw.print("    maxUpdatesPerInterval: ");
   3546             pw.println(mMaxUpdatesPerInterval);
   3547             pw.print("    maxShortcutsPerActivity: ");
   3548             pw.println(mMaxShortcuts);
   3549             pw.println();
   3550 
   3551             pw.println("  Stats:");
   3552             synchronized (mStatLock) {
   3553                 for (int i = 0; i < Stats.COUNT; i++) {
   3554                     dumpStatLS(pw, "    ", i);
   3555                 }
   3556             }
   3557 
   3558             pw.println();
   3559             pw.print("  #Failures: ");
   3560             pw.println(mWtfCount);
   3561 
   3562             if (mLastWtfStacktrace != null) {
   3563                 pw.print("  Last failure stack trace: ");
   3564                 pw.println(Log.getStackTraceString(mLastWtfStacktrace));
   3565             }
   3566 
   3567             pw.println();
   3568             mShortcutBitmapSaver.dumpLocked(pw, "  ");
   3569 
   3570             for (int i = 0; i < mUsers.size(); i++) {
   3571                 pw.println();
   3572                 mUsers.valueAt(i).dump(pw, "  ");
   3573             }
   3574         }
   3575     }
   3576 
   3577     private void dumpUid(PrintWriter pw) {
   3578         synchronized (mLock) {
   3579             pw.println("** SHORTCUT MANAGER UID STATES (dumpsys shortcut -n -u)");
   3580 
   3581             for (int i = 0; i < mUidState.size(); i++) {
   3582                 final int uid = mUidState.keyAt(i);
   3583                 final int state = mUidState.valueAt(i);
   3584                 pw.print("    UID=");
   3585                 pw.print(uid);
   3586                 pw.print(" state=");
   3587                 pw.print(state);
   3588                 if (isProcessStateForeground(state)) {
   3589                     pw.print("  [FG]");
   3590                 }
   3591                 pw.print("  last FG=");
   3592                 pw.print(mUidLastForegroundElapsedTime.get(uid));
   3593                 pw.println();
   3594             }
   3595         }
   3596     }
   3597 
   3598     static String formatTime(long time) {
   3599         Time tobj = new Time();
   3600         tobj.set(time);
   3601         return tobj.format("%Y-%m-%d %H:%M:%S");
   3602     }
   3603 
   3604     private void dumpCurrentTime(PrintWriter pw) {
   3605         pw.print(formatTime(injectCurrentTimeMillis()));
   3606     }
   3607 
   3608     private void dumpStatLS(PrintWriter pw, String prefix, int statId) {
   3609         pw.print(prefix);
   3610         final int count = mCountStats[statId];
   3611         final long dur = mDurationStats[statId];
   3612         pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
   3613                 STAT_LABELS[statId], count, dur,
   3614                 (count == 0 ? 0 : ((double) dur) / count)));
   3615     }
   3616 
   3617     /**
   3618      * Dumpsys for checkin.
   3619      *
   3620      * @param clear if true, clear the history information.  Some other system services have this
   3621      * behavior but shortcut service doesn't for now.
   3622      */
   3623     private  void dumpCheckin(PrintWriter pw, boolean clear) {
   3624         synchronized (mLock) {
   3625             try {
   3626                 final JSONArray users = new JSONArray();
   3627 
   3628                 for (int i = 0; i < mUsers.size(); i++) {
   3629                     users.put(mUsers.valueAt(i).dumpCheckin(clear));
   3630                 }
   3631 
   3632                 final JSONObject result = new JSONObject();
   3633 
   3634                 result.put(KEY_SHORTCUT, users);
   3635                 result.put(KEY_LOW_RAM, injectIsLowRamDevice());
   3636                 result.put(KEY_ICON_SIZE, mMaxIconDimension);
   3637 
   3638                 pw.println(result.toString(1));
   3639             } catch (JSONException e) {
   3640                 Slog.e(TAG, "Unable to write in json", e);
   3641             }
   3642         }
   3643     }
   3644 
   3645     private void dumpDumpFiles(PrintWriter pw) {
   3646         synchronized (mLock) {
   3647             pw.println("** SHORTCUT MANAGER FILES (dumpsys shortcut -n -f)");
   3648             mShortcutDumpFiles.dumpAll(pw);
   3649         }
   3650     }
   3651 
   3652     // === Shell support ===
   3653 
   3654     @Override
   3655     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
   3656             String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
   3657 
   3658         enforceShell();
   3659 
   3660         final long token = injectClearCallingIdentity();
   3661         try {
   3662             final int status = (new MyShellCommand()).exec(this, in, out, err, args, callback,
   3663                     resultReceiver);
   3664             resultReceiver.send(status, null);
   3665         } finally {
   3666             injectRestoreCallingIdentity(token);
   3667         }
   3668     }
   3669 
   3670     static class CommandException extends Exception {
   3671         public CommandException(String message) {
   3672             super(message);
   3673         }
   3674     }
   3675 
   3676     /**
   3677      * Handle "adb shell cmd".
   3678      */
   3679     private class MyShellCommand extends ShellCommand {
   3680 
   3681         private int mUserId = UserHandle.USER_SYSTEM;
   3682 
   3683         private void parseOptionsLocked(boolean takeUser)
   3684                 throws CommandException {
   3685             String opt;
   3686             while ((opt = getNextOption()) != null) {
   3687                 switch (opt) {
   3688                     case "--user":
   3689                         if (takeUser) {
   3690                             mUserId = UserHandle.parseUserArg(getNextArgRequired());
   3691                             if (!isUserUnlockedL(mUserId)) {
   3692                                 throw new CommandException(
   3693                                         "User " + mUserId + " is not running or locked");
   3694                             }
   3695                             break;
   3696                         }
   3697                         // fallthrough
   3698                     default:
   3699                         throw new CommandException("Unknown option: " + opt);
   3700                 }
   3701             }
   3702         }
   3703 
   3704         @Override
   3705         public int onCommand(String cmd) {
   3706             if (cmd == null) {
   3707                 return handleDefaultCommands(cmd);
   3708             }
   3709             final PrintWriter pw = getOutPrintWriter();
   3710             try {
   3711                 switch (cmd) {
   3712                     case "reset-throttling":
   3713                         handleResetThrottling();
   3714                         break;
   3715                     case "reset-all-throttling":
   3716                         handleResetAllThrottling();
   3717                         break;
   3718                     case "override-config":
   3719                         handleOverrideConfig();
   3720                         break;
   3721                     case "reset-config":
   3722                         handleResetConfig();
   3723                         break;
   3724                     case "clear-default-launcher":
   3725                         handleClearDefaultLauncher();
   3726                         break;
   3727                     case "get-default-launcher":
   3728                         handleGetDefaultLauncher();
   3729                         break;
   3730                     case "unload-user":
   3731                         handleUnloadUser();
   3732                         break;
   3733                     case "clear-shortcuts":
   3734                         handleClearShortcuts();
   3735                         break;
   3736                     case "verify-states": // hidden command to verify various internal states.
   3737                         handleVerifyStates();
   3738                         break;
   3739                     default:
   3740                         return handleDefaultCommands(cmd);
   3741                 }
   3742             } catch (CommandException e) {
   3743                 pw.println("Error: " + e.getMessage());
   3744                 return 1;
   3745             }
   3746             pw.println("Success");
   3747             return 0;
   3748         }
   3749 
   3750         @Override
   3751         public void onHelp() {
   3752             final PrintWriter pw = getOutPrintWriter();
   3753             pw.println("Usage: cmd shortcut COMMAND [options ...]");
   3754             pw.println();
   3755             pw.println("cmd shortcut reset-throttling [--user USER_ID]");
   3756             pw.println("    Reset throttling for all packages and users");
   3757             pw.println();
   3758             pw.println("cmd shortcut reset-all-throttling");
   3759             pw.println("    Reset the throttling state for all users");
   3760             pw.println();
   3761             pw.println("cmd shortcut override-config CONFIG");
   3762             pw.println("    Override the configuration for testing (will last until reboot)");
   3763             pw.println();
   3764             pw.println("cmd shortcut reset-config");
   3765             pw.println("    Reset the configuration set with \"update-config\"");
   3766             pw.println();
   3767             pw.println("cmd shortcut clear-default-launcher [--user USER_ID]");
   3768             pw.println("    Clear the cached default launcher");
   3769             pw.println();
   3770             pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
   3771             pw.println("    Show the default launcher");
   3772             pw.println();
   3773             pw.println("cmd shortcut unload-user [--user USER_ID]");
   3774             pw.println("    Unload a user from the memory");
   3775             pw.println("    (This should not affect any observable behavior)");
   3776             pw.println();
   3777             pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
   3778             pw.println("    Remove all shortcuts from a package, including pinned shortcuts");
   3779             pw.println();
   3780         }
   3781 
   3782         private void handleResetThrottling() throws CommandException {
   3783             synchronized (mLock) {
   3784                 parseOptionsLocked(/* takeUser =*/ true);
   3785 
   3786                 Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
   3787 
   3788                 resetThrottlingInner(mUserId);
   3789             }
   3790         }
   3791 
   3792         private void handleResetAllThrottling() {
   3793             Slog.i(TAG, "cmd: handleResetAllThrottling");
   3794 
   3795             resetAllThrottlingInner();
   3796         }
   3797 
   3798         private void handleOverrideConfig() throws CommandException {
   3799             final String config = getNextArgRequired();
   3800 
   3801             Slog.i(TAG, "cmd: handleOverrideConfig: " + config);
   3802 
   3803             synchronized (mLock) {
   3804                 if (!updateConfigurationLocked(config)) {
   3805                     throw new CommandException("override-config failed.  See logcat for details.");
   3806                 }
   3807             }
   3808         }
   3809 
   3810         private void handleResetConfig() {
   3811             Slog.i(TAG, "cmd: handleResetConfig");
   3812 
   3813             synchronized (mLock) {
   3814                 loadConfigurationLocked();
   3815             }
   3816         }
   3817 
   3818         private void clearLauncher() {
   3819             synchronized (mLock) {
   3820                 getUserShortcutsLocked(mUserId).forceClearLauncher();
   3821             }
   3822         }
   3823 
   3824         private void showLauncher() {
   3825             synchronized (mLock) {
   3826                 // This ensures to set the cached launcher.  Package name doesn't matter.
   3827                 hasShortcutHostPermissionInner("-", mUserId);
   3828 
   3829                 getOutPrintWriter().println("Launcher: "
   3830                         + getUserShortcutsLocked(mUserId).getLastKnownLauncher());
   3831             }
   3832         }
   3833 
   3834         private void handleClearDefaultLauncher() throws CommandException {
   3835             synchronized (mLock) {
   3836                 parseOptionsLocked(/* takeUser =*/ true);
   3837 
   3838                 clearLauncher();
   3839             }
   3840         }
   3841 
   3842         private void handleGetDefaultLauncher() throws CommandException {
   3843             synchronized (mLock) {
   3844                 parseOptionsLocked(/* takeUser =*/ true);
   3845 
   3846                 clearLauncher();
   3847                 showLauncher();
   3848             }
   3849         }
   3850 
   3851         private void handleUnloadUser() throws CommandException {
   3852             synchronized (mLock) {
   3853                 parseOptionsLocked(/* takeUser =*/ true);
   3854 
   3855                 Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
   3856 
   3857                 ShortcutService.this.handleStopUser(mUserId);
   3858             }
   3859         }
   3860 
   3861         private void handleClearShortcuts() throws CommandException {
   3862             synchronized (mLock) {
   3863                 parseOptionsLocked(/* takeUser =*/ true);
   3864                 final String packageName = getNextArgRequired();
   3865 
   3866                 Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
   3867 
   3868                 ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
   3869                         /* appStillExists = */ true);
   3870             }
   3871         }
   3872 
   3873         private void handleVerifyStates() throws CommandException {
   3874             try {
   3875                 verifyStatesForce(); // This will throw when there's an issue.
   3876             } catch (Throwable th) {
   3877                 throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
   3878             }
   3879         }
   3880     }
   3881 
   3882     // === Unit test support ===
   3883 
   3884     // Injection point.
   3885     @VisibleForTesting
   3886     long injectCurrentTimeMillis() {
   3887         return System.currentTimeMillis();
   3888     }
   3889 
   3890     @VisibleForTesting
   3891     long injectElapsedRealtime() {
   3892         return SystemClock.elapsedRealtime();
   3893     }
   3894 
   3895     @VisibleForTesting
   3896     long injectUptimeMillis() {
   3897         return SystemClock.uptimeMillis();
   3898     }
   3899 
   3900     // Injection point.
   3901     @VisibleForTesting
   3902     int injectBinderCallingUid() {
   3903         return getCallingUid();
   3904     }
   3905 
   3906     private int getCallingUserId() {
   3907         return UserHandle.getUserId(injectBinderCallingUid());
   3908     }
   3909 
   3910     // Injection point.
   3911     @VisibleForTesting
   3912     long injectClearCallingIdentity() {
   3913         return Binder.clearCallingIdentity();
   3914     }
   3915 
   3916     // Injection point.
   3917     @VisibleForTesting
   3918     void injectRestoreCallingIdentity(long token) {
   3919         Binder.restoreCallingIdentity(token);
   3920     }
   3921 
   3922     // Injection point.
   3923     @VisibleForTesting
   3924     String injectBuildFingerprint() {
   3925         return Build.FINGERPRINT;
   3926     }
   3927 
   3928     final void wtf(String message) {
   3929         wtf(message, /* exception= */ null);
   3930     }
   3931 
   3932     // Injection point.
   3933     void wtf(String message, Throwable e) {
   3934         if (e == null) {
   3935             e = new RuntimeException("Stacktrace");
   3936         }
   3937         synchronized (mLock) {
   3938             mWtfCount++;
   3939             mLastWtfStacktrace = new Exception("Last failure was logged here:");
   3940         }
   3941         Slog.wtf(TAG, message, e);
   3942     }
   3943 
   3944     @VisibleForTesting
   3945     File injectSystemDataPath() {
   3946         return Environment.getDataSystemDirectory();
   3947     }
   3948 
   3949     @VisibleForTesting
   3950     File injectUserDataPath(@UserIdInt int userId) {
   3951         return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
   3952     }
   3953 
   3954     public File getDumpPath() {
   3955         return new File(injectUserDataPath(UserHandle.USER_SYSTEM), DIRECTORY_DUMP);
   3956     }
   3957 
   3958     @VisibleForTesting
   3959     boolean injectIsLowRamDevice() {
   3960         return ActivityManager.isLowRamDeviceStatic();
   3961     }
   3962 
   3963     @VisibleForTesting
   3964     void injectRegisterUidObserver(IUidObserver observer, int which) {
   3965         try {
   3966             ActivityManager.getService().registerUidObserver(observer, which,
   3967                     ActivityManager.PROCESS_STATE_UNKNOWN, null);
   3968         } catch (RemoteException shouldntHappen) {
   3969         }
   3970     }
   3971 
   3972     File getUserBitmapFilePath(@UserIdInt int userId) {
   3973         return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
   3974     }
   3975 
   3976     @VisibleForTesting
   3977     SparseArray<ShortcutUser> getShortcutsForTest() {
   3978         return mUsers;
   3979     }
   3980 
   3981     @VisibleForTesting
   3982     int getMaxShortcutsForTest() {
   3983         return mMaxShortcuts;
   3984     }
   3985 
   3986     @VisibleForTesting
   3987     int getMaxUpdatesPerIntervalForTest() {
   3988         return mMaxUpdatesPerInterval;
   3989     }
   3990 
   3991     @VisibleForTesting
   3992     long getResetIntervalForTest() {
   3993         return mResetInterval;
   3994     }
   3995 
   3996     @VisibleForTesting
   3997     int getMaxIconDimensionForTest() {
   3998         return mMaxIconDimension;
   3999     }
   4000 
   4001     @VisibleForTesting
   4002     CompressFormat getIconPersistFormatForTest() {
   4003         return mIconPersistFormat;
   4004     }
   4005 
   4006     @VisibleForTesting
   4007     int getIconPersistQualityForTest() {
   4008         return mIconPersistQuality;
   4009     }
   4010 
   4011     @VisibleForTesting
   4012     ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
   4013         synchronized (mLock) {
   4014             final ShortcutUser user = mUsers.get(userId);
   4015             if (user == null) return null;
   4016 
   4017             return user.getAllPackagesForTest().get(packageName);
   4018         }
   4019     }
   4020 
   4021     @VisibleForTesting
   4022     ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
   4023         synchronized (mLock) {
   4024             final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
   4025             if (pkg == null) return null;
   4026 
   4027             return pkg.findShortcutById(shortcutId);
   4028         }
   4029     }
   4030 
   4031     @VisibleForTesting
   4032     ShortcutLauncher getLauncherShortcutForTest(String packageName, int userId) {
   4033         synchronized (mLock) {
   4034             final ShortcutUser user = mUsers.get(userId);
   4035             if (user == null) return null;
   4036 
   4037             return user.getAllLaunchersForTest().get(PackageWithUser.of(userId, packageName));
   4038         }
   4039     }
   4040 
   4041     @VisibleForTesting
   4042     ShortcutRequestPinProcessor getShortcutRequestPinProcessorForTest() {
   4043         return mShortcutRequestPinProcessor;
   4044     }
   4045 
   4046     /**
   4047      * Control whether {@link #verifyStates} should be performed.  We always perform it during unit
   4048      * tests.
   4049      */
   4050     @VisibleForTesting
   4051     boolean injectShouldPerformVerification() {
   4052         return DEBUG;
   4053     }
   4054 
   4055     /**
   4056      * Check various internal states and throws if there's any inconsistency.
   4057      * This is normally only enabled during unit tests.
   4058      */
   4059     final void verifyStates() {
   4060         if (injectShouldPerformVerification()) {
   4061             verifyStatesInner();
   4062         }
   4063     }
   4064 
   4065     private final void verifyStatesForce() {
   4066         verifyStatesInner();
   4067     }
   4068 
   4069     private void verifyStatesInner() {
   4070         synchronized (mLock) {
   4071             forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
   4072         }
   4073     }
   4074 
   4075     @VisibleForTesting
   4076     void waitForBitmapSavesForTest() {
   4077         synchronized (mLock) {
   4078             mShortcutBitmapSaver.waitForAllSavesLocked();
   4079         }
   4080     }
   4081 }
   4082