Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2008 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;
     18 
     19 import android.appwidget.AppWidgetProviderInfo;
     20 import android.content.BroadcastReceiver;
     21 import android.content.ComponentName;
     22 import android.content.ContentProviderOperation;
     23 import android.content.ContentResolver;
     24 import android.content.ContentValues;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.LauncherActivityInfo;
     29 import android.net.Uri;
     30 import android.os.Handler;
     31 import android.os.HandlerThread;
     32 import android.os.Looper;
     33 import android.os.Process;
     34 import android.os.SystemClock;
     35 import android.os.Trace;
     36 import android.os.UserHandle;
     37 import android.support.annotation.Nullable;
     38 import android.text.TextUtils;
     39 import android.util.Log;
     40 import android.util.LongSparseArray;
     41 import android.util.MutableInt;
     42 
     43 import com.android.launcher3.compat.AppWidgetManagerCompat;
     44 import com.android.launcher3.compat.LauncherAppsCompat;
     45 import com.android.launcher3.compat.PackageInstallerCompat;
     46 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
     47 import com.android.launcher3.compat.UserManagerCompat;
     48 import com.android.launcher3.config.ProviderConfig;
     49 import com.android.launcher3.dynamicui.ExtractionUtils;
     50 import com.android.launcher3.folder.Folder;
     51 import com.android.launcher3.folder.FolderIcon;
     52 import com.android.launcher3.graphics.LauncherIcons;
     53 import com.android.launcher3.logging.FileLog;
     54 import com.android.launcher3.model.AddWorkspaceItemsTask;
     55 import com.android.launcher3.model.BgDataModel;
     56 import com.android.launcher3.model.CacheDataUpdatedTask;
     57 import com.android.launcher3.model.ExtendedModelTask;
     58 import com.android.launcher3.model.GridSizeMigrationTask;
     59 import com.android.launcher3.model.LoaderCursor;
     60 import com.android.launcher3.model.ModelWriter;
     61 import com.android.launcher3.model.PackageInstallStateChangedTask;
     62 import com.android.launcher3.model.PackageItemInfo;
     63 import com.android.launcher3.model.PackageUpdatedTask;
     64 import com.android.launcher3.model.SdCardAvailableReceiver;
     65 import com.android.launcher3.model.ShortcutsChangedTask;
     66 import com.android.launcher3.model.UserLockStateChangedTask;
     67 import com.android.launcher3.model.WidgetItem;
     68 import com.android.launcher3.model.WidgetsModel;
     69 import com.android.launcher3.provider.ImportDataTask;
     70 import com.android.launcher3.provider.LauncherDbUtils;
     71 import com.android.launcher3.shortcuts.DeepShortcutManager;
     72 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
     73 import com.android.launcher3.shortcuts.ShortcutKey;
     74 import com.android.launcher3.util.ComponentKey;
     75 import com.android.launcher3.util.ManagedProfileHeuristic;
     76 import com.android.launcher3.util.MultiHashMap;
     77 import com.android.launcher3.util.PackageManagerHelper;
     78 import com.android.launcher3.util.PackageUserKey;
     79 import com.android.launcher3.util.Preconditions;
     80 import com.android.launcher3.util.Provider;
     81 import com.android.launcher3.util.Thunk;
     82 import com.android.launcher3.util.ViewOnDrawExecutor;
     83 
     84 import java.io.FileDescriptor;
     85 import java.io.PrintWriter;
     86 import java.lang.ref.WeakReference;
     87 import java.util.ArrayList;
     88 import java.util.Collections;
     89 import java.util.Comparator;
     90 import java.util.HashMap;
     91 import java.util.HashSet;
     92 import java.util.Iterator;
     93 import java.util.List;
     94 import java.util.Map;
     95 import java.util.Set;
     96 import java.util.concurrent.CancellationException;
     97 import java.util.concurrent.Executor;
     98 
     99 /**
    100  * Maintains in-memory state of the Launcher. It is expected that there should be only one
    101  * LauncherModel object held in a static. Also provide APIs for updating the database state
    102  * for the Launcher.
    103  */
    104 public class LauncherModel extends BroadcastReceiver
    105         implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
    106     static final boolean DEBUG_LOADERS = false;
    107     private static final boolean DEBUG_RECEIVER = false;
    108 
    109     static final String TAG = "Launcher.Model";
    110 
    111     private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
    112     private static final long INVALID_SCREEN_ID = -1L;
    113 
    114     @Thunk final LauncherAppState mApp;
    115     @Thunk final Object mLock = new Object();
    116     @Thunk DeferredHandler mHandler = new DeferredHandler();
    117     @Thunk LoaderTask mLoaderTask;
    118     @Thunk boolean mIsLoaderTaskRunning;
    119     @Thunk boolean mHasLoaderCompletedOnce;
    120     @Thunk boolean mIsManagedHeuristicAppsUpdated;
    121 
    122     @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
    123     static {
    124         sWorkerThread.start();
    125     }
    126     @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
    127 
    128     // Indicates whether the current model data is valid or not.
    129     // We start off with everything not loaded. After that, we assume that
    130     // our monitoring of the package manager provides all updates and we never
    131     // need to do a requery. This is only ever touched from the loader thread.
    132     private boolean mModelLoaded;
    133     public boolean isModelLoaded() {
    134         synchronized (mLock) {
    135             return mModelLoaded && mLoaderTask == null;
    136         }
    137     }
    138 
    139     /**
    140      * Set of runnables to be called on the background thread after the workspace binding
    141      * is complete.
    142      */
    143     static final ArrayList<Runnable> mBindCompleteRunnables = new ArrayList<Runnable>();
    144 
    145     @Thunk WeakReference<Callbacks> mCallbacks;
    146 
    147     // < only access in worker thread >
    148     private final AllAppsList mBgAllAppsList;
    149     // Entire list of widgets.
    150     private final WidgetsModel mBgWidgetsModel;
    151 
    152     private boolean mHasShortcutHostPermission;
    153     // Runnable to check if the shortcuts permission has changed.
    154     private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
    155         @Override
    156         public void run() {
    157             if (mModelLoaded) {
    158                 boolean hasShortcutHostPermission =
    159                         DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
    160                 if (hasShortcutHostPermission != mHasShortcutHostPermission) {
    161                     forceReload();
    162                 }
    163             }
    164         }
    165     };
    166 
    167     /**
    168      * All the static data should be accessed on the background thread, A lock should be acquired
    169      * on this object when accessing any data from this model.
    170      */
    171     static final BgDataModel sBgDataModel = new BgDataModel();
    172 
    173     // </ only access in worker thread >
    174 
    175     private final IconCache mIconCache;
    176 
    177     private final LauncherAppsCompat mLauncherApps;
    178     private final UserManagerCompat mUserManager;
    179 
    180     public interface Callbacks {
    181         public boolean setLoadOnResume();
    182         public int getCurrentWorkspaceScreen();
    183         public void clearPendingBinds();
    184         public void startBinding();
    185         public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
    186                               boolean forceAnimateIcons);
    187         public void bindScreens(ArrayList<Long> orderedScreenIds);
    188         public void finishFirstPageBind(ViewOnDrawExecutor executor);
    189         public void finishBindingItems();
    190         public void bindAppWidget(LauncherAppWidgetInfo info);
    191         public void bindAllApplications(ArrayList<AppInfo> apps);
    192         public void bindAppsAdded(ArrayList<Long> newScreens,
    193                                   ArrayList<ItemInfo> addNotAnimated,
    194                                   ArrayList<ItemInfo> addAnimated,
    195                                   ArrayList<AppInfo> addedApps);
    196         public void bindAppsUpdated(ArrayList<AppInfo> apps);
    197         public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
    198                 ArrayList<ShortcutInfo> removed, UserHandle user);
    199         public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
    200         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
    201         public void bindWorkspaceComponentsRemoved(
    202                 HashSet<String> packageNames, HashSet<ComponentName> components,
    203                 UserHandle user);
    204         public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
    205         public void notifyWidgetProvidersChanged();
    206         public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets);
    207         public void onPageBoundSynchronously(int page);
    208         public void executeOnNextDraw(ViewOnDrawExecutor executor);
    209         public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap);
    210     }
    211 
    212     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
    213         Context context = app.getContext();
    214         mApp = app;
    215         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
    216         mBgWidgetsModel = new WidgetsModel(iconCache, appFilter);
    217         mIconCache = iconCache;
    218 
    219         mLauncherApps = LauncherAppsCompat.getInstance(context);
    220         mUserManager = UserManagerCompat.getInstance(context);
    221     }
    222 
    223     /** Runs the specified runnable immediately if called from the main thread, otherwise it is
    224      * posted on the main thread handler. */
    225     private void runOnMainThread(Runnable r) {
    226         if (sWorkerThread.getThreadId() == Process.myTid()) {
    227             // If we are on the worker thread, post onto the main handler
    228             mHandler.post(r);
    229         } else {
    230             r.run();
    231         }
    232     }
    233 
    234     /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
    235      * posted on the worker thread handler. */
    236     private static void runOnWorkerThread(Runnable r) {
    237         if (sWorkerThread.getThreadId() == Process.myTid()) {
    238             r.run();
    239         } else {
    240             // If we are not on the worker thread, then post to the worker handler
    241             sWorker.post(r);
    242         }
    243     }
    244 
    245     public void setPackageState(PackageInstallInfo installInfo) {
    246         enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
    247     }
    248 
    249     /**
    250      * Updates the icons and label of all pending icons for the provided package name.
    251      */
    252     public void updateSessionDisplayInfo(final String packageName) {
    253         HashSet<String> packages = new HashSet<>();
    254         packages.add(packageName);
    255         enqueueModelUpdateTask(new CacheDataUpdatedTask(
    256                 CacheDataUpdatedTask.OP_SESSION_UPDATE, Process.myUserHandle(), packages));
    257     }
    258 
    259     /**
    260      * Adds the provided items to the workspace.
    261      */
    262     public void addAndBindAddedWorkspaceItems(List<ItemInfo> workspaceApps) {
    263         addAndBindAddedWorkspaceItems(Provider.of(workspaceApps));
    264     }
    265 
    266     /**
    267      * Adds the provided items to the workspace.
    268      */
    269     public void addAndBindAddedWorkspaceItems(
    270             Provider<List<ItemInfo>> appsProvider) {
    271         enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
    272     }
    273 
    274     public ModelWriter getWriter(boolean hasVerticalHotseat) {
    275         return new ModelWriter(mApp.getContext(), sBgDataModel, hasVerticalHotseat);
    276     }
    277 
    278     static void checkItemInfoLocked(
    279             final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
    280         ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
    281         if (modelItem != null && item != modelItem) {
    282             // check all the data is consistent
    283             if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
    284                 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
    285                 ShortcutInfo shortcut = (ShortcutInfo) item;
    286                 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
    287                         modelShortcut.intent.filterEquals(shortcut.intent) &&
    288                         modelShortcut.id == shortcut.id &&
    289                         modelShortcut.itemType == shortcut.itemType &&
    290                         modelShortcut.container == shortcut.container &&
    291                         modelShortcut.screenId == shortcut.screenId &&
    292                         modelShortcut.cellX == shortcut.cellX &&
    293                         modelShortcut.cellY == shortcut.cellY &&
    294                         modelShortcut.spanX == shortcut.spanX &&
    295                         modelShortcut.spanY == shortcut.spanY) {
    296                     // For all intents and purposes, this is the same object
    297                     return;
    298                 }
    299             }
    300 
    301             // the modelItem needs to match up perfectly with item if our model is
    302             // to be consistent with the database-- for now, just require
    303             // modelItem == item or the equality check above
    304             String msg = "item: " + ((item != null) ? item.toString() : "null") +
    305                     "modelItem: " +
    306                     ((modelItem != null) ? modelItem.toString() : "null") +
    307                     "Error: ItemInfo passed to checkItemInfo doesn't match original";
    308             RuntimeException e = new RuntimeException(msg);
    309             if (stackTrace != null) {
    310                 e.setStackTrace(stackTrace);
    311             }
    312             throw e;
    313         }
    314     }
    315 
    316     static void checkItemInfo(final ItemInfo item) {
    317         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
    318         final long itemId = item.id;
    319         Runnable r = new Runnable() {
    320             public void run() {
    321                 synchronized (sBgDataModel) {
    322                     checkItemInfoLocked(itemId, item, stackTrace);
    323                 }
    324             }
    325         };
    326         runOnWorkerThread(r);
    327     }
    328 
    329     /**
    330      * Update the order of the workspace screens in the database. The array list contains
    331      * a list of screen ids in the order that they should appear.
    332      */
    333     public static void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
    334         final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
    335         final ContentResolver cr = context.getContentResolver();
    336         final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
    337 
    338         // Remove any negative screen ids -- these aren't persisted
    339         Iterator<Long> iter = screensCopy.iterator();
    340         while (iter.hasNext()) {
    341             long id = iter.next();
    342             if (id < 0) {
    343                 iter.remove();
    344             }
    345         }
    346 
    347         Runnable r = new Runnable() {
    348             @Override
    349             public void run() {
    350                 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
    351                 // Clear the table
    352                 ops.add(ContentProviderOperation.newDelete(uri).build());
    353                 int count = screensCopy.size();
    354                 for (int i = 0; i < count; i++) {
    355                     ContentValues v = new ContentValues();
    356                     long screenId = screensCopy.get(i);
    357                     v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
    358                     v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
    359                     ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
    360                 }
    361 
    362                 try {
    363                     cr.applyBatch(LauncherProvider.AUTHORITY, ops);
    364                 } catch (Exception ex) {
    365                     throw new RuntimeException(ex);
    366                 }
    367 
    368                 synchronized (sBgDataModel) {
    369                     sBgDataModel.workspaceScreens.clear();
    370                     sBgDataModel.workspaceScreens.addAll(screensCopy);
    371                 }
    372             }
    373         };
    374         runOnWorkerThread(r);
    375     }
    376 
    377     /**
    378      * Set this as the current Launcher activity object for the loader.
    379      */
    380     public void initialize(Callbacks callbacks) {
    381         synchronized (mLock) {
    382             Preconditions.assertUIThread();
    383             // Remove any queued UI runnables
    384             mHandler.cancelAll();
    385             mCallbacks = new WeakReference<>(callbacks);
    386         }
    387     }
    388 
    389     @Override
    390     public void onPackageChanged(String packageName, UserHandle user) {
    391         int op = PackageUpdatedTask.OP_UPDATE;
    392         enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
    393     }
    394 
    395     @Override
    396     public void onPackageRemoved(String packageName, UserHandle user) {
    397         onPackagesRemoved(user, packageName);
    398     }
    399 
    400     public void onPackagesRemoved(UserHandle user, String... packages) {
    401         int op = PackageUpdatedTask.OP_REMOVE;
    402         enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
    403     }
    404 
    405     @Override
    406     public void onPackageAdded(String packageName, UserHandle user) {
    407         int op = PackageUpdatedTask.OP_ADD;
    408         enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
    409     }
    410 
    411     @Override
    412     public void onPackagesAvailable(String[] packageNames, UserHandle user,
    413             boolean replacing) {
    414         enqueueModelUpdateTask(
    415                 new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageNames));
    416     }
    417 
    418     @Override
    419     public void onPackagesUnavailable(String[] packageNames, UserHandle user,
    420             boolean replacing) {
    421         if (!replacing) {
    422             enqueueModelUpdateTask(new PackageUpdatedTask(
    423                     PackageUpdatedTask.OP_UNAVAILABLE, user, packageNames));
    424         }
    425     }
    426 
    427     @Override
    428     public void onPackagesSuspended(String[] packageNames, UserHandle user) {
    429         enqueueModelUpdateTask(new PackageUpdatedTask(
    430                 PackageUpdatedTask.OP_SUSPEND, user, packageNames));
    431     }
    432 
    433     @Override
    434     public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
    435         enqueueModelUpdateTask(new PackageUpdatedTask(
    436                 PackageUpdatedTask.OP_UNSUSPEND, user, packageNames));
    437     }
    438 
    439     @Override
    440     public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
    441             UserHandle user) {
    442         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
    443     }
    444 
    445     public void updatePinnedShortcuts(String packageName, List<ShortcutInfoCompat> shortcuts,
    446             UserHandle user) {
    447         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
    448     }
    449 
    450     /**
    451      * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
    452      * ACTION_PACKAGE_CHANGED.
    453      */
    454     @Override
    455     public void onReceive(Context context, Intent intent) {
    456         if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
    457 
    458         final String action = intent.getAction();
    459         if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
    460             // If we have changed locale we need to clear out the labels in all apps/workspace.
    461             forceReload();
    462         } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
    463                 || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
    464             UserManagerCompat.getInstance(context).enableAndResetCache();
    465             forceReload();
    466         } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
    467                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
    468                 Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
    469             UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
    470             if (user != null) {
    471                 if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
    472                         Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
    473                     enqueueModelUpdateTask(new PackageUpdatedTask(
    474                             PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
    475                 }
    476 
    477                 // ACTION_MANAGED_PROFILE_UNAVAILABLE sends the profile back to locked mode, so
    478                 // we need to run the state change task again.
    479                 if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
    480                         Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
    481                     enqueueModelUpdateTask(new UserLockStateChangedTask(user));
    482                 }
    483             }
    484         } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) {
    485             ExtractionUtils.startColorExtractionServiceIfNecessary(context);
    486         }
    487     }
    488 
    489     /**
    490      * Reloads the workspace items from the DB and re-binds the workspace. This should generally
    491      * not be called as DB updates are automatically followed by UI update
    492      */
    493     public void forceReload() {
    494         synchronized (mLock) {
    495             // Stop any existing loaders first, so they don't set mModelLoaded to true later
    496             stopLoaderLocked();
    497             mModelLoaded = false;
    498         }
    499 
    500         // Do this here because if the launcher activity is running it will be restarted.
    501         // If it's not running startLoaderFromBackground will merely tell it that it needs
    502         // to reload.
    503         startLoaderFromBackground();
    504     }
    505 
    506     /**
    507      * When the launcher is in the background, it's possible for it to miss paired
    508      * configuration changes.  So whenever we trigger the loader from the background
    509      * tell the launcher that it needs to re-run the loader when it comes back instead
    510      * of doing it now.
    511      */
    512     public void startLoaderFromBackground() {
    513         Callbacks callbacks = getCallback();
    514         if (callbacks != null) {
    515             // Only actually run the loader if they're not paused.
    516             if (!callbacks.setLoadOnResume()) {
    517                 startLoader(callbacks.getCurrentWorkspaceScreen());
    518             }
    519         }
    520     }
    521 
    522     /**
    523      * If there is already a loader task running, tell it to stop.
    524      */
    525     private void stopLoaderLocked() {
    526         LoaderTask oldTask = mLoaderTask;
    527         if (oldTask != null) {
    528             oldTask.stopLocked();
    529         }
    530     }
    531 
    532     public boolean isCurrentCallbacks(Callbacks callbacks) {
    533         return (mCallbacks != null && mCallbacks.get() == callbacks);
    534     }
    535 
    536     /**
    537      * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible.
    538      * @return true if the page could be bound synchronously.
    539      */
    540     public boolean startLoader(int synchronousBindPage) {
    541         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
    542         InstallShortcutReceiver.enableInstallQueue();
    543         synchronized (mLock) {
    544             // Don't bother to start the thread if we know it's not going to do anything
    545             if (mCallbacks != null && mCallbacks.get() != null) {
    546                 final Callbacks oldCallbacks = mCallbacks.get();
    547                 // Clear any pending bind-runnables from the synchronized load process.
    548                 runOnMainThread(new Runnable() {
    549                     public void run() {
    550                         oldCallbacks.clearPendingBinds();
    551                     }
    552                 });
    553 
    554                 // If there is already one running, tell it to stop.
    555                 stopLoaderLocked();
    556                 mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
    557                 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
    558                         && mModelLoaded && !mIsLoaderTaskRunning) {
    559                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
    560                     return true;
    561                 } else {
    562                     sWorkerThread.setPriority(Thread.NORM_PRIORITY);
    563                     sWorker.post(mLoaderTask);
    564                 }
    565             }
    566         }
    567         return false;
    568     }
    569 
    570     public void stopLoader() {
    571         synchronized (mLock) {
    572             if (mLoaderTask != null) {
    573                 mLoaderTask.stopLocked();
    574             }
    575         }
    576     }
    577 
    578     /**
    579      * Loads the workspace screen ids in an ordered list.
    580      */
    581     public static ArrayList<Long> loadWorkspaceScreensDb(Context context) {
    582         final ContentResolver contentResolver = context.getContentResolver();
    583         final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
    584 
    585         // Get screens ordered by rank.
    586         return LauncherDbUtils.getScreenIdsFromCursor(contentResolver.query(
    587                 screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
    588     }
    589 
    590     /**
    591      * Runnable for the thread that loads the contents of the launcher:
    592      *   - workspace icons
    593      *   - widgets
    594      *   - all apps icons
    595      *   - deep shortcuts within apps
    596      */
    597     private class LoaderTask implements Runnable {
    598         private Context mContext;
    599         private int mPageToBindFirst;
    600 
    601         @Thunk boolean mIsLoadingAndBindingWorkspace;
    602         private boolean mStopped;
    603         @Thunk boolean mLoadAndBindStepFinished;
    604 
    605         LoaderTask(Context context, int pageToBindFirst) {
    606             mContext = context;
    607             mPageToBindFirst = pageToBindFirst;
    608         }
    609 
    610         private void waitForIdle() {
    611             // Wait until the either we're stopped or the other threads are done.
    612             // This way we don't start loading all apps until the workspace has settled
    613             // down.
    614             synchronized (LoaderTask.this) {
    615                 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
    616 
    617                 mHandler.postIdle(new Runnable() {
    618                         public void run() {
    619                             synchronized (LoaderTask.this) {
    620                                 mLoadAndBindStepFinished = true;
    621                                 if (DEBUG_LOADERS) {
    622                                     Log.d(TAG, "done with previous binding step");
    623                                 }
    624                                 LoaderTask.this.notify();
    625                             }
    626                         }
    627                     });
    628 
    629                 while (!mStopped && !mLoadAndBindStepFinished) {
    630                     try {
    631                         // Just in case mFlushingWorkerThread changes but we aren't woken up,
    632                         // wait no longer than 1sec at a time
    633                         this.wait(1000);
    634                     } catch (InterruptedException ex) {
    635                         // Ignore
    636                     }
    637                 }
    638                 if (DEBUG_LOADERS) {
    639                     Log.d(TAG, "waited "
    640                             + (SystemClock.uptimeMillis()-workspaceWaitTime)
    641                             + "ms for previous step to finish binding");
    642                 }
    643             }
    644         }
    645 
    646         void runBindSynchronousPage(int synchronousBindPage) {
    647             if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
    648                 // Ensure that we have a valid page index to load synchronously
    649                 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
    650                         "valid page index");
    651             }
    652             if (!mModelLoaded) {
    653                 // Ensure that we don't try and bind a specified page when the pages have not been
    654                 // loaded already (we should load everything asynchronously in that case)
    655                 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
    656             }
    657             synchronized (mLock) {
    658                 if (mIsLoaderTaskRunning) {
    659                     // Ensure that we are never running the background loading at this point since
    660                     // we also touch the background collections
    661                     throw new RuntimeException("Error! Background loading is already running");
    662                 }
    663             }
    664 
    665             // XXX: Throw an exception if we are already loading (since we touch the worker thread
    666             //      data structures, we can't allow any other thread to touch that data, but because
    667             //      this call is synchronous, we can get away with not locking).
    668 
    669             // The LauncherModel is static in the LauncherAppState and mHandler may have queued
    670             // operations from the previous activity.  We need to ensure that all queued operations
    671             // are executed before any synchronous binding work is done.
    672             mHandler.flush();
    673 
    674             // Divide the set of loaded items into those that we are binding synchronously, and
    675             // everything else that is to be bound normally (asynchronously).
    676             bindWorkspace(synchronousBindPage);
    677             // XXX: For now, continue posting the binding of AllApps as there are other issues that
    678             //      arise from that.
    679             onlyBindAllApps();
    680 
    681             bindDeepShortcuts();
    682         }
    683 
    684         private void verifyNotStopped() throws CancellationException {
    685             synchronized (LoaderTask.this) {
    686                 if (mStopped) {
    687                     throw new CancellationException("Loader stopped");
    688                 }
    689             }
    690         }
    691 
    692         public void run() {
    693             synchronized (mLock) {
    694                 if (mStopped) {
    695                     return;
    696                 }
    697                 mIsLoaderTaskRunning = true;
    698             }
    699 
    700             try {
    701                 if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
    702                 // Set to false in bindWorkspace()
    703                 mIsLoadingAndBindingWorkspace = true;
    704                 loadWorkspace();
    705 
    706                 verifyNotStopped();
    707                 if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
    708                 bindWorkspace(mPageToBindFirst);
    709 
    710                 // Take a break
    711                 if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle");
    712                 waitForIdle();
    713                 verifyNotStopped();
    714 
    715                 // second step
    716                 if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
    717                 loadAllApps();
    718 
    719                 verifyNotStopped();
    720                 if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Update icon cache");
    721                 updateIconCache();
    722 
    723                 // Take a break
    724                 if (DEBUG_LOADERS) Log.d(TAG, "step 2 completed, wait for idle");
    725                 waitForIdle();
    726                 verifyNotStopped();
    727 
    728                 // third step
    729                 if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
    730                 loadDeepShortcuts();
    731 
    732                 verifyNotStopped();
    733                 if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
    734                 bindDeepShortcuts();
    735 
    736                 // Take a break
    737                 if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
    738                 waitForIdle();
    739                 verifyNotStopped();
    740 
    741                 // fourth step
    742                 if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
    743                 refreshAndBindWidgetsAndShortcuts(getCallback(), false /* bindFirst */,
    744                         null /* packageUser */);
    745 
    746                 synchronized (mLock) {
    747                     // Everything loaded bind the data.
    748                     mModelLoaded = true;
    749                     mHasLoaderCompletedOnce = true;
    750                 }
    751             } catch (CancellationException e) {
    752               // Loader stopped, ignore
    753             } finally {
    754                 // Clear out this reference, otherwise we end up holding it until all of the
    755                 // callback runnables are done.
    756                 mContext = null;
    757 
    758                 synchronized (mLock) {
    759                     // If we are still the last one to be scheduled, remove ourselves.
    760                     if (mLoaderTask == this) {
    761                         mLoaderTask = null;
    762                     }
    763                     mIsLoaderTaskRunning = false;
    764                 }
    765             }
    766         }
    767 
    768         public void stopLocked() {
    769             synchronized (LoaderTask.this) {
    770                 mStopped = true;
    771                 this.notify();
    772             }
    773         }
    774 
    775         /**
    776          * Gets the callbacks object.  If we've been stopped, or if the launcher object
    777          * has somehow been garbage collected, return null instead.  Pass in the Callbacks
    778          * object that was around when the deferred message was scheduled, and if there's
    779          * a new Callbacks object around then also return null.  This will save us from
    780          * calling onto it with data that will be ignored.
    781          */
    782         Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
    783             synchronized (mLock) {
    784                 if (mStopped) {
    785                     return null;
    786                 }
    787 
    788                 if (mCallbacks == null) {
    789                     return null;
    790                 }
    791 
    792                 final Callbacks callbacks = mCallbacks.get();
    793                 if (callbacks != oldCallbacks) {
    794                     return null;
    795                 }
    796                 if (callbacks == null) {
    797                     Log.w(TAG, "no mCallbacks");
    798                     return null;
    799                 }
    800 
    801                 return callbacks;
    802             }
    803         }
    804 
    805         private void loadWorkspace() {
    806             if (LauncherAppState.PROFILE_STARTUP) {
    807                 Trace.beginSection("Loading Workspace");
    808             }
    809 
    810             final Context context = mContext;
    811             final ContentResolver contentResolver = context.getContentResolver();
    812             final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
    813             final boolean isSafeMode = pmHelper.isSafeMode();
    814             final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
    815             final DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(context);
    816             final boolean isSdCardReady = Utilities.isBootCompleted();
    817             final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
    818 
    819             boolean clearDb = false;
    820             try {
    821                 ImportDataTask.performImportIfPossible(context);
    822             } catch (Exception e) {
    823                 // Migration failed. Clear workspace.
    824                 clearDb = true;
    825             }
    826 
    827             if (!clearDb && GridSizeMigrationTask.ENABLED &&
    828                     !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
    829                 // Migration failed. Clear workspace.
    830                 clearDb = true;
    831             }
    832 
    833             if (clearDb) {
    834                 Log.d(TAG, "loadWorkspace: resetting launcher database");
    835                 LauncherSettings.Settings.call(contentResolver,
    836                         LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
    837             }
    838 
    839             Log.d(TAG, "loadWorkspace: loading default favorites");
    840             LauncherSettings.Settings.call(contentResolver,
    841                     LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
    842 
    843             synchronized (sBgDataModel) {
    844                 sBgDataModel.clear();
    845 
    846                 final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
    847                         .getInstance(mContext).updateAndGetActiveSessionCache();
    848                 sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
    849 
    850                 Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
    851                 final LoaderCursor c = new LoaderCursor(contentResolver.query(
    852                         LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
    853 
    854                 HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
    855 
    856                 try {
    857                     final int appWidgetIdIndex = c.getColumnIndexOrThrow(
    858                             LauncherSettings.Favorites.APPWIDGET_ID);
    859                     final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
    860                             LauncherSettings.Favorites.APPWIDGET_PROVIDER);
    861                     final int spanXIndex = c.getColumnIndexOrThrow
    862                             (LauncherSettings.Favorites.SPANX);
    863                     final int spanYIndex = c.getColumnIndexOrThrow(
    864                             LauncherSettings.Favorites.SPANY);
    865                     final int rankIndex = c.getColumnIndexOrThrow(
    866                             LauncherSettings.Favorites.RANK);
    867                     final int optionsIndex = c.getColumnIndexOrThrow(
    868                             LauncherSettings.Favorites.OPTIONS);
    869 
    870                     final LongSparseArray<UserHandle> allUsers = c.allUsers;
    871                     final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
    872                     final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
    873                     for (UserHandle user : mUserManager.getUserProfiles()) {
    874                         long serialNo = mUserManager.getSerialNumberForUser(user);
    875                         allUsers.put(serialNo, user);
    876                         quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
    877 
    878                         boolean userUnlocked = mUserManager.isUserUnlocked(user);
    879 
    880                         // We can only query for shortcuts when the user is unlocked.
    881                         if (userUnlocked) {
    882                             List<ShortcutInfoCompat> pinnedShortcuts =
    883                                     shortcutManager.queryForPinnedShortcuts(null, user);
    884                             if (shortcutManager.wasLastCallSuccess()) {
    885                                 for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
    886                                     shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
    887                                             shortcut);
    888                                 }
    889                             } else {
    890                                 // Shortcut manager can fail due to some race condition when the
    891                                 // lock state changes too frequently. For the purpose of the loading
    892                                 // shortcuts, consider the user is still locked.
    893                                 userUnlocked = false;
    894                             }
    895                         }
    896                         unlockedUsers.put(serialNo, userUnlocked);
    897                     }
    898 
    899                     ShortcutInfo info;
    900                     LauncherAppWidgetInfo appWidgetInfo;
    901                     Intent intent;
    902                     String targetPkg;
    903 
    904                     while (!mStopped && c.moveToNext()) {
    905                         try {
    906                             if (c.user == null) {
    907                                 // User has been deleted, remove the item.
    908                                 c.markDeleted("User has been deleted");
    909                                 continue;
    910                             }
    911 
    912                             boolean allowMissingTarget = false;
    913                             switch (c.itemType) {
    914                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
    915                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
    916                             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
    917                                 intent = c.parseIntent();
    918                                 if (intent == null) {
    919                                     c.markDeleted("Invalid or null intent");
    920                                     continue;
    921                                 }
    922 
    923                                 int disabledState = quietMode.get(c.serialNumber) ?
    924                                         ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
    925                                 ComponentName cn = intent.getComponent();
    926                                 targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
    927 
    928                                 if (!Process.myUserHandle().equals(c.user)) {
    929                                     if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
    930                                         c.markDeleted("Legacy shortcuts are only allowed for default user");
    931                                         continue;
    932                                     } else if (c.restoreFlag != 0) {
    933                                         // Don't restore items for other profiles.
    934                                         c.markDeleted("Restore from managed profile not supported");
    935                                         continue;
    936                                     }
    937                                 }
    938                                 if (TextUtils.isEmpty(targetPkg) &&
    939                                         c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
    940                                     c.markDeleted("Only legacy shortcuts can have null package");
    941                                     continue;
    942                                 }
    943 
    944                                 // If there is no target package, its an implicit intent
    945                                 // (legacy shortcut) which is always valid
    946                                 boolean validTarget = TextUtils.isEmpty(targetPkg) ||
    947                                         launcherApps.isPackageEnabledForProfile(targetPkg, c.user);
    948 
    949                                 if (cn != null && validTarget) {
    950                                     // If the apk is present and the shortcut points to a specific
    951                                     // component.
    952 
    953                                     // If the component is already present
    954                                     if (launcherApps.isActivityEnabledForProfile(cn, c.user)) {
    955                                         // no special handling necessary for this item
    956                                         c.markRestored();
    957                                     } else {
    958                                         if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
    959                                             // We allow auto install apps to have their intent
    960                                             // updated after an install.
    961                                             intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
    962                                             if (intent != null) {
    963                                                 c.restoreFlag = 0;
    964                                                 c.updater().put(
    965                                                         LauncherSettings.Favorites.INTENT,
    966                                                         intent.toUri(0)).commit();
    967                                                 cn = intent.getComponent();
    968                                             } else {
    969                                                 c.markDeleted("Unable to find a launch target");
    970                                                 continue;
    971                                             }
    972                                         } else {
    973                                             // The app is installed but the component is no
    974                                             // longer available.
    975                                             c.markDeleted("Invalid component removed: " + cn);
    976                                             continue;
    977                                         }
    978                                     }
    979                                 }
    980                                 // else if cn == null => can't infer much, leave it
    981                                 // else if !validPkg => could be restored icon or missing sd-card
    982 
    983                                 if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
    984                                     // Points to a valid app (superset of cn != null) but the apk
    985                                     // is not available.
    986 
    987                                     if (c.restoreFlag != 0) {
    988                                         // Package is not yet available but might be
    989                                         // installed later.
    990                                         FileLog.d(TAG, "package not yet restored: " + targetPkg);
    991 
    992                                         if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
    993                                             // Restore has started once.
    994                                         } else if (installingPkgs.containsKey(targetPkg)) {
    995                                             // App restore has started. Update the flag
    996                                             c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
    997                                             c.updater().commit();
    998                                         } else {
    999                                             c.markDeleted("Unrestored app removed: " + targetPkg);
   1000                                             continue;
   1001                                         }
   1002                                     } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
   1003                                         // Package is present but not available.
   1004                                         disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
   1005                                         // Add the icon on the workspace anyway.
   1006                                         allowMissingTarget = true;
   1007                                     } else if (!isSdCardReady) {
   1008                                         // SdCard is not ready yet. Package might get available,
   1009                                         // once it is ready.
   1010                                         Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
   1011                                         pendingPackages.addToList(c.user, targetPkg);
   1012                                         // Add the icon on the workspace anyway.
   1013                                         allowMissingTarget = true;
   1014                                     } else {
   1015                                         // Do not wait for external media load anymore.
   1016                                         c.markDeleted("Invalid package removed: " + targetPkg);
   1017                                         continue;
   1018                                     }
   1019                                 }
   1020 
   1021                                 if (validTarget) {
   1022                                     // The shortcut points to a valid target (either no target
   1023                                     // or something which is ready to be used)
   1024                                     c.markRestored();
   1025                                 }
   1026 
   1027                                 boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
   1028                                         c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
   1029 
   1030                                 if (c.restoreFlag != 0) {
   1031                                     // Already verified above that user is same as default user
   1032                                     info = c.getRestoredItemInfo(intent);
   1033                                 } else if (c.itemType ==
   1034                                         LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
   1035                                     info = c.getAppShortcutInfo(
   1036                                             intent, allowMissingTarget, useLowResIcon);
   1037                                 } else if (c.itemType ==
   1038                                         LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
   1039 
   1040                                     ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
   1041                                     if (unlockedUsers.get(c.serialNumber)) {
   1042                                         ShortcutInfoCompat pinnedShortcut =
   1043                                                 shortcutKeyToPinnedShortcuts.get(key);
   1044                                         if (pinnedShortcut == null) {
   1045                                             // The shortcut is no longer valid.
   1046                                             c.markDeleted("Pinned shortcut not found");
   1047                                             continue;
   1048                                         }
   1049                                         info = new ShortcutInfo(pinnedShortcut, context);
   1050                                         info.iconBitmap = LauncherIcons
   1051                                                 .createShortcutIcon(pinnedShortcut, context);
   1052                                         if (pmHelper.isAppSuspended(
   1053                                                 pinnedShortcut.getPackage(), info.user)) {
   1054                                             info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
   1055                                         }
   1056                                         intent = info.intent;
   1057                                     } else {
   1058                                         // Create a shortcut info in disabled mode for now.
   1059                                         info = c.loadSimpleShortcut();
   1060                                         info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
   1061                                     }
   1062                                 } else { // item type == ITEM_TYPE_SHORTCUT
   1063                                     info = c.loadSimpleShortcut();
   1064 
   1065                                     // Shortcuts are only available on the primary profile
   1066                                     if (!TextUtils.isEmpty(targetPkg)
   1067                                             && pmHelper.isAppSuspended(targetPkg, c.user)) {
   1068                                         disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
   1069                                     }
   1070 
   1071                                     // App shortcuts that used to be automatically added to Launcher
   1072                                     // didn't always have the correct intent flags set, so do that
   1073                                     // here
   1074                                     if (intent.getAction() != null &&
   1075                                         intent.getCategories() != null &&
   1076                                         intent.getAction().equals(Intent.ACTION_MAIN) &&
   1077                                         intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
   1078                                         intent.addFlags(
   1079                                             Intent.FLAG_ACTIVITY_NEW_TASK |
   1080                                             Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
   1081                                     }
   1082                                 }
   1083 
   1084                                 if (info != null) {
   1085                                     c.applyCommonProperties(info);
   1086 
   1087                                     info.intent = intent;
   1088                                     info.rank = c.getInt(rankIndex);
   1089                                     info.spanX = 1;
   1090                                     info.spanY = 1;
   1091                                     info.isDisabled |= disabledState;
   1092                                     if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
   1093                                         info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
   1094                                     }
   1095 
   1096                                     if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
   1097                                         Integer progress = installingPkgs.get(targetPkg);
   1098                                         if (progress != null) {
   1099                                             info.setInstallProgress(progress);
   1100                                         } else {
   1101                                             info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
   1102                                         }
   1103                                     }
   1104 
   1105                                     c.checkAndAddItem(info, sBgDataModel);
   1106                                 } else {
   1107                                     throw new RuntimeException("Unexpected null ShortcutInfo");
   1108                                 }
   1109                                 break;
   1110 
   1111                             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   1112                                 FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id);
   1113                                 c.applyCommonProperties(folderInfo);
   1114 
   1115                                 // Do not trim the folder label, as is was set by the user.
   1116                                 folderInfo.title = c.getString(c.titleIndex);
   1117                                 folderInfo.spanX = 1;
   1118                                 folderInfo.spanY = 1;
   1119                                 folderInfo.options = c.getInt(optionsIndex);
   1120 
   1121                                 // no special handling required for restored folders
   1122                                 c.markRestored();
   1123 
   1124                                 c.checkAndAddItem(folderInfo, sBgDataModel);
   1125                                 break;
   1126 
   1127                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
   1128                             case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
   1129                                 // Read all Launcher-specific widget details
   1130                                 boolean customWidget = c.itemType ==
   1131                                     LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
   1132 
   1133                                 int appWidgetId = c.getInt(appWidgetIdIndex);
   1134                                 String savedProvider = c.getString(appWidgetProviderIndex);
   1135 
   1136                                 final ComponentName component =
   1137                                         ComponentName.unflattenFromString(savedProvider);
   1138 
   1139                                 final boolean isIdValid = !c.hasRestoreFlag(
   1140                                         LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
   1141                                 final boolean wasProviderReady = !c.hasRestoreFlag(
   1142                                         LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
   1143 
   1144                                 if (widgetProvidersMap == null) {
   1145                                     widgetProvidersMap = AppWidgetManagerCompat
   1146                                             .getInstance(mContext).getAllProvidersMap();
   1147                                 }
   1148                                 final AppWidgetProviderInfo provider = widgetProvidersMap.get(
   1149                                         new ComponentKey(
   1150                                                 ComponentName.unflattenFromString(savedProvider),
   1151                                                 c.user));
   1152 
   1153                                 final boolean isProviderReady = isValidProvider(provider);
   1154                                 if (!isSafeMode && !customWidget &&
   1155                                         wasProviderReady && !isProviderReady) {
   1156                                     c.markDeleted(
   1157                                             "Deleting widget that isn't installed anymore: "
   1158                                             + provider);
   1159                                 } else {
   1160                                     if (isProviderReady) {
   1161                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
   1162                                                 provider.provider);
   1163 
   1164                                         // The provider is available. So the widget is either
   1165                                         // available or not available. We do not need to track
   1166                                         // any future restore updates.
   1167                                         int status = c.restoreFlag &
   1168                                                 ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
   1169                                         if (!wasProviderReady) {
   1170                                             // If provider was not previously ready, update the
   1171                                             // status and UI flag.
   1172 
   1173                                             // Id would be valid only if the widget restore broadcast was received.
   1174                                             if (isIdValid) {
   1175                                                 status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
   1176                                             } else {
   1177                                                 status &= ~LauncherAppWidgetInfo
   1178                                                         .FLAG_PROVIDER_NOT_READY;
   1179                                             }
   1180                                         }
   1181                                         appWidgetInfo.restoreStatus = status;
   1182                                     } else {
   1183                                         Log.v(TAG, "Widget restore pending id=" + c.id
   1184                                                 + " appWidgetId=" + appWidgetId
   1185                                                 + " status =" + c.restoreFlag);
   1186                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
   1187                                                 component);
   1188                                         appWidgetInfo.restoreStatus = c.restoreFlag;
   1189                                         Integer installProgress = installingPkgs.get(component.getPackageName());
   1190 
   1191                                         if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
   1192                                             // Restore has started once.
   1193                                         } else if (installProgress != null) {
   1194                                             // App restore has started. Update the flag
   1195                                             appWidgetInfo.restoreStatus |=
   1196                                                     LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
   1197                                         } else if (!isSafeMode) {
   1198                                             c.markDeleted("Unrestored widget removed: " + component);
   1199                                             continue;
   1200                                         }
   1201 
   1202                                         appWidgetInfo.installProgress =
   1203                                                 installProgress == null ? 0 : installProgress;
   1204                                     }
   1205                                     if (appWidgetInfo.hasRestoreFlag(
   1206                                             LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
   1207                                         appWidgetInfo.bindOptions = c.parseIntent();
   1208                                     }
   1209 
   1210                                     c.applyCommonProperties(appWidgetInfo);
   1211                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
   1212                                     appWidgetInfo.spanY = c.getInt(spanYIndex);
   1213                                     appWidgetInfo.user = c.user;
   1214 
   1215                                     if (!c.isOnWorkspaceOrHotseat()) {
   1216                                         c.markDeleted("Widget found where container != " +
   1217                                                 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
   1218                                         continue;
   1219                                     }
   1220 
   1221                                     if (!customWidget) {
   1222                                         String providerName =
   1223                                                 appWidgetInfo.providerName.flattenToString();
   1224                                         if (!providerName.equals(savedProvider) ||
   1225                                                 (appWidgetInfo.restoreStatus != c.restoreFlag)) {
   1226                                             c.updater()
   1227                                                     .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
   1228                                                             providerName)
   1229                                                     .put(LauncherSettings.Favorites.RESTORED,
   1230                                                             appWidgetInfo.restoreStatus)
   1231                                                     .commit();
   1232                                         }
   1233                                     }
   1234                                     c.checkAndAddItem(appWidgetInfo, sBgDataModel);
   1235                                 }
   1236                                 break;
   1237                             }
   1238                         } catch (Exception e) {
   1239                             Log.e(TAG, "Desktop items loading interrupted", e);
   1240                         }
   1241                     }
   1242                 } finally {
   1243                     Utilities.closeSilently(c);
   1244                 }
   1245 
   1246                 // Break early if we've stopped loading
   1247                 if (mStopped) {
   1248                     sBgDataModel.clear();
   1249                     return;
   1250                 }
   1251 
   1252                 // Remove dead items
   1253                 if (c.commitDeleted()) {
   1254                     // Remove any empty folder
   1255                     ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
   1256                             .call(contentResolver,
   1257                                     LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
   1258                             .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
   1259                     for (long folderId : deletedFolderIds) {
   1260                         sBgDataModel.workspaceItems.remove(sBgDataModel.folders.get(folderId));
   1261                         sBgDataModel.folders.remove(folderId);
   1262                         sBgDataModel.itemsIdMap.remove(folderId);
   1263                     }
   1264 
   1265                     // Remove any ghost widgets
   1266                     LauncherSettings.Settings.call(contentResolver,
   1267                             LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
   1268                 }
   1269 
   1270                 // Unpin shortcuts that don't exist on the workspace.
   1271                 HashSet<ShortcutKey> pendingShortcuts =
   1272                         InstallShortcutReceiver.getPendingShortcuts(context);
   1273                 for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
   1274                     MutableInt numTimesPinned = sBgDataModel.pinnedShortcutCounts.get(key);
   1275                     if ((numTimesPinned == null || numTimesPinned.value == 0)
   1276                             && !pendingShortcuts.contains(key)) {
   1277                         // Shortcut is pinned but doesn't exist on the workspace; unpin it.
   1278                         shortcutManager.unpinShortcut(key);
   1279                     }
   1280                 }
   1281 
   1282                 // Sort all the folder items and make sure the first 3 items are high resolution.
   1283                 for (FolderInfo folder : sBgDataModel.folders) {
   1284                     Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
   1285                     int pos = 0;
   1286                     for (ShortcutInfo info : folder.contents) {
   1287                         if (info.usingLowResIcon &&
   1288                                 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
   1289                             mIconCache.getTitleAndIcon(info, false);
   1290                         }
   1291                         pos ++;
   1292                         if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
   1293                             break;
   1294                         }
   1295                     }
   1296                 }
   1297 
   1298                 c.commitRestoredItems();
   1299                 if (!isSdCardReady && !pendingPackages.isEmpty()) {
   1300                     context.registerReceiver(
   1301                             new SdCardAvailableReceiver(
   1302                                     LauncherModel.this, mContext, pendingPackages),
   1303                             new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
   1304                             null,
   1305                             sWorker);
   1306                 }
   1307 
   1308                 // Remove any empty screens
   1309                 ArrayList<Long> unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
   1310                 for (ItemInfo item: sBgDataModel.itemsIdMap) {
   1311                     long screenId = item.screenId;
   1312                     if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
   1313                             unusedScreens.contains(screenId)) {
   1314                         unusedScreens.remove(screenId);
   1315                     }
   1316                 }
   1317 
   1318                 // If there are any empty screens remove them, and update.
   1319                 if (unusedScreens.size() != 0) {
   1320                     sBgDataModel.workspaceScreens.removeAll(unusedScreens);
   1321                     updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
   1322                 }
   1323             }
   1324             if (LauncherAppState.PROFILE_STARTUP) {
   1325                 Trace.endSection();
   1326             }
   1327         }
   1328 
   1329         /** Filters the set of items who are directly or indirectly (via another container) on the
   1330          * specified screen. */
   1331         private void filterCurrentWorkspaceItems(long currentScreenId,
   1332                 ArrayList<ItemInfo> allWorkspaceItems,
   1333                 ArrayList<ItemInfo> currentScreenItems,
   1334                 ArrayList<ItemInfo> otherScreenItems) {
   1335             // Purge any null ItemInfos
   1336             Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
   1337             while (iter.hasNext()) {
   1338                 ItemInfo i = iter.next();
   1339                 if (i == null) {
   1340                     iter.remove();
   1341                 }
   1342             }
   1343 
   1344             // Order the set of items by their containers first, this allows use to walk through the
   1345             // list sequentially, build up a list of containers that are in the specified screen,
   1346             // as well as all items in those containers.
   1347             Set<Long> itemsOnScreen = new HashSet<Long>();
   1348             Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
   1349                 @Override
   1350                 public int compare(ItemInfo lhs, ItemInfo rhs) {
   1351                     return Utilities.longCompare(lhs.container, rhs.container);
   1352                 }
   1353             });
   1354             for (ItemInfo info : allWorkspaceItems) {
   1355                 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
   1356                     if (info.screenId == currentScreenId) {
   1357                         currentScreenItems.add(info);
   1358                         itemsOnScreen.add(info.id);
   1359                     } else {
   1360                         otherScreenItems.add(info);
   1361                     }
   1362                 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   1363                     currentScreenItems.add(info);
   1364                     itemsOnScreen.add(info.id);
   1365                 } else {
   1366                     if (itemsOnScreen.contains(info.container)) {
   1367                         currentScreenItems.add(info);
   1368                         itemsOnScreen.add(info.id);
   1369                     } else {
   1370                         otherScreenItems.add(info);
   1371                     }
   1372                 }
   1373             }
   1374         }
   1375 
   1376         /** Filters the set of widgets which are on the specified screen. */
   1377         private void filterCurrentAppWidgets(long currentScreenId,
   1378                 ArrayList<LauncherAppWidgetInfo> appWidgets,
   1379                 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
   1380                 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
   1381 
   1382             for (LauncherAppWidgetInfo widget : appWidgets) {
   1383                 if (widget == null) continue;
   1384                 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
   1385                         widget.screenId == currentScreenId) {
   1386                     currentScreenWidgets.add(widget);
   1387                 } else {
   1388                     otherScreenWidgets.add(widget);
   1389                 }
   1390             }
   1391         }
   1392 
   1393         /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
   1394          * right) */
   1395         private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
   1396             final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
   1397             final int screenCols = profile.numColumns;
   1398             final int screenCellCount = profile.numColumns * profile.numRows;
   1399             Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
   1400                 @Override
   1401                 public int compare(ItemInfo lhs, ItemInfo rhs) {
   1402                     if (lhs.container == rhs.container) {
   1403                         // Within containers, order by their spatial position in that container
   1404                         switch ((int) lhs.container) {
   1405                             case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
   1406                                 long lr = (lhs.screenId * screenCellCount +
   1407                                         lhs.cellY * screenCols + lhs.cellX);
   1408                                 long rr = (rhs.screenId * screenCellCount +
   1409                                         rhs.cellY * screenCols + rhs.cellX);
   1410                                 return Utilities.longCompare(lr, rr);
   1411                             }
   1412                             case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
   1413                                 // We currently use the screen id as the rank
   1414                                 return Utilities.longCompare(lhs.screenId, rhs.screenId);
   1415                             }
   1416                             default:
   1417                                 if (ProviderConfig.IS_DOGFOOD_BUILD) {
   1418                                     throw new RuntimeException("Unexpected container type when " +
   1419                                             "sorting workspace items.");
   1420                                 }
   1421                                 return 0;
   1422                         }
   1423                     } else {
   1424                         // Between containers, order by hotseat, desktop
   1425                         return Utilities.longCompare(lhs.container, rhs.container);
   1426                     }
   1427                 }
   1428             });
   1429         }
   1430 
   1431         private void bindWorkspaceScreens(final Callbacks oldCallbacks,
   1432                 final ArrayList<Long> orderedScreens) {
   1433             final Runnable r = new Runnable() {
   1434                 @Override
   1435                 public void run() {
   1436                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1437                     if (callbacks != null) {
   1438                         callbacks.bindScreens(orderedScreens);
   1439                     }
   1440                 }
   1441             };
   1442             runOnMainThread(r);
   1443         }
   1444 
   1445         private void bindWorkspaceItems(final Callbacks oldCallbacks,
   1446                 final ArrayList<ItemInfo> workspaceItems,
   1447                 final ArrayList<LauncherAppWidgetInfo> appWidgets,
   1448                 final Executor executor) {
   1449 
   1450             // Bind the workspace items
   1451             int N = workspaceItems.size();
   1452             for (int i = 0; i < N; i += ITEMS_CHUNK) {
   1453                 final int start = i;
   1454                 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
   1455                 final Runnable r = new Runnable() {
   1456                     @Override
   1457                     public void run() {
   1458                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1459                         if (callbacks != null) {
   1460                             callbacks.bindItems(workspaceItems, start, start+chunkSize,
   1461                                     false);
   1462                         }
   1463                     }
   1464                 };
   1465                 executor.execute(r);
   1466             }
   1467 
   1468             // Bind the widgets, one at a time
   1469             N = appWidgets.size();
   1470             for (int i = 0; i < N; i++) {
   1471                 final LauncherAppWidgetInfo widget = appWidgets.get(i);
   1472                 final Runnable r = new Runnable() {
   1473                     public void run() {
   1474                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1475                         if (callbacks != null) {
   1476                             callbacks.bindAppWidget(widget);
   1477                         }
   1478                     }
   1479                 };
   1480                 executor.execute(r);
   1481             }
   1482         }
   1483 
   1484         /**
   1485          * Binds all loaded data to actual views on the main thread.
   1486          */
   1487         private void bindWorkspace(int synchronizeBindPage) {
   1488             final long t = SystemClock.uptimeMillis();
   1489             Runnable r;
   1490 
   1491             // Don't use these two variables in any of the callback runnables.
   1492             // Otherwise we hold a reference to them.
   1493             final Callbacks oldCallbacks = mCallbacks.get();
   1494             if (oldCallbacks == null) {
   1495                 // This launcher has exited and nobody bothered to tell us.  Just bail.
   1496                 Log.w(TAG, "LoaderTask running with no launcher");
   1497                 return;
   1498             }
   1499 
   1500             // Save a copy of all the bg-thread collections
   1501             ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
   1502             ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
   1503             ArrayList<Long> orderedScreenIds = new ArrayList<>();
   1504 
   1505             synchronized (sBgDataModel) {
   1506                 workspaceItems.addAll(sBgDataModel.workspaceItems);
   1507                 appWidgets.addAll(sBgDataModel.appWidgets);
   1508                 orderedScreenIds.addAll(sBgDataModel.workspaceScreens);
   1509             }
   1510 
   1511             final int currentScreen;
   1512             {
   1513                 int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE
   1514                         ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen();
   1515                 if (currScreen >= orderedScreenIds.size()) {
   1516                     // There may be no workspace screens (just hotseat items and an empty page).
   1517                     currScreen = PagedView.INVALID_RESTORE_PAGE;
   1518                 }
   1519                 currentScreen = currScreen;
   1520             }
   1521             final boolean validFirstPage = currentScreen >= 0;
   1522             final long currentScreenId =
   1523                     validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
   1524 
   1525             // Separate the items that are on the current screen, and all the other remaining items
   1526             ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
   1527             ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
   1528             ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
   1529             ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
   1530 
   1531             filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
   1532                     otherWorkspaceItems);
   1533             filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
   1534                     otherAppWidgets);
   1535             sortWorkspaceItemsSpatially(currentWorkspaceItems);
   1536             sortWorkspaceItemsSpatially(otherWorkspaceItems);
   1537 
   1538             // Tell the workspace that we're about to start binding items
   1539             r = new Runnable() {
   1540                 public void run() {
   1541                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1542                     if (callbacks != null) {
   1543                         callbacks.clearPendingBinds();
   1544                         callbacks.startBinding();
   1545                     }
   1546                 }
   1547             };
   1548             runOnMainThread(r);
   1549 
   1550             bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
   1551 
   1552             Executor mainExecutor = new DeferredMainThreadExecutor();
   1553             // Load items on the current page.
   1554             bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor);
   1555 
   1556             // In case of validFirstPage, only bind the first screen, and defer binding the
   1557             // remaining screens after first onDraw (and an optional the fade animation whichever
   1558             // happens later).
   1559             // This ensures that the first screen is immediately visible (eg. during rotation)
   1560             // In case of !validFirstPage, bind all pages one after other.
   1561             final Executor deferredExecutor =
   1562                     validFirstPage ? new ViewOnDrawExecutor(mHandler) : mainExecutor;
   1563 
   1564             mainExecutor.execute(new Runnable() {
   1565                 @Override
   1566                 public void run() {
   1567                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1568                     if (callbacks != null) {
   1569                         callbacks.finishFirstPageBind(
   1570                                 validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
   1571                     }
   1572                 }
   1573             });
   1574 
   1575             bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor);
   1576 
   1577             // Tell the workspace that we're done binding items
   1578             r = new Runnable() {
   1579                 public void run() {
   1580                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1581                     if (callbacks != null) {
   1582                         callbacks.finishBindingItems();
   1583                     }
   1584 
   1585                     mIsLoadingAndBindingWorkspace = false;
   1586 
   1587                     // Run all the bind complete runnables after workspace is bound.
   1588                     if (!mBindCompleteRunnables.isEmpty()) {
   1589                         synchronized (mBindCompleteRunnables) {
   1590                             for (final Runnable r : mBindCompleteRunnables) {
   1591                                 runOnWorkerThread(r);
   1592                             }
   1593                             mBindCompleteRunnables.clear();
   1594                         }
   1595                     }
   1596 
   1597                     // If we're profiling, ensure this is the last thing in the queue.
   1598                     if (DEBUG_LOADERS) {
   1599                         Log.d(TAG, "bound workspace in "
   1600                             + (SystemClock.uptimeMillis()-t) + "ms");
   1601                     }
   1602 
   1603                 }
   1604             };
   1605             deferredExecutor.execute(r);
   1606 
   1607             if (validFirstPage) {
   1608                 r = new Runnable() {
   1609                     public void run() {
   1610                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1611                         if (callbacks != null) {
   1612                             // We are loading synchronously, which means, some of the pages will be
   1613                             // bound after first draw. Inform the callbacks that page binding is
   1614                             // not complete, and schedule the remaining pages.
   1615                             if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
   1616                                 callbacks.onPageBoundSynchronously(currentScreen);
   1617                             }
   1618                             callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
   1619                         }
   1620                     }
   1621                 };
   1622                 runOnMainThread(r);
   1623             }
   1624         }
   1625 
   1626         private void updateIconCache() {
   1627             // Ignore packages which have a promise icon.
   1628             HashSet<String> packagesToIgnore = new HashSet<>();
   1629             synchronized (sBgDataModel) {
   1630                 for (ItemInfo info : sBgDataModel.itemsIdMap) {
   1631                     if (info instanceof ShortcutInfo) {
   1632                         ShortcutInfo si = (ShortcutInfo) info;
   1633                         if (si.isPromise() && si.getTargetComponent() != null) {
   1634                             packagesToIgnore.add(si.getTargetComponent().getPackageName());
   1635                         }
   1636                     } else if (info instanceof LauncherAppWidgetInfo) {
   1637                         LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
   1638                         if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
   1639                             packagesToIgnore.add(lawi.providerName.getPackageName());
   1640                         }
   1641                     }
   1642                 }
   1643             }
   1644             mIconCache.updateDbIcons(packagesToIgnore);
   1645         }
   1646 
   1647         private void onlyBindAllApps() {
   1648             final Callbacks oldCallbacks = mCallbacks.get();
   1649             if (oldCallbacks == null) {
   1650                 // This launcher has exited and nobody bothered to tell us.  Just bail.
   1651                 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
   1652                 return;
   1653             }
   1654 
   1655             // shallow copy
   1656             @SuppressWarnings("unchecked")
   1657             final ArrayList<AppInfo> list
   1658                     = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
   1659             Runnable r = new Runnable() {
   1660                 public void run() {
   1661                     final long t = SystemClock.uptimeMillis();
   1662                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1663                     if (callbacks != null) {
   1664                         callbacks.bindAllApplications(list);
   1665                     }
   1666                     if (DEBUG_LOADERS) {
   1667                         Log.d(TAG, "bound all " + list.size() + " apps from cache in "
   1668                                 + (SystemClock.uptimeMillis() - t) + "ms");
   1669                     }
   1670                 }
   1671             };
   1672             runOnMainThread(r);
   1673         }
   1674 
   1675         private void scheduleManagedHeuristicRunnable(final ManagedProfileHeuristic heuristic,
   1676                 final UserHandle user, final List<LauncherActivityInfo> apps) {
   1677             if (heuristic != null) {
   1678                 // Assume the app lists now is updated.
   1679                 mIsManagedHeuristicAppsUpdated = false;
   1680                 final Runnable managedHeuristicRunnable = new Runnable() {
   1681                     @Override
   1682                     public void run() {
   1683                         if (mIsManagedHeuristicAppsUpdated) {
   1684                             // If app list is updated, we need to reschedule it otherwise old app
   1685                             // list will override everything in processUserApps().
   1686                             sWorker.post(new Runnable() {
   1687                                 public void run() {
   1688                                     final List<LauncherActivityInfo> updatedApps =
   1689                                             mLauncherApps.getActivityList(null, user);
   1690                                     scheduleManagedHeuristicRunnable(heuristic, user,
   1691                                             updatedApps);
   1692                                 }
   1693                             });
   1694                         } else {
   1695                             heuristic.processUserApps(apps);
   1696                         }
   1697                     }
   1698                 };
   1699                 runOnMainThread(new Runnable() {
   1700                     @Override
   1701                     public void run() {
   1702                         // Check isLoadingWorkspace on the UI thread, as it is updated on the UI
   1703                         // thread.
   1704                         if (mIsLoadingAndBindingWorkspace) {
   1705                             synchronized (mBindCompleteRunnables) {
   1706                                 mBindCompleteRunnables.add(managedHeuristicRunnable);
   1707                             }
   1708                         } else {
   1709                             runOnWorkerThread(managedHeuristicRunnable);
   1710                         }
   1711                     }
   1712                 });
   1713             }
   1714         }
   1715 
   1716         private void loadAllApps() {
   1717             final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
   1718 
   1719             final Callbacks oldCallbacks = mCallbacks.get();
   1720             if (oldCallbacks == null) {
   1721                 // This launcher has exited and nobody bothered to tell us.  Just bail.
   1722                 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
   1723                 return;
   1724             }
   1725 
   1726             final List<UserHandle> profiles = mUserManager.getUserProfiles();
   1727 
   1728             // Clear the list of apps
   1729             mBgAllAppsList.clear();
   1730             for (UserHandle user : profiles) {
   1731                 // Query for the set of apps
   1732                 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
   1733                 final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
   1734                 if (DEBUG_LOADERS) {
   1735                     Log.d(TAG, "getActivityList took "
   1736                             + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
   1737                     Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
   1738                 }
   1739                 // Fail if we don't have any apps
   1740                 // TODO: Fix this. Only fail for the current user.
   1741                 if (apps == null || apps.isEmpty()) {
   1742                     return;
   1743                 }
   1744                 boolean quietMode = mUserManager.isQuietModeEnabled(user);
   1745                 // Create the ApplicationInfos
   1746                 for (int i = 0; i < apps.size(); i++) {
   1747                     LauncherActivityInfo app = apps.get(i);
   1748                     // This builds the icon bitmaps.
   1749                     mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
   1750                 }
   1751                 final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
   1752                 if (heuristic != null) {
   1753                     scheduleManagedHeuristicRunnable(heuristic, user, apps);
   1754                 }
   1755             }
   1756             // Huh? Shouldn't this be inside the Runnable below?
   1757             final ArrayList<AppInfo> added = mBgAllAppsList.added;
   1758             mBgAllAppsList.added = new ArrayList<AppInfo>();
   1759 
   1760             // Post callback on main thread
   1761             mHandler.post(new Runnable() {
   1762                 public void run() {
   1763 
   1764                     final long bindTime = SystemClock.uptimeMillis();
   1765                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   1766                     if (callbacks != null) {
   1767                         callbacks.bindAllApplications(added);
   1768                         if (DEBUG_LOADERS) {
   1769                             Log.d(TAG, "bound " + added.size() + " apps in "
   1770                                     + (SystemClock.uptimeMillis() - bindTime) + "ms");
   1771                         }
   1772                     } else {
   1773                         Log.i(TAG, "not binding apps: no Launcher activity");
   1774                     }
   1775                 }
   1776             });
   1777             // Cleanup any data stored for a deleted user.
   1778             ManagedProfileHeuristic.processAllUsers(profiles, mContext);
   1779             if (DEBUG_LOADERS) {
   1780                 Log.d(TAG, "Icons processed in "
   1781                         + (SystemClock.uptimeMillis() - loadTime) + "ms");
   1782             }
   1783         }
   1784 
   1785         private void loadDeepShortcuts() {
   1786             sBgDataModel.deepShortcutMap.clear();
   1787             DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
   1788             mHasShortcutHostPermission = shortcutManager.hasHostPermission();
   1789             if (mHasShortcutHostPermission) {
   1790                 for (UserHandle user : mUserManager.getUserProfiles()) {
   1791                     if (mUserManager.isUserUnlocked(user)) {
   1792                         List<ShortcutInfoCompat> shortcuts =
   1793                                 shortcutManager.queryForAllShortcuts(user);
   1794                         sBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
   1795                     }
   1796                 }
   1797             }
   1798         }
   1799     }
   1800 
   1801     public void bindDeepShortcuts() {
   1802         final MultiHashMap<ComponentKey, String> shortcutMapCopy =
   1803                 sBgDataModel.deepShortcutMap.clone();
   1804         Runnable r = new Runnable() {
   1805             @Override
   1806             public void run() {
   1807                 Callbacks callbacks = getCallback();
   1808                 if (callbacks != null) {
   1809                     callbacks.bindDeepShortcutMap(shortcutMapCopy);
   1810                 }
   1811             }
   1812         };
   1813         runOnMainThread(r);
   1814     }
   1815 
   1816     /**
   1817      * Refreshes the cached shortcuts if the shortcut permission has changed.
   1818      * Current implementation simply reloads the workspace, but it can be optimized to
   1819      * use partial updates similar to {@link UserManagerCompat}
   1820      */
   1821     public void refreshShortcutsIfRequired() {
   1822         if (Utilities.ATLEAST_NOUGAT_MR1) {
   1823             sWorker.removeCallbacks(mShortcutPermissionCheckRunnable);
   1824             sWorker.post(mShortcutPermissionCheckRunnable);
   1825         }
   1826     }
   1827 
   1828     /**
   1829      * Called when the icons for packages have been updated in the icon cache.
   1830      */
   1831     public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user) {
   1832         // If any package icon has changed (app was updated while launcher was dead),
   1833         // update the corresponding shortcuts.
   1834         enqueueModelUpdateTask(new CacheDataUpdatedTask(
   1835                 CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
   1836     }
   1837 
   1838     void enqueueModelUpdateTask(BaseModelUpdateTask task) {
   1839         if (!mModelLoaded && mLoaderTask == null) {
   1840             if (DEBUG_LOADERS) {
   1841                 Log.d(TAG, "enqueueModelUpdateTask Ignoring task since loader is pending=" + task);
   1842             }
   1843             return;
   1844         }
   1845         task.init(this);
   1846         runOnWorkerThread(task);
   1847     }
   1848 
   1849     /**
   1850      * A task to be executed on the current callbacks on the UI thread.
   1851      * If there is no current callbacks, the task is ignored.
   1852      */
   1853     public interface CallbackTask {
   1854 
   1855         void execute(Callbacks callbacks);
   1856     }
   1857 
   1858     /**
   1859      * A runnable which changes/updates the data model of the launcher based on certain events.
   1860      */
   1861     public static abstract class BaseModelUpdateTask implements Runnable {
   1862 
   1863         private LauncherModel mModel;
   1864         private DeferredHandler mUiHandler;
   1865 
   1866         /* package private */
   1867         void init(LauncherModel model) {
   1868             mModel = model;
   1869             mUiHandler = mModel.mHandler;
   1870         }
   1871 
   1872         @Override
   1873         public void run() {
   1874             if (!mModel.mHasLoaderCompletedOnce) {
   1875                 // Loader has not yet run.
   1876                 return;
   1877             }
   1878             execute(mModel.mApp, sBgDataModel, mModel.mBgAllAppsList);
   1879         }
   1880 
   1881         /**
   1882          * Execute the actual task. Called on the worker thread.
   1883          */
   1884         public abstract void execute(
   1885                 LauncherAppState app, BgDataModel dataModel, AllAppsList apps);
   1886 
   1887         /**
   1888          * Schedules a {@param task} to be executed on the current callbacks.
   1889          */
   1890         public final void scheduleCallbackTask(final CallbackTask task) {
   1891             final Callbacks callbacks = mModel.getCallback();
   1892             mUiHandler.post(new Runnable() {
   1893                 public void run() {
   1894                     Callbacks cb = mModel.getCallback();
   1895                     if (callbacks == cb && cb != null) {
   1896                         task.execute(callbacks);
   1897                     }
   1898                 }
   1899             });
   1900         }
   1901 
   1902         public ModelWriter getModelWriter() {
   1903             // Updates from model task, do not deal with icon position in hotseat.
   1904             return mModel.getWriter(false /* hasVerticalHotseat */);
   1905         }
   1906     }
   1907 
   1908     public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) {
   1909         updateAndBindShortcutInfo(new Provider<ShortcutInfo>() {
   1910             @Override
   1911             public ShortcutInfo get() {
   1912                 si.updateFromDeepShortcutInfo(info, mApp.getContext());
   1913                 si.iconBitmap = LauncherIcons.createShortcutIcon(info, mApp.getContext());
   1914                 return si;
   1915             }
   1916         });
   1917     }
   1918 
   1919     /**
   1920      * Utility method to update a shortcut on the background thread.
   1921      */
   1922     public void updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider) {
   1923         enqueueModelUpdateTask(new ExtendedModelTask() {
   1924             @Override
   1925             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
   1926                 ShortcutInfo info = shortcutProvider.get();
   1927                 ArrayList<ShortcutInfo> update = new ArrayList<>();
   1928                 update.add(info);
   1929                 bindUpdatedShortcuts(update, info.user);
   1930             }
   1931         });
   1932     }
   1933 
   1934     private void bindWidgetsModel(final Callbacks callbacks) {
   1935         final MultiHashMap<PackageItemInfo, WidgetItem> widgets
   1936                 = mBgWidgetsModel.getWidgetsMap().clone();
   1937         mHandler.post(new Runnable() {
   1938             @Override
   1939             public void run() {
   1940                 Callbacks cb = getCallback();
   1941                 if (callbacks == cb && cb != null) {
   1942                     callbacks.bindAllWidgets(widgets);
   1943                 }
   1944             }
   1945         });
   1946     }
   1947 
   1948     public void refreshAndBindWidgetsAndShortcuts(final Callbacks callbacks,
   1949             final boolean bindFirst, @Nullable final PackageUserKey packageUser) {
   1950         runOnWorkerThread(new Runnable() {
   1951             @Override
   1952             public void run() {
   1953                 if (bindFirst && !mBgWidgetsModel.isEmpty()) {
   1954                     bindWidgetsModel(callbacks);
   1955                 }
   1956                 ArrayList<WidgetItem> widgets = mBgWidgetsModel.update(
   1957                         mApp.getContext(), packageUser);
   1958                 bindWidgetsModel(callbacks);
   1959 
   1960                 // update the Widget entries inside DB on the worker thread.
   1961                 mApp.getWidgetCache().removeObsoletePreviews(widgets, packageUser);
   1962             }
   1963         });
   1964     }
   1965 
   1966     static boolean isValidProvider(AppWidgetProviderInfo provider) {
   1967         return (provider != null) && (provider.provider != null)
   1968                 && (provider.provider.getPackageName() != null);
   1969     }
   1970 
   1971     public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
   1972         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
   1973             writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
   1974             for (AppInfo info : mBgAllAppsList.data) {
   1975                 writer.println(prefix + "   title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
   1976                         + " componentName=" + info.componentName.getPackageName());
   1977             }
   1978         }
   1979         sBgDataModel.dump(prefix, fd, writer, args);
   1980     }
   1981 
   1982     public Callbacks getCallback() {
   1983         return mCallbacks != null ? mCallbacks.get() : null;
   1984     }
   1985 
   1986     /**
   1987      * @return {@link FolderInfo} if its already loaded.
   1988      */
   1989     public FolderInfo findFolderById(Long folderId) {
   1990         synchronized (sBgDataModel) {
   1991             return sBgDataModel.folders.get(folderId);
   1992         }
   1993     }
   1994 
   1995     @Thunk class DeferredMainThreadExecutor implements Executor {
   1996 
   1997         @Override
   1998         public void execute(Runnable command) {
   1999             runOnMainThread(command);
   2000         }
   2001     }
   2002 
   2003     /**
   2004      * @return the looper for the worker thread which can be used to start background tasks.
   2005      */
   2006     public static Looper getWorkerLooper() {
   2007         return sWorkerThread.getLooper();
   2008     }
   2009 }
   2010