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