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