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