• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package com.android.launcher3.model;
18 
19 import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
20 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
21 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
22 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
23 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
24 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
25 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
26 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
27 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
28 import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
29 import static com.android.launcher3.util.PackageManagerHelper.isSystemApp;
30 
31 import android.annotation.SuppressLint;
32 import android.appwidget.AppWidgetProviderInfo;
33 import android.content.ComponentName;
34 import android.content.ContentResolver;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.content.pm.LauncherActivityInfo;
39 import android.content.pm.LauncherApps;
40 import android.content.pm.PackageInstaller;
41 import android.content.pm.PackageInstaller.SessionInfo;
42 import android.content.pm.PackageManager;
43 import android.content.pm.ShortcutInfo;
44 import android.graphics.Point;
45 import android.net.Uri;
46 import android.os.Bundle;
47 import android.os.UserHandle;
48 import android.os.UserManager;
49 import android.text.TextUtils;
50 import android.util.ArrayMap;
51 import android.util.Log;
52 import android.util.LongSparseArray;
53 import android.util.TimingLogger;
54 
55 import com.android.launcher3.DeviceProfile;
56 import com.android.launcher3.InvariantDeviceProfile;
57 import com.android.launcher3.LauncherAppState;
58 import com.android.launcher3.LauncherModel;
59 import com.android.launcher3.LauncherSettings;
60 import com.android.launcher3.Utilities;
61 import com.android.launcher3.config.FeatureFlags;
62 import com.android.launcher3.folder.Folder;
63 import com.android.launcher3.folder.FolderGridOrganizer;
64 import com.android.launcher3.folder.FolderNameInfos;
65 import com.android.launcher3.folder.FolderNameProvider;
66 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
67 import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
68 import com.android.launcher3.icons.IconCache;
69 import com.android.launcher3.icons.LauncherActivityCachingLogic;
70 import com.android.launcher3.icons.ShortcutCachingLogic;
71 import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
72 import com.android.launcher3.logging.FileLog;
73 import com.android.launcher3.model.data.AppInfo;
74 import com.android.launcher3.model.data.FolderInfo;
75 import com.android.launcher3.model.data.ItemInfo;
76 import com.android.launcher3.model.data.ItemInfoWithIcon;
77 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
78 import com.android.launcher3.model.data.WorkspaceItemInfo;
79 import com.android.launcher3.pm.InstallSessionHelper;
80 import com.android.launcher3.pm.PackageInstallInfo;
81 import com.android.launcher3.pm.UserCache;
82 import com.android.launcher3.provider.ImportDataTask;
83 import com.android.launcher3.qsb.QsbContainerView;
84 import com.android.launcher3.shortcuts.ShortcutKey;
85 import com.android.launcher3.shortcuts.ShortcutRequest;
86 import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
87 import com.android.launcher3.util.ComponentKey;
88 import com.android.launcher3.util.IOUtils;
89 import com.android.launcher3.util.LooperIdleLock;
90 import com.android.launcher3.util.PackageManagerHelper;
91 import com.android.launcher3.util.PackageUserKey;
92 import com.android.launcher3.util.TraceHelper;
93 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
94 import com.android.launcher3.widget.WidgetManagerHelper;
95 
96 import java.util.ArrayList;
97 import java.util.Collections;
98 import java.util.HashMap;
99 import java.util.HashSet;
100 import java.util.List;
101 import java.util.Map;
102 import java.util.Set;
103 import java.util.concurrent.CancellationException;
104 
105 /**
106  * Runnable for the thread that loads the contents of the launcher:
107  *   - workspace icons
108  *   - widgets
109  *   - all apps icons
110  *   - deep shortcuts within apps
111  */
112 public class LoaderTask implements Runnable {
113     private static final String TAG = "LoaderTask";
114 
115     private static final boolean DEBUG = true;
116 
117     protected final LauncherAppState mApp;
118     private final AllAppsList mBgAllAppsList;
119     protected final BgDataModel mBgDataModel;
120     private final ModelDelegate mModelDelegate;
121 
122     private FirstScreenBroadcast mFirstScreenBroadcast;
123 
124     private final LoaderResults mResults;
125 
126     private final LauncherApps mLauncherApps;
127     private final UserManager mUserManager;
128     private final UserCache mUserCache;
129 
130     private final InstallSessionHelper mSessionHelper;
131     private final IconCache mIconCache;
132 
133     private final UserManagerState mUserManagerState = new UserManagerState();
134 
135     protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>();
136 
137     private boolean mStopped;
138 
139     private final Set<PackageUserKey> mPendingPackages = new HashSet<>();
140     private boolean mItemsDeleted = false;
141     private String mDbName;
142 
LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, ModelDelegate modelDelegate, LoaderResults results)143     public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
144             ModelDelegate modelDelegate, LoaderResults results) {
145         mApp = app;
146         mBgAllAppsList = bgAllAppsList;
147         mBgDataModel = dataModel;
148         mModelDelegate = modelDelegate;
149         mResults = results;
150 
151         mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
152         mUserManager = mApp.getContext().getSystemService(UserManager.class);
153         mUserCache = UserCache.INSTANCE.get(mApp.getContext());
154         mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
155         mIconCache = mApp.getIconCache();
156     }
157 
waitForIdle()158     protected synchronized void waitForIdle() {
159         // Wait until the either we're stopped or the other threads are done.
160         // This way we don't start loading all apps until the workspace has settled
161         // down.
162         LooperIdleLock idleLock = mResults.newIdleLock(this);
163         // Just in case mFlushingWorkerThread changes but we aren't woken up,
164         // wait no longer than 1sec at a time
165         while (!mStopped && idleLock.awaitLocked(1000));
166     }
167 
verifyNotStopped()168     private synchronized void verifyNotStopped() throws CancellationException {
169         if (mStopped) {
170             throw new CancellationException("Loader stopped");
171         }
172     }
173 
sendFirstScreenActiveInstallsBroadcast()174     private void sendFirstScreenActiveInstallsBroadcast() {
175         ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
176         ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems();
177         // Screen set is never empty
178         final int firstScreen = mBgDataModel.collectWorkspaceScreens().get(0);
179 
180         filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
181                 new ArrayList<>() /* otherScreenItems are ignored */);
182         mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
183     }
184 
run()185     public void run() {
186         synchronized (this) {
187             // Skip fast if we are already stopped.
188             if (mStopped) {
189                 return;
190             }
191         }
192 
193         Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
194         TimingLogger logger = new TimingLogger(TAG, "run");
195         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
196             List<ShortcutInfo> allShortcuts = new ArrayList<>();
197             loadWorkspace(allShortcuts);
198             logASplit(logger, "loadWorkspace");
199 
200             // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
201             // sanitizeData should not be invoked if the workspace is loaded from a db different
202             // from the main db as defined in the invariant device profile.
203             // (e.g. both grid preview and minimal device mode uses a different db)
204             if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
205                 verifyNotStopped();
206                 sanitizeData();
207                 logASplit(logger, "sanitizeData");
208             }
209 
210             verifyNotStopped();
211             mResults.bindWorkspace();
212             logASplit(logger, "bindWorkspace");
213 
214             mModelDelegate.workspaceLoadComplete();
215             // Notify the installer packages of packages with active installs on the first screen.
216             sendFirstScreenActiveInstallsBroadcast();
217             logASplit(logger, "sendFirstScreenActiveInstallsBroadcast");
218 
219             // Take a break
220             waitForIdle();
221             logASplit(logger, "step 1 complete");
222             verifyNotStopped();
223 
224             // second step
225             List<LauncherActivityInfo> allActivityList = loadAllApps();
226             logASplit(logger, "loadAllApps");
227 
228             verifyNotStopped();
229             mResults.bindAllApps();
230             logASplit(logger, "bindAllApps");
231 
232             verifyNotStopped();
233             IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
234             setIgnorePackages(updateHandler);
235             updateHandler.updateIcons(allActivityList,
236                     LauncherActivityCachingLogic.newInstance(mApp.getContext()),
237                     mApp.getModel()::onPackageIconsUpdated);
238             logASplit(logger, "update icon cache");
239 
240             if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
241                 verifyNotStopped();
242                 logASplit(logger, "save shortcuts in icon cache");
243                 updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
244                         mApp.getModel()::onPackageIconsUpdated);
245             }
246 
247             // Take a break
248             waitForIdle();
249             logASplit(logger, "step 2 complete");
250             verifyNotStopped();
251 
252             // third step
253             List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
254             logASplit(logger, "loadDeepShortcuts");
255 
256             verifyNotStopped();
257             mResults.bindDeepShortcuts();
258             logASplit(logger, "bindDeepShortcuts");
259 
260             if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
261                 verifyNotStopped();
262                 logASplit(logger, "save deep shortcuts in icon cache");
263                 updateHandler.updateIcons(allDeepShortcuts,
264                         new ShortcutCachingLogic(), (pkgs, user) -> { });
265             }
266 
267             // Take a break
268             waitForIdle();
269             logASplit(logger, "step 3 complete");
270             verifyNotStopped();
271 
272             // fourth step
273             List<ComponentWithLabelAndIcon> allWidgetsList =
274                     mBgDataModel.widgetsModel.update(mApp, null);
275             logASplit(logger, "load widgets");
276 
277             verifyNotStopped();
278             mResults.bindWidgets();
279             logASplit(logger, "bindWidgets");
280             verifyNotStopped();
281 
282             updateHandler.updateIcons(allWidgetsList,
283                     new ComponentWithIconCachingLogic(mApp.getContext(), true),
284                     mApp.getModel()::onWidgetLabelsUpdated);
285             logASplit(logger, "save widgets in icon cache");
286 
287             // fifth step
288             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
289                 loadFolderNames();
290             }
291 
292             verifyNotStopped();
293             updateHandler.finish();
294             logASplit(logger, "finish icon update");
295 
296             mModelDelegate.modelLoadComplete();
297             transaction.commit();
298         } catch (CancellationException e) {
299             // Loader stopped, ignore
300             logASplit(logger, "Cancelled");
301         } finally {
302             logger.dumpToLog();
303         }
304         TraceHelper.INSTANCE.endSection(traceToken);
305     }
306 
stopLocked()307     public synchronized void stopLocked() {
308         mStopped = true;
309         this.notify();
310     }
311 
loadWorkspace(List<ShortcutInfo> allDeepShortcuts)312     private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
313         loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
314                 null /* selection */);
315     }
316 
loadWorkspace(List<ShortcutInfo> allDeepShortcuts, Uri contentUri, String selection)317     protected void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, Uri contentUri,
318             String selection) {
319         final Context context = mApp.getContext();
320         final ContentResolver contentResolver = context.getContentResolver();
321         final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
322         final boolean isSafeMode = pmHelper.isSafeMode();
323         final boolean isSdCardReady = Utilities.isBootCompleted();
324         final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
325 
326         boolean clearDb = false;
327         try {
328             ImportDataTask.performImportIfPossible(context);
329         } catch (Exception e) {
330             // Migration failed. Clear workspace.
331             clearDb = true;
332         }
333 
334         if (!clearDb && (MULTI_DB_GRID_MIRATION_ALGO.get()
335                 ? !GridSizeMigrationTaskV2.migrateGridIfNeeded(context)
336                 : !GridSizeMigrationTask.migrateGridIfNeeded(context))) {
337             // Migration failed. Clear workspace.
338             clearDb = true;
339         }
340 
341         if (clearDb) {
342             Log.d(TAG, "loadWorkspace: resetting launcher database");
343             LauncherSettings.Settings.call(contentResolver,
344                     LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
345         }
346 
347         Log.d(TAG, "loadWorkspace: loading default favorites");
348         LauncherSettings.Settings.call(contentResolver,
349                 LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
350 
351         synchronized (mBgDataModel) {
352             mBgDataModel.clear();
353             mPendingPackages.clear();
354 
355             final HashMap<PackageUserKey, SessionInfo> installingPkgs =
356                     mSessionHelper.getActiveSessions();
357             installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
358 
359             final PackageUserKey tempPackageKey = new PackageUserKey(null, null);
360             mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
361 
362             Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
363             final LoaderCursor c = new LoaderCursor(
364                     contentResolver.query(contentUri, null, selection, null, null), contentUri,
365                     mApp, mUserManagerState);
366             final Bundle extras = c.getExtras();
367             mDbName = extras == null
368                     ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME);
369             try {
370                 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
371                         LauncherSettings.Favorites.APPWIDGET_ID);
372                 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
373                         LauncherSettings.Favorites.APPWIDGET_PROVIDER);
374                 final int spanXIndex = c.getColumnIndexOrThrow
375                         (LauncherSettings.Favorites.SPANX);
376                 final int spanYIndex = c.getColumnIndexOrThrow(
377                         LauncherSettings.Favorites.SPANY);
378                 final int rankIndex = c.getColumnIndexOrThrow(
379                         LauncherSettings.Favorites.RANK);
380                 final int optionsIndex = c.getColumnIndexOrThrow(
381                         LauncherSettings.Favorites.OPTIONS);
382                 final int sourceContainerIndex = c.getColumnIndexOrThrow(
383                         LauncherSettings.Favorites.APPWIDGET_SOURCE);
384 
385                 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
386 
387                 mUserManagerState.init(mUserCache, mUserManager);
388 
389                 for (UserHandle user : mUserCache.getUserProfiles()) {
390                     long serialNo = mUserCache.getSerialNumberForUser(user);
391                     boolean userUnlocked = mUserManager.isUserUnlocked(user);
392 
393                     // We can only query for shortcuts when the user is unlocked.
394                     if (userUnlocked) {
395                         QueryResult pinnedShortcuts = new ShortcutRequest(context, user)
396                                 .query(ShortcutRequest.PINNED);
397                         if (pinnedShortcuts.wasSuccess()) {
398                             for (ShortcutInfo shortcut : pinnedShortcuts) {
399                                 shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
400                                         shortcut);
401                             }
402                         } else {
403                             // Shortcut manager can fail due to some race condition when the
404                             // lock state changes too frequently. For the purpose of the loading
405                             // shortcuts, consider the user is still locked.
406                             userUnlocked = false;
407                         }
408                     }
409                     unlockedUsers.put(serialNo, userUnlocked);
410                 }
411 
412                 WorkspaceItemInfo info;
413                 LauncherAppWidgetInfo appWidgetInfo;
414                 LauncherAppWidgetProviderInfo widgetProviderInfo;
415                 Intent intent;
416                 String targetPkg;
417 
418                 while (!mStopped && c.moveToNext()) {
419                     try {
420                         if (c.user == null) {
421                             // User has been deleted, remove the item.
422                             c.markDeleted("User has been deleted");
423                             continue;
424                         }
425 
426                         boolean allowMissingTarget = false;
427                         switch (c.itemType) {
428                         case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
429                         case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
430                         case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
431                             intent = c.parseIntent();
432                             if (intent == null) {
433                                 c.markDeleted("Invalid or null intent");
434                                 continue;
435                             }
436 
437                             int disabledState = mUserManagerState.isUserQuiet(c.serialNumber)
438                                     ? WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0;
439                             ComponentName cn = intent.getComponent();
440                             targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
441 
442                             if (TextUtils.isEmpty(targetPkg) &&
443                                     c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
444                                 c.markDeleted("Only legacy shortcuts can have null package");
445                                 continue;
446                             }
447 
448                             // If there is no target package, its an implicit intent
449                             // (legacy shortcut) which is always valid
450                             boolean validTarget = TextUtils.isEmpty(targetPkg) ||
451                                     mLauncherApps.isPackageEnabled(targetPkg, c.user);
452 
453                             // If it's a deep shortcut, we'll use pinned shortcuts to restore it
454                             if (cn != null && validTarget && c.itemType
455                                     != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
456                                 // If the apk is present and the shortcut points to a specific
457                                 // component.
458 
459                                 // If the component is already present
460                                 if (mLauncherApps.isActivityEnabled(cn, c.user)) {
461                                     // no special handling necessary for this item
462                                     c.markRestored();
463                                 } else {
464                                     // Gracefully try to find a fallback activity.
465                                     intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
466                                     if (intent != null) {
467                                         c.restoreFlag = 0;
468                                         c.updater().put(
469                                                 LauncherSettings.Favorites.INTENT,
470                                                 intent.toUri(0)).commit();
471                                         cn = intent.getComponent();
472                                     } else {
473                                         c.markDeleted("Unable to find a launch target");
474                                         continue;
475                                     }
476                                 }
477                             }
478                             // else if cn == null => can't infer much, leave it
479                             // else if !validPkg => could be restored icon or missing sd-card
480 
481                             if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
482                                 // Points to a valid app (superset of cn != null) but the apk
483                                 // is not available.
484 
485                                 if (c.restoreFlag != 0) {
486                                     // Package is not yet available but might be
487                                     // installed later.
488                                     FileLog.d(TAG, "package not yet restored: " + targetPkg);
489 
490                                     tempPackageKey.update(targetPkg, c.user);
491                                     if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) {
492                                         // Restore has started once.
493                                     } else if (installingPkgs.containsKey(tempPackageKey)) {
494                                         // App restore has started. Update the flag
495                                         c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
496                                         c.updater().put(LauncherSettings.Favorites.RESTORED,
497                                                 c.restoreFlag).commit();
498                                     } else {
499                                         c.markDeleted("Unrestored app removed: " + targetPkg);
500                                         continue;
501                                     }
502                                 } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
503                                     // Package is present but not available.
504                                     disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE;
505                                     // Add the icon on the workspace anyway.
506                                     allowMissingTarget = true;
507                                 } else if (!isSdCardReady) {
508                                     // SdCard is not ready yet. Package might get available,
509                                     // once it is ready.
510                                     Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
511                                     mPendingPackages.add(new PackageUserKey(targetPkg, c.user));
512                                     // Add the icon on the workspace anyway.
513                                     allowMissingTarget = true;
514                                 } else {
515                                     // Do not wait for external media load anymore.
516                                     c.markDeleted("Invalid package removed: " + targetPkg);
517                                     continue;
518                                 }
519                             }
520 
521                             if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
522                                 validTarget = false;
523                             }
524 
525                             if (validTarget) {
526                                 // The shortcut points to a valid target (either no target
527                                 // or something which is ready to be used)
528                                 c.markRestored();
529                             }
530 
531                             boolean useLowResIcon = !c.isOnWorkspaceOrHotseat();
532 
533                             if (c.restoreFlag != 0) {
534                                 // Already verified above that user is same as default user
535                                 info = c.getRestoredItemInfo(intent);
536                             } else if (c.itemType ==
537                                     LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
538                                 info = c.getAppShortcutInfo(
539                                         intent, allowMissingTarget, useLowResIcon);
540                             } else if (c.itemType ==
541                                     LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
542 
543                                 ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
544                                 if (unlockedUsers.get(c.serialNumber)) {
545                                     ShortcutInfo pinnedShortcut =
546                                             shortcutKeyToPinnedShortcuts.get(key);
547                                     if (pinnedShortcut == null) {
548                                         // The shortcut is no longer valid.
549                                         c.markDeleted("Pinned shortcut not found");
550                                         continue;
551                                     }
552                                     info = new WorkspaceItemInfo(pinnedShortcut, context);
553                                     // If the pinned deep shortcut is no longer published,
554                                     // use the last saved icon instead of the default.
555                                     mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);
556 
557                                     if (pmHelper.isAppSuspended(
558                                             pinnedShortcut.getPackage(), info.user)) {
559                                         info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
560                                     }
561                                     intent = info.getIntent();
562                                     allDeepShortcuts.add(pinnedShortcut);
563                                 } else {
564                                     // Create a shortcut info in disabled mode for now.
565                                     info = c.loadSimpleWorkspaceItem();
566                                     info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
567                                 }
568                             } else { // item type == ITEM_TYPE_SHORTCUT
569                                 info = c.loadSimpleWorkspaceItem();
570 
571                                 // Shortcuts are only available on the primary profile
572                                 if (!TextUtils.isEmpty(targetPkg)
573                                         && pmHelper.isAppSuspended(targetPkg, c.user)) {
574                                     disabledState |= FLAG_DISABLED_SUSPENDED;
575                                 }
576 
577                                 // App shortcuts that used to be automatically added to Launcher
578                                 // didn't always have the correct intent flags set, so do that
579                                 // here
580                                 if (intent.getAction() != null &&
581                                     intent.getCategories() != null &&
582                                     intent.getAction().equals(Intent.ACTION_MAIN) &&
583                                     intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
584                                     intent.addFlags(
585                                         Intent.FLAG_ACTIVITY_NEW_TASK |
586                                         Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
587                                 }
588                             }
589 
590                             if (info != null) {
591                                 c.applyCommonProperties(info);
592 
593                                 info.intent = intent;
594                                 info.rank = c.getInt(rankIndex);
595                                 info.spanX = 1;
596                                 info.spanY = 1;
597                                 info.runtimeStatusFlags |= disabledState;
598                                 if (isSafeMode && !isSystemApp(context, intent)) {
599                                     info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
600                                 }
601                                     LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
602                                     if (activityInfo != null) {
603                                         info.setProgressLevel(
604                                                 PackageManagerHelper
605                                                     .getLoadingProgress(activityInfo),
606                                                 PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
607                                     }
608 
609                                 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
610                                     tempPackageKey.update(targetPkg, c.user);
611                                     SessionInfo si = installingPkgs.get(tempPackageKey);
612                                         if (si == null) {
613                                             info.runtimeStatusFlags &=
614                                                 ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
615                                         } else if (activityInfo == null) {
616                                             int installProgress = (int) (si.getProgress() * 100);
617 
618                                             info.setProgressLevel(
619                                                     installProgress,
620                                                     PackageInstallInfo.STATUS_INSTALLING);
621                                         }
622                                 }
623 
624                                 c.checkAndAddItem(info, mBgDataModel);
625                             } else {
626                                 throw new RuntimeException("Unexpected null WorkspaceItemInfo");
627                             }
628                             break;
629 
630                         case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
631                             FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
632                             c.applyCommonProperties(folderInfo);
633 
634                             // Do not trim the folder label, as is was set by the user.
635                             folderInfo.title = c.getString(c.titleIndex);
636                             folderInfo.spanX = 1;
637                             folderInfo.spanY = 1;
638                             folderInfo.options = c.getInt(optionsIndex);
639 
640                             // no special handling required for restored folders
641                             c.markRestored();
642 
643                             c.checkAndAddItem(folderInfo, mBgDataModel);
644                             break;
645 
646                         case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
647                             if (WidgetsModel.GO_DISABLE_WIDGETS) {
648                                 c.markDeleted("Only legacy shortcuts can have null package");
649                                 continue;
650                             }
651                             // Follow through
652                         case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
653                             // Read all Launcher-specific widget details
654                             boolean customWidget = c.itemType ==
655                                 LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
656 
657                             int appWidgetId = c.getInt(appWidgetIdIndex);
658                             String savedProvider = c.getString(appWidgetProviderIndex);
659                             final ComponentName component;
660 
661                             boolean isSearchWidget = (c.getInt(optionsIndex)
662                                     & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0;
663                             if (isSearchWidget) {
664                                 component  = QsbContainerView.getSearchComponentName(context);
665                                 if (component == null) {
666                                     c.markDeleted("Discarding SearchWidget without packagename ");
667                                     continue;
668                                 }
669                             } else {
670                                 component = ComponentName.unflattenFromString(savedProvider);
671                             }
672                             final boolean isIdValid = !c.hasRestoreFlag(
673                                     LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
674                             final boolean wasProviderReady = !c.hasRestoreFlag(
675                                     LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
676 
677                             ComponentKey providerKey = new ComponentKey(component, c.user);
678                             if (!mWidgetProvidersMap.containsKey(providerKey)) {
679                                 mWidgetProvidersMap.put(providerKey,
680                                         widgetHelper.findProvider(component, c.user));
681                             }
682                             final AppWidgetProviderInfo provider =
683                                     mWidgetProvidersMap.get(providerKey);
684 
685                             final boolean isProviderReady = isValidProvider(provider);
686                             if (!isSafeMode && !customWidget &&
687                                     wasProviderReady && !isProviderReady) {
688                                 c.markDeleted(
689                                         "Deleting widget that isn't installed anymore: "
690                                         + provider);
691                             } else {
692                                 if (isProviderReady) {
693                                     appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
694                                             provider.provider);
695 
696                                     // The provider is available. So the widget is either
697                                     // available or not available. We do not need to track
698                                     // any future restore updates.
699                                     int status = c.restoreFlag &
700                                             ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED &
701                                             ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
702                                     if (!wasProviderReady) {
703                                         // If provider was not previously ready, update the
704                                         // status and UI flag.
705 
706                                         // Id would be valid only if the widget restore broadcast was received.
707                                         if (isIdValid) {
708                                             status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
709                                         }
710                                     }
711                                     appWidgetInfo.restoreStatus = status;
712                                 } else {
713                                     Log.v(TAG, "Widget restore pending id=" + c.id
714                                             + " appWidgetId=" + appWidgetId
715                                             + " status =" + c.restoreFlag);
716                                     appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
717                                             component);
718                                     appWidgetInfo.restoreStatus = c.restoreFlag;
719 
720                                     tempPackageKey.update(component.getPackageName(), c.user);
721                                     SessionInfo si =
722                                             installingPkgs.get(tempPackageKey);
723                                     Integer installProgress = si == null
724                                             ? null
725                                             : (int) (si.getProgress() * 100);
726 
727                                     if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
728                                         // Restore has started once.
729                                     } else if (installProgress != null) {
730                                         // App restore has started. Update the flag
731                                         appWidgetInfo.restoreStatus |=
732                                                 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
733                                     } else if (!isSafeMode) {
734                                         c.markDeleted("Unrestored widget removed: " + component);
735                                         continue;
736                                     }
737 
738                                     appWidgetInfo.installProgress =
739                                             installProgress == null ? 0 : installProgress;
740                                 }
741                                 if (appWidgetInfo.hasRestoreFlag(
742                                         LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
743                                     appWidgetInfo.bindOptions = c.parseIntent();
744                                 }
745 
746                                 c.applyCommonProperties(appWidgetInfo);
747                                 appWidgetInfo.spanX = c.getInt(spanXIndex);
748                                 appWidgetInfo.spanY = c.getInt(spanYIndex);
749                                 appWidgetInfo.options = c.getInt(optionsIndex);
750                                 appWidgetInfo.user = c.user;
751                                 appWidgetInfo.sourceContainer = c.getInt(sourceContainerIndex);
752 
753                                 if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
754                                     c.markDeleted("Widget has invalid size: "
755                                             + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
756                                     continue;
757                                 }
758                                 widgetProviderInfo =
759                                         widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
760                                 if (widgetProviderInfo != null
761                                         && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
762                                         || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
763                                     FileLog.d(TAG, "Widget " + widgetProviderInfo.getComponent()
764                                             + " minSizes not meet: span=" + appWidgetInfo.spanX
765                                             + "x" + appWidgetInfo.spanY + " minSpan="
766                                             + widgetProviderInfo.minSpanX + "x"
767                                             + widgetProviderInfo.minSpanY);
768                                     logWidgetInfo(mApp.getInvariantDeviceProfile(),
769                                             widgetProviderInfo);
770                                 }
771                                 if (!c.isOnWorkspaceOrHotseat()) {
772                                     c.markDeleted("Widget found where container != " +
773                                             "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
774                                     continue;
775                                 }
776 
777                                 if (!customWidget) {
778                                     String providerName =
779                                             appWidgetInfo.providerName.flattenToString();
780                                     if (!providerName.equals(savedProvider) ||
781                                             (appWidgetInfo.restoreStatus != c.restoreFlag)) {
782                                         c.updater()
783                                                 .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
784                                                         providerName)
785                                                 .put(LauncherSettings.Favorites.RESTORED,
786                                                         appWidgetInfo.restoreStatus)
787                                                 .commit();
788                                     }
789                                 }
790 
791                                 if (appWidgetInfo.restoreStatus !=
792                                         LauncherAppWidgetInfo.RESTORE_COMPLETED) {
793                                     appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo(
794                                             appWidgetInfo.providerName);
795                                     appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
796                                     mIconCache.getTitleAndIconForApp(
797                                             appWidgetInfo.pendingItemInfo, false);
798                                 }
799 
800                                 c.checkAndAddItem(appWidgetInfo, mBgDataModel);
801                             }
802                             break;
803                         }
804                     } catch (Exception e) {
805                         Log.e(TAG, "Desktop items loading interrupted", e);
806                     }
807                 }
808             } finally {
809                 IOUtils.closeSilently(c);
810             }
811 
812             // Load delegate items
813             mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);
814 
815             // Break early if we've stopped loading
816             if (mStopped) {
817                 mBgDataModel.clear();
818                 return;
819             }
820 
821             // Remove dead items
822             mItemsDeleted = c.commitDeleted();
823 
824             // Sort the folder items, update ranks, and make sure all preview items are high res.
825             FolderGridOrganizer verifier =
826                     new FolderGridOrganizer(mApp.getInvariantDeviceProfile());
827             for (FolderInfo folder : mBgDataModel.folders) {
828                 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
829                 verifier.setFolderInfo(folder);
830                 int size = folder.contents.size();
831 
832                 // Update ranks here to ensure there are no gaps caused by removed folder items.
833                 // Ranks are the source of truth for folder items, so cellX and cellY can be ignored
834                 // for now. Database will be updated once user manually modifies folder.
835                 for (int rank = 0; rank < size; ++rank) {
836                     WorkspaceItemInfo info = folder.contents.get(rank);
837                     info.rank = rank;
838 
839                     if (info.usingLowResIcon()
840                             && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
841                             && verifier.isItemInPreview(info.rank)) {
842                         mIconCache.getTitleAndIcon(info, false);
843                     }
844                 }
845             }
846 
847             c.commitRestoredItems();
848         }
849     }
850 
setIgnorePackages(IconCacheUpdateHandler updateHandler)851     private void setIgnorePackages(IconCacheUpdateHandler updateHandler) {
852         // Ignore packages which have a promise icon.
853         synchronized (mBgDataModel) {
854             for (ItemInfo info : mBgDataModel.itemsIdMap) {
855                 if (info instanceof WorkspaceItemInfo) {
856                     WorkspaceItemInfo si = (WorkspaceItemInfo) info;
857                     if (si.isPromise() && si.getTargetComponent() != null) {
858                         updateHandler.addPackagesToIgnore(
859                                 si.user, si.getTargetComponent().getPackageName());
860                     }
861                 } else if (info instanceof LauncherAppWidgetInfo) {
862                     LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
863                     if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
864                         updateHandler.addPackagesToIgnore(
865                                 lawi.user, lawi.providerName.getPackageName());
866                     }
867                 }
868             }
869         }
870     }
871 
sanitizeData()872     private void sanitizeData() {
873         Context context = mApp.getContext();
874         ContentResolver contentResolver = context.getContentResolver();
875         if (mItemsDeleted) {
876             // Remove any empty folder
877             int[] deletedFolderIds = LauncherSettings.Settings
878                     .call(contentResolver,
879                             LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
880                     .getIntArray(LauncherSettings.Settings.EXTRA_VALUE);
881             synchronized (mBgDataModel) {
882                 for (int folderId : deletedFolderIds) {
883                     mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
884                     mBgDataModel.folders.remove(folderId);
885                     mBgDataModel.itemsIdMap.remove(folderId);
886                 }
887             }
888 
889         }
890         // Remove any ghost widgets
891         LauncherSettings.Settings.call(contentResolver,
892                 LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
893 
894         // Update pinned state of model shortcuts
895         mBgDataModel.updateShortcutPinnedState(context);
896 
897         if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) {
898             context.registerReceiver(
899                     new SdCardAvailableReceiver(mApp, mPendingPackages),
900                     new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
901                     null,
902                     MODEL_EXECUTOR.getHandler());
903         }
904     }
905 
loadAllApps()906     private List<LauncherActivityInfo> loadAllApps() {
907         final List<UserHandle> profiles = mUserCache.getUserProfiles();
908         List<LauncherActivityInfo> allActivityList = new ArrayList<>();
909         // Clear the list of apps
910         mBgAllAppsList.clear();
911         for (UserHandle user : profiles) {
912             // Query for the set of apps
913             final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
914             // Fail if we don't have any apps
915             // TODO: Fix this. Only fail for the current user.
916             if (apps == null || apps.isEmpty()) {
917                 return allActivityList;
918             }
919             boolean quietMode = mUserManagerState.isUserQuiet(user);
920             // Create the ApplicationInfos
921             for (int i = 0; i < apps.size(); i++) {
922                 LauncherActivityInfo app = apps.get(i);
923                 // This builds the icon bitmaps.
924                 mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
925             }
926             allActivityList.addAll(apps);
927         }
928 
929         if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
930             // get all active sessions and add them to the all apps list
931             for (PackageInstaller.SessionInfo info :
932                     mSessionHelper.getAllVerifiedSessions()) {
933                 mBgAllAppsList.addPromiseApp(mApp.getContext(),
934                         PackageInstallInfo.fromInstallingState(info));
935             }
936         }
937 
938         mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED,
939                 mUserManagerState.isAnyProfileQuietModeEnabled());
940         mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION,
941                 hasShortcutsPermission(mApp.getContext()));
942         mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION,
943                 mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
944                         == PackageManager.PERMISSION_GRANTED);
945 
946         mBgAllAppsList.getAndResetChangeFlag();
947         return allActivityList;
948     }
949 
loadDeepShortcuts()950     private List<ShortcutInfo> loadDeepShortcuts() {
951         List<ShortcutInfo> allShortcuts = new ArrayList<>();
952         mBgDataModel.deepShortcutMap.clear();
953 
954         if (mBgAllAppsList.hasShortcutHostPermission()) {
955             for (UserHandle user : mUserCache.getUserProfiles()) {
956                 if (mUserManager.isUserUnlocked(user)) {
957                     List<ShortcutInfo> shortcuts = new ShortcutRequest(mApp.getContext(), user)
958                             .query(ShortcutRequest.ALL);
959                     allShortcuts.addAll(shortcuts);
960                     mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
961                 }
962             }
963         }
964         return allShortcuts;
965     }
966 
loadFolderNames()967     private void loadFolderNames() {
968         FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
969                 mBgAllAppsList.data, mBgDataModel.folders);
970 
971         synchronized (mBgDataModel) {
972             for (int i = 0; i < mBgDataModel.folders.size(); i++) {
973                 FolderNameInfos suggestionInfos = new FolderNameInfos();
974                 FolderInfo info = mBgDataModel.folders.valueAt(i);
975                 if (info.suggestedFolderNames == null) {
976                     provider.getSuggestedFolderName(mApp.getContext(), info.contents,
977                             suggestionInfos);
978                     info.suggestedFolderNames = suggestionInfos;
979                 }
980             }
981         }
982     }
983 
isValidProvider(AppWidgetProviderInfo provider)984     public static boolean isValidProvider(AppWidgetProviderInfo provider) {
985         return (provider != null) && (provider.provider != null)
986                 && (provider.provider.getPackageName() != null);
987     }
988 
989     @SuppressLint("NewApi") // Already added API check.
logWidgetInfo(InvariantDeviceProfile idp, LauncherAppWidgetProviderInfo widgetProviderInfo)990     private static void logWidgetInfo(InvariantDeviceProfile idp,
991             LauncherAppWidgetProviderInfo widgetProviderInfo) {
992         Point cellSize = new Point();
993         for (DeviceProfile deviceProfile : idp.supportedProfiles) {
994             deviceProfile.getCellSize(cellSize);
995             FileLog.d(TAG, "DeviceProfile available width: " + deviceProfile.availableWidthPx
996                     + ", available height: " + deviceProfile.availableHeightPx
997                     + ", cellLayoutBorderSpacingPx: " + deviceProfile.cellLayoutBorderSpacingPx
998                     + ", cellSize: " + cellSize);
999         }
1000 
1001         StringBuilder widgetDimension = new StringBuilder();
1002         widgetDimension.append("Widget dimensions:\n")
1003                 .append("minResizeWidth: ")
1004                 .append(widgetProviderInfo.minResizeWidth)
1005                 .append("\n")
1006                 .append("minResizeHeight: ")
1007                 .append(widgetProviderInfo.minResizeHeight)
1008                 .append("\n")
1009                 .append("defaultWidth: ")
1010                 .append(widgetProviderInfo.minWidth)
1011                 .append("\n")
1012                 .append("defaultHeight: ")
1013                 .append(widgetProviderInfo.minHeight)
1014                 .append("\n");
1015         if (Utilities.ATLEAST_S) {
1016             widgetDimension.append("targetCellWidth: ")
1017                     .append(widgetProviderInfo.targetCellWidth)
1018                     .append("\n")
1019                     .append("targetCellHeight: ")
1020                     .append(widgetProviderInfo.targetCellHeight)
1021                     .append("\n")
1022                     .append("maxResizeWidth: ")
1023                     .append(widgetProviderInfo.maxResizeWidth)
1024                     .append("\n")
1025                     .append("maxResizeHeight: ")
1026                     .append(widgetProviderInfo.maxResizeHeight)
1027                     .append("\n");
1028         }
1029         FileLog.d(TAG, widgetDimension.toString());
1030     }
1031 
logASplit(final TimingLogger logger, final String label)1032     private static void logASplit(final TimingLogger logger, final String label) {
1033         logger.addSplit(label);
1034         if (DEBUG) {
1035             Log.d(TAG, label);
1036         }
1037     }
1038 }
1039