diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2017-06-06 10:46:59 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2017-06-06 10:47:23 -0700 |
commit | b434fde42702000cfb8eacfdfc85c2f759be0a75 (patch) | |
tree | b26ff100c1ba0424a3cd35f849cc5672510b8d9a /src/com/android/launcher3 | |
parent | 0540c0606b17b3aeca5c0f4c16b3daf5aac0c421 (diff) | |
download | android_packages_apps_Trebuchet-b434fde42702000cfb8eacfdfc85c2f759be0a75.tar.gz android_packages_apps_Trebuchet-b434fde42702000cfb8eacfdfc85c2f759be0a75.tar.bz2 android_packages_apps_Trebuchet-b434fde42702000cfb8eacfdfc85c2f759be0a75.zip |
Moving LoaderTask to a separate class file
Bug: 34112546
Change-Id: I86d93cfb5d3903bfd3878564cbe8b67b64fd8423
Diffstat (limited to 'src/com/android/launcher3')
-rw-r--r-- | src/com/android/launcher3/AppWidgetsRestoredReceiver.java | 3 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 794 | ||||
-rw-r--r-- | src/com/android/launcher3/model/LoaderTask.java | 839 |
3 files changed, 846 insertions, 790 deletions
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index 84a8bce6e..70be7dae4 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -12,6 +12,7 @@ import android.os.Handler; import android.util.Log; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.model.LoaderTask; import com.android.launcher3.util.ContentWriter; public class AppWidgetsRestoredReceiver extends BroadcastReceiver { @@ -52,7 +53,7 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]); final int state; - if (LauncherModel.isValidProvider(provider)) { + if (LoaderTask.isValidProvider(provider)) { // This will ensure that we show 'Click to setup' UI if required. state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY; } else { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 265c7e33a..48c5c562c 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProviderOperation; @@ -24,61 +23,40 @@ import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.LauncherActivityInfo; -import android.content.pm.PackageInstaller; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Process; -import android.os.SystemClock; -import android.os.Trace; import android.os.UserHandle; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; -import android.util.LongSparseArray; -import android.util.MutableInt; import android.util.Pair; -import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherAppsCompat; -import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserManagerCompat; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dynamicui.ExtractionUtils; -import com.android.launcher3.folder.Folder; -import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.folder.FolderIconPreviewVerifier; import com.android.launcher3.graphics.LauncherIcons; -import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.AddWorkspaceItemsTask; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.CacheDataUpdatedTask; import com.android.launcher3.model.ExtendedModelTask; -import com.android.launcher3.model.GridSizeMigrationTask; -import com.android.launcher3.model.LoaderCursor; import com.android.launcher3.model.LoaderResults; +import com.android.launcher3.model.LoaderTask; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.PackageInstallStateChangedTask; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.model.PackageUpdatedTask; -import com.android.launcher3.model.SdCardAvailableReceiver; import com.android.launcher3.model.ShortcutsChangedTask; import com.android.launcher3.model.UserLockStateChangedTask; import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.provider.ImportDataTask; import com.android.launcher3.provider.LauncherDbUtils; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutInfoCompat; -import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.LooperIdleLock; -import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.MultiHashMap; -import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Provider; @@ -89,12 +67,9 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.concurrent.CancellationException; import java.util.concurrent.Executor; @@ -105,7 +80,7 @@ import java.util.concurrent.Executor; */ public class LauncherModel extends BroadcastReceiver implements LauncherAppsCompat.OnAppsChangedCallbackCompat { - static final boolean DEBUG_LOADERS = false; + static final boolean DEBUG_TASKS = false; private static final boolean DEBUG_RECEIVER = false; static final String TAG = "Launcher.Model"; @@ -113,7 +88,8 @@ public class LauncherModel extends BroadcastReceiver private final MainThreadExecutor mUiExecutor = new MainThreadExecutor(); @Thunk final LauncherAppState mApp; @Thunk final Object mLock = new Object(); - @Thunk LoaderTask mLoaderTask; + @Thunk + LoaderTask mLoaderTask; @Thunk boolean mIsLoaderTaskRunning; @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); @@ -610,761 +586,6 @@ public class LauncherModel extends BroadcastReceiver } /** - * Runnable for the thread that loads the contents of the launcher: - * - workspace icons - * - widgets - * - all apps icons - * - deep shortcuts within apps - */ - private static class LoaderTask implements Runnable { - private final LauncherAppState mApp; - private final AllAppsList mBgAllAppsList; - private final BgDataModel mBgDataModel; - - private final LoaderResults mResults; - - private final LauncherAppsCompat mLauncherApps; - private final UserManagerCompat mUserManager; - private final DeepShortcutManager mShortcutManager; - private final PackageInstallerCompat mPackageInstaller; - private final AppWidgetManagerCompat mAppWidgetManager; - private final IconCache mIconCache; - - private boolean mStopped; - - LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, - LoaderResults results) { - mApp = app; - mBgAllAppsList = bgAllAppsList; - mBgDataModel = dataModel; - mResults = results; - - mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext()); - mUserManager = UserManagerCompat.getInstance(mApp.getContext()); - mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext()); - mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext()); - mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext()); - mIconCache = mApp.getIconCache(); - } - - private synchronized void waitForIdle() { - // Wait until the either we're stopped or the other threads are done. - // This way we don't start loading all apps until the workspace has settled - // down. - LooperIdleLock idleLock = new LooperIdleLock(this, Looper.getMainLooper()); - // Just in case mFlushingWorkerThread changes but we aren't woken up, - // wait no longer than 1sec at a time - while (!mStopped && idleLock.awaitLocked(1000)); - } - - private synchronized void verifyNotStopped() throws CancellationException { - if (mStopped) { - throw new CancellationException("Loader stopped"); - } - } - - public void run() { - synchronized (this) { - // Skip fast if we are already stopped. - if (mStopped) { - return; - } - } - - try (LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { - long now = 0; - if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace"); - loadWorkspace(); - - verifyNotStopped(); - if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace"); - mResults.bindWorkspace(); - - // Take a break - if (DEBUG_LOADERS) { - Log.d(TAG, "step 1 completed, wait for idle"); - now = SystemClock.uptimeMillis(); - } - waitForIdle(); - if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms"); - verifyNotStopped(); - - // second step - if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps"); - loadAllApps(); - - if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps"); - verifyNotStopped(); - mResults.bindAllApps(); - - verifyNotStopped(); - if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache"); - updateIconCache(); - - // Take a break - if (DEBUG_LOADERS) { - Log.d(TAG, "step 2 completed, wait for idle"); - now = SystemClock.uptimeMillis(); - } - waitForIdle(); - if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms"); - verifyNotStopped(); - - // third step - if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts"); - loadDeepShortcuts(); - - verifyNotStopped(); - if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts"); - mResults.bindDeepShortcuts(); - - // Take a break - if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle"); - waitForIdle(); - verifyNotStopped(); - - // fourth step - if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets"); - mBgDataModel.widgetsModel.update(mApp, null); - - verifyNotStopped(); - if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets"); - mResults.bindWidgets(); - - transaction.commit(); - } catch (CancellationException e) { - // Loader stopped, ignore - } - } - - public synchronized void stopLocked() { - mStopped = true; - this.notify(); - } - - private void loadWorkspace() { - if (LauncherAppState.PROFILE_STARTUP) { - Trace.beginSection("Loading Workspace"); - } - - final Context context = mApp.getContext(); - final ContentResolver contentResolver = context.getContentResolver(); - final PackageManagerHelper pmHelper = new PackageManagerHelper(context); - final boolean isSafeMode = pmHelper.isSafeMode(); - final boolean isSdCardReady = Utilities.isBootCompleted(); - final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>(); - - boolean clearDb = false; - try { - ImportDataTask.performImportIfPossible(context); - } catch (Exception e) { - // Migration failed. Clear workspace. - clearDb = true; - } - - if (!clearDb && GridSizeMigrationTask.ENABLED && - !GridSizeMigrationTask.migrateGridIfNeeded(context)) { - // Migration failed. Clear workspace. - clearDb = true; - } - - if (clearDb) { - Log.d(TAG, "loadWorkspace: resetting launcher database"); - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - } - - Log.d(TAG, "loadWorkspace: loading default favorites"); - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES); - - synchronized (mBgDataModel) { - mBgDataModel.clear(); - - final HashMap<String, Integer> installingPkgs = - mPackageInstaller.updateAndGetActiveSessionCache(); - mBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(context)); - - Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>(); - final LoaderCursor c = new LoaderCursor(contentResolver.query( - LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp); - - HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null; - - try { - final int appWidgetIdIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_ID); - final int appWidgetProviderIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_PROVIDER); - final int spanXIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.SPANX); - final int spanYIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.SPANY); - final int rankIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.RANK); - final int optionsIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.OPTIONS); - - final LongSparseArray<UserHandle> allUsers = c.allUsers; - final LongSparseArray<Boolean> quietMode = new LongSparseArray<>(); - final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>(); - for (UserHandle user : mUserManager.getUserProfiles()) { - long serialNo = mUserManager.getSerialNumberForUser(user); - allUsers.put(serialNo, user); - quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user)); - - boolean userUnlocked = mUserManager.isUserUnlocked(user); - - // We can only query for shortcuts when the user is unlocked. - if (userUnlocked) { - List<ShortcutInfoCompat> pinnedShortcuts = - mShortcutManager.queryForPinnedShortcuts(null, user); - if (mShortcutManager.wasLastCallSuccess()) { - for (ShortcutInfoCompat shortcut : pinnedShortcuts) { - shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), - shortcut); - } - } else { - // Shortcut manager can fail due to some race condition when the - // lock state changes too frequently. For the purpose of the loading - // shortcuts, consider the user is still locked. - userUnlocked = false; - } - } - unlockedUsers.put(serialNo, userUnlocked); - } - - ShortcutInfo info; - LauncherAppWidgetInfo appWidgetInfo; - Intent intent; - String targetPkg; - - FolderIconPreviewVerifier verifier = - new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile()); - while (!mStopped && c.moveToNext()) { - try { - if (c.user == null) { - // User has been deleted, remove the item. - c.markDeleted("User has been deleted"); - continue; - } - - boolean allowMissingTarget = false; - switch (c.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: - intent = c.parseIntent(); - if (intent == null) { - c.markDeleted("Invalid or null intent"); - continue; - } - - int disabledState = quietMode.get(c.serialNumber) ? - ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0; - ComponentName cn = intent.getComponent(); - targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); - - if (!Process.myUserHandle().equals(c.user)) { - if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { - c.markDeleted("Legacy shortcuts are only allowed for default user"); - continue; - } else if (c.restoreFlag != 0) { - // Don't restore items for other profiles. - c.markDeleted("Restore from managed profile not supported"); - continue; - } - } - if (TextUtils.isEmpty(targetPkg) && - c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { - c.markDeleted("Only legacy shortcuts can have null package"); - continue; - } - - // If there is no target package, its an implicit intent - // (legacy shortcut) which is always valid - boolean validTarget = TextUtils.isEmpty(targetPkg) || - mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user); - - if (cn != null && validTarget) { - // If the apk is present and the shortcut points to a specific - // component. - - // If the component is already present - if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) { - // no special handling necessary for this item - c.markRestored(); - } else { - if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) { - // We allow auto install apps to have their intent - // updated after an install. - intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); - if (intent != null) { - c.restoreFlag = 0; - c.updater().put( - LauncherSettings.Favorites.INTENT, - intent.toUri(0)).commit(); - cn = intent.getComponent(); - } else { - c.markDeleted("Unable to find a launch target"); - continue; - } - } else { - // The app is installed but the component is no - // longer available. - c.markDeleted("Invalid component removed: " + cn); - continue; - } - } - } - // else if cn == null => can't infer much, leave it - // else if !validPkg => could be restored icon or missing sd-card - - if (!TextUtils.isEmpty(targetPkg) && !validTarget) { - // Points to a valid app (superset of cn != null) but the apk - // is not available. - - if (c.restoreFlag != 0) { - // Package is not yet available but might be - // installed later. - FileLog.d(TAG, "package not yet restored: " + targetPkg); - - if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) { - // Restore has started once. - } else if (installingPkgs.containsKey(targetPkg)) { - // App restore has started. Update the flag - c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED; - c.updater().commit(); - } else { - c.markDeleted("Unrestored app removed: " + targetPkg); - continue; - } - } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { - // Package is present but not available. - disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE; - // Add the icon on the workspace anyway. - allowMissingTarget = true; - } else if (!isSdCardReady) { - // SdCard is not ready yet. Package might get available, - // once it is ready. - Log.d(TAG, "Missing pkg, will check later: " + targetPkg); - pendingPackages.addToList(c.user, targetPkg); - // Add the icon on the workspace anyway. - allowMissingTarget = true; - } else { - // Do not wait for external media load anymore. - c.markDeleted("Invalid package removed: " + targetPkg); - continue; - } - } - - if (validTarget) { - // The shortcut points to a valid target (either no target - // or something which is ready to be used) - c.markRestored(); - } - - boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() && - !verifier.isItemInPreview(c.getInt(rankIndex)); - - if (c.restoreFlag != 0) { - // Already verified above that user is same as default user - info = c.getRestoredItemInfo(intent); - } else if (c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = c.getAppShortcutInfo( - intent, allowMissingTarget, useLowResIcon); - } else if (c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - - ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); - if (unlockedUsers.get(c.serialNumber)) { - ShortcutInfoCompat pinnedShortcut = - shortcutKeyToPinnedShortcuts.get(key); - if (pinnedShortcut == null) { - // The shortcut is no longer valid. - c.markDeleted("Pinned shortcut not found"); - continue; - } - info = new ShortcutInfo(pinnedShortcut, context); - info.iconBitmap = LauncherIcons - .createShortcutIcon(pinnedShortcut, context); - if (pmHelper.isAppSuspended( - pinnedShortcut.getPackage(), info.user)) { - info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; - } - intent = info.intent; - } else { - // Create a shortcut info in disabled mode for now. - info = c.loadSimpleShortcut(); - info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER; - } - } else { // item type == ITEM_TYPE_SHORTCUT - info = c.loadSimpleShortcut(); - - // Shortcuts are only available on the primary profile - if (!TextUtils.isEmpty(targetPkg) - && pmHelper.isAppSuspended(targetPkg, c.user)) { - disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; - } - - // App shortcuts that used to be automatically added to Launcher - // didn't always have the correct intent flags set, so do that - // here - if (intent.getAction() != null && - intent.getCategories() != null && - intent.getAction().equals(Intent.ACTION_MAIN) && - intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { - intent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - } - } - - if (info != null) { - c.applyCommonProperties(info); - - info.intent = intent; - info.rank = c.getInt(rankIndex); - info.spanX = 1; - info.spanY = 1; - info.isDisabled |= disabledState; - if (isSafeMode && !Utilities.isSystemApp(context, intent)) { - info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE; - } - - if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { - Integer progress = installingPkgs.get(targetPkg); - if (progress != null) { - info.setInstallProgress(progress); - } else { - info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; - } - } - - c.checkAndAddItem(info, mBgDataModel); - } else { - throw new RuntimeException("Unexpected null ShortcutInfo"); - } - break; - - case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); - c.applyCommonProperties(folderInfo); - - // Do not trim the folder label, as is was set by the user. - folderInfo.title = c.getString(c.titleIndex); - folderInfo.spanX = 1; - folderInfo.spanY = 1; - folderInfo.options = c.getInt(optionsIndex); - - // no special handling required for restored folders - c.markRestored(); - - c.checkAndAddItem(folderInfo, mBgDataModel); - break; - - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: - // Read all Launcher-specific widget details - boolean customWidget = c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; - - int appWidgetId = c.getInt(appWidgetIdIndex); - String savedProvider = c.getString(appWidgetProviderIndex); - - final ComponentName component = - ComponentName.unflattenFromString(savedProvider); - - final boolean isIdValid = !c.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); - final boolean wasProviderReady = !c.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); - - if (widgetProvidersMap == null) { - widgetProvidersMap = mAppWidgetManager.getAllProvidersMap(); - } - final AppWidgetProviderInfo provider = widgetProvidersMap.get( - new ComponentKey( - ComponentName.unflattenFromString(savedProvider), - c.user)); - - final boolean isProviderReady = isValidProvider(provider); - if (!isSafeMode && !customWidget && - wasProviderReady && !isProviderReady) { - c.markDeleted( - "Deleting widget that isn't installed anymore: " - + provider); - } else { - if (isProviderReady) { - appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, - provider.provider); - - // The provider is available. So the widget is either - // available or not available. We do not need to track - // any future restore updates. - int status = c.restoreFlag & - ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; - if (!wasProviderReady) { - // If provider was not previously ready, update the - // status and UI flag. - - // Id would be valid only if the widget restore broadcast was received. - if (isIdValid) { - status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; - } else { - status &= ~LauncherAppWidgetInfo - .FLAG_PROVIDER_NOT_READY; - } - } - appWidgetInfo.restoreStatus = status; - } else { - Log.v(TAG, "Widget restore pending id=" + c.id - + " appWidgetId=" + appWidgetId - + " status =" + c.restoreFlag); - appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, - component); - appWidgetInfo.restoreStatus = c.restoreFlag; - Integer installProgress = installingPkgs.get(component.getPackageName()); - - if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { - // Restore has started once. - } else if (installProgress != null) { - // App restore has started. Update the flag - appWidgetInfo.restoreStatus |= - LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; - } else if (!isSafeMode) { - c.markDeleted("Unrestored widget removed: " + component); - continue; - } - - appWidgetInfo.installProgress = - installProgress == null ? 0 : installProgress; - } - if (appWidgetInfo.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { - appWidgetInfo.bindOptions = c.parseIntent(); - } - - c.applyCommonProperties(appWidgetInfo); - appWidgetInfo.spanX = c.getInt(spanXIndex); - appWidgetInfo.spanY = c.getInt(spanYIndex); - appWidgetInfo.user = c.user; - - if (!c.isOnWorkspaceOrHotseat()) { - c.markDeleted("Widget found where container != " + - "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); - continue; - } - - if (!customWidget) { - String providerName = - appWidgetInfo.providerName.flattenToString(); - if (!providerName.equals(savedProvider) || - (appWidgetInfo.restoreStatus != c.restoreFlag)) { - c.updater() - .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, - providerName) - .put(LauncherSettings.Favorites.RESTORED, - appWidgetInfo.restoreStatus) - .commit(); - } - } - - if (appWidgetInfo.restoreStatus != - LauncherAppWidgetInfo.RESTORE_COMPLETED) { - String pkg = appWidgetInfo.providerName.getPackageName(); - appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg); - appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user; - mIconCache.getTitleAndIconForApp( - appWidgetInfo.pendingItemInfo, false); - } - - c.checkAndAddItem(appWidgetInfo, mBgDataModel); - } - break; - } - } catch (Exception e) { - Log.e(TAG, "Desktop items loading interrupted", e); - } - } - } finally { - Utilities.closeSilently(c); - } - - // Break early if we've stopped loading - if (mStopped) { - mBgDataModel.clear(); - return; - } - - // Remove dead items - if (c.commitDeleted()) { - // Remove any empty folder - ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings - .call(contentResolver, - LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS) - .getSerializable(LauncherSettings.Settings.EXTRA_VALUE); - for (long folderId : deletedFolderIds) { - mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); - mBgDataModel.folders.remove(folderId); - mBgDataModel.itemsIdMap.remove(folderId); - } - - // Remove any ghost widgets - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS); - } - - // Unpin shortcuts that don't exist on the workspace. - HashSet<ShortcutKey> pendingShortcuts = - InstallShortcutReceiver.getPendingShortcuts(context); - for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) { - MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key); - if ((numTimesPinned == null || numTimesPinned.value == 0) - && !pendingShortcuts.contains(key)) { - // Shortcut is pinned but doesn't exist on the workspace; unpin it. - mShortcutManager.unpinShortcut(key); - } - } - - FolderIconPreviewVerifier verifier = - new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile()); - // Sort the folder items and make sure all items in the preview are high resolution. - for (FolderInfo folder : mBgDataModel.folders) { - Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); - verifier.setFolderInfo(folder); - - int numItemsInPreview = 0; - for (ShortcutInfo info : folder.contents) { - if (info.usingLowResIcon - && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION - && verifier.isItemInPreview(info.rank)) { - mIconCache.getTitleAndIcon(info, false); - numItemsInPreview++; - } - - if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) { - break; - } - } - } - - c.commitRestoredItems(); - if (!isSdCardReady && !pendingPackages.isEmpty()) { - context.registerReceiver( - new SdCardAvailableReceiver(mApp, pendingPackages), - new IntentFilter(Intent.ACTION_BOOT_COMPLETED), - null, - sWorker); - } - - // Remove any empty screens - ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens); - for (ItemInfo item: mBgDataModel.itemsIdMap) { - long screenId = item.screenId; - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - unusedScreens.contains(screenId)) { - unusedScreens.remove(screenId); - } - } - - // If there are any empty screens remove them, and update. - if (unusedScreens.size() != 0) { - mBgDataModel.workspaceScreens.removeAll(unusedScreens); - updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens); - } - } - if (LauncherAppState.PROFILE_STARTUP) { - Trace.endSection(); - } - } - - private void updateIconCache() { - // Ignore packages which have a promise icon. - HashSet<String> packagesToIgnore = new HashSet<>(); - synchronized (mBgDataModel) { - for (ItemInfo info : mBgDataModel.itemsIdMap) { - if (info instanceof ShortcutInfo) { - ShortcutInfo si = (ShortcutInfo) info; - if (si.isPromise() && si.getTargetComponent() != null) { - packagesToIgnore.add(si.getTargetComponent().getPackageName()); - } - } else if (info instanceof LauncherAppWidgetInfo) { - LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info; - if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { - packagesToIgnore.add(lawi.providerName.getPackageName()); - } - } - } - } - mIconCache.updateDbIcons(packagesToIgnore); - } - - private void loadAllApps() { - final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - - final List<UserHandle> profiles = mUserManager.getUserProfiles(); - - // Clear the list of apps - mBgAllAppsList.clear(); - for (UserHandle user : profiles) { - // Query for the set of apps - final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user); - if (DEBUG_LOADERS) { - Log.d(TAG, "getActivityList took " - + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user); - Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user); - } - // Fail if we don't have any apps - // TODO: Fix this. Only fail for the current user. - if (apps == null || apps.isEmpty()) { - return; - } - boolean quietMode = mUserManager.isQuietModeEnabled(user); - // Create the ApplicationInfos - for (int i = 0; i < apps.size(); i++) { - LauncherActivityInfo app = apps.get(i); - // This builds the icon bitmaps. - mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); - } - - ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user); - } - - if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { - // get all active sessions and add them to the all apps list - for (PackageInstaller.SessionInfo info : - mPackageInstaller.getAllVerifiedSessions()) { - mBgAllAppsList.addPromiseApp(mApp.getContext(), - PackageInstallInfo.fromInstallingState(info)); - } - } - - mBgAllAppsList.added = new ArrayList<>(); - if (DEBUG_LOADERS) { - Log.d(TAG, "All apps loaded in in " - + (SystemClock.uptimeMillis() - loadTime) + "ms"); - } - } - - private void loadDeepShortcuts() { - mBgDataModel.deepShortcutMap.clear(); - mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission(); - if (mBgDataModel.hasShortcutHostPermission) { - for (UserHandle user : mUserManager.getUserProfiles()) { - if (mUserManager.isUserUnlocked(user)) { - List<ShortcutInfoCompat> shortcuts = - mShortcutManager.queryForAllShortcuts(user); - mBgDataModel.updateDeepShortcutMap(null, user, shortcuts); - } - } - } - } - } - - /** * Refreshes the cached shortcuts if the shortcut permission has changed. * Current implementation simply reloads the workspace, but it can be optimized to * use partial updates similar to {@link UserManagerCompat} @@ -1417,7 +638,7 @@ public class LauncherModel extends BroadcastReceiver @Override public final void run() { if (!mModel.mModelLoaded) { - if (DEBUG_LOADERS) { + if (DEBUG_TASKS) { Log.d(TAG, "Ignoring model task since loader is pending=" + this); } // Loader has not yet run. @@ -1489,11 +710,6 @@ public class LauncherModel extends BroadcastReceiver }); } - static boolean isValidProvider(AppWidgetProviderInfo provider) { - return (provider != null) && (provider.provider != null) - && (provider.provider.getPackageName() != null); - } - public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { if (args.length > 0 && TextUtils.equals(args[0], "--all")) { writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size()); diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java new file mode 100644 index 000000000..bcf516eed --- /dev/null +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.model; + +import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.PackageInstaller; +import android.os.Handler; +import android.os.Looper; +import android.os.Process; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import android.util.LongSparseArray; +import android.util.MutableInt; + +import com.android.launcher3.AllAppsList; +import com.android.launcher3.AppInfo; +import com.android.launcher3.FolderInfo; +import com.android.launcher3.IconCache; +import com.android.launcher3.InstallShortcutReceiver; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetInfo; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.PackageInstallerCompat; +import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.folder.FolderIconPreviewVerifier; +import com.android.launcher3.graphics.LauncherIcons; +import com.android.launcher3.logging.FileLog; +import com.android.launcher3.provider.ImportDataTask; +import com.android.launcher3.shortcuts.DeepShortcutManager; +import com.android.launcher3.shortcuts.ShortcutInfoCompat; +import com.android.launcher3.shortcuts.ShortcutKey; +import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.LooperIdleLock; +import com.android.launcher3.util.ManagedProfileHeuristic; +import com.android.launcher3.util.MultiHashMap; +import com.android.launcher3.util.PackageManagerHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CancellationException; + +/** + * Runnable for the thread that loads the contents of the launcher: + * - workspace icons + * - widgets + * - all apps icons + * - deep shortcuts within apps + */ +public class LoaderTask implements Runnable { + private static final boolean DEBUG_LOADERS = false; + private static final String TAG = "LoaderTask"; + + private final LauncherAppState mApp; + private final AllAppsList mBgAllAppsList; + private final BgDataModel mBgDataModel; + + private final LoaderResults mResults; + + private final LauncherAppsCompat mLauncherApps; + private final UserManagerCompat mUserManager; + private final DeepShortcutManager mShortcutManager; + private final PackageInstallerCompat mPackageInstaller; + private final AppWidgetManagerCompat mAppWidgetManager; + private final IconCache mIconCache; + + private boolean mStopped; + + public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, + LoaderResults results) { + mApp = app; + mBgAllAppsList = bgAllAppsList; + mBgDataModel = dataModel; + mResults = results; + + mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext()); + mUserManager = UserManagerCompat.getInstance(mApp.getContext()); + mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext()); + mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext()); + mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext()); + mIconCache = mApp.getIconCache(); + } + + private synchronized void waitForIdle() { + // Wait until the either we're stopped or the other threads are done. + // This way we don't start loading all apps until the workspace has settled + // down. + LooperIdleLock idleLock = new LooperIdleLock(this, Looper.getMainLooper()); + // Just in case mFlushingWorkerThread changes but we aren't woken up, + // wait no longer than 1sec at a time + while (!mStopped && idleLock.awaitLocked(1000)); + } + + private synchronized void verifyNotStopped() throws CancellationException { + if (mStopped) { + throw new CancellationException("Loader stopped"); + } + } + + public void run() { + synchronized (this) { + // Skip fast if we are already stopped. + if (mStopped) { + return; + } + } + + try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { + long now = 0; + if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace"); + loadWorkspace(); + + verifyNotStopped(); + if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace"); + mResults.bindWorkspace(); + + // Take a break + if (DEBUG_LOADERS) { + Log.d(TAG, "step 1 completed, wait for idle"); + now = SystemClock.uptimeMillis(); + } + waitForIdle(); + if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms"); + verifyNotStopped(); + + // second step + if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps"); + loadAllApps(); + + if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps"); + verifyNotStopped(); + mResults.bindAllApps(); + + verifyNotStopped(); + if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache"); + updateIconCache(); + + // Take a break + if (DEBUG_LOADERS) { + Log.d(TAG, "step 2 completed, wait for idle"); + now = SystemClock.uptimeMillis(); + } + waitForIdle(); + if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms"); + verifyNotStopped(); + + // third step + if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts"); + loadDeepShortcuts(); + + verifyNotStopped(); + if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts"); + mResults.bindDeepShortcuts(); + + // Take a break + if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle"); + waitForIdle(); + verifyNotStopped(); + + // fourth step + if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets"); + mBgDataModel.widgetsModel.update(mApp, null); + + verifyNotStopped(); + if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets"); + mResults.bindWidgets(); + + transaction.commit(); + } catch (CancellationException e) { + // Loader stopped, ignore + } + } + + public synchronized void stopLocked() { + mStopped = true; + this.notify(); + } + + private void loadWorkspace() { + if (LauncherAppState.PROFILE_STARTUP) { + Trace.beginSection("Loading Workspace"); + } + + final Context context = mApp.getContext(); + final ContentResolver contentResolver = context.getContentResolver(); + final PackageManagerHelper pmHelper = new PackageManagerHelper(context); + final boolean isSafeMode = pmHelper.isSafeMode(); + final boolean isSdCardReady = Utilities.isBootCompleted(); + final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>(); + + boolean clearDb = false; + try { + ImportDataTask.performImportIfPossible(context); + } catch (Exception e) { + // Migration failed. Clear workspace. + clearDb = true; + } + + if (!clearDb && GridSizeMigrationTask.ENABLED && + !GridSizeMigrationTask.migrateGridIfNeeded(context)) { + // Migration failed. Clear workspace. + clearDb = true; + } + + if (clearDb) { + Log.d(TAG, "loadWorkspace: resetting launcher database"); + LauncherSettings.Settings.call(contentResolver, + LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); + } + + Log.d(TAG, "loadWorkspace: loading default favorites"); + LauncherSettings.Settings.call(contentResolver, + LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES); + + synchronized (mBgDataModel) { + mBgDataModel.clear(); + + final HashMap<String, Integer> installingPkgs = + mPackageInstaller.updateAndGetActiveSessionCache(); + mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context)); + + Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>(); + final LoaderCursor c = new LoaderCursor(contentResolver.query( + LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp); + + HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null; + + try { + final int appWidgetIdIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.APPWIDGET_ID); + final int appWidgetProviderIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.APPWIDGET_PROVIDER); + final int spanXIndex = c.getColumnIndexOrThrow + (LauncherSettings.Favorites.SPANX); + final int spanYIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.SPANY); + final int rankIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.RANK); + final int optionsIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.OPTIONS); + + final LongSparseArray<UserHandle> allUsers = c.allUsers; + final LongSparseArray<Boolean> quietMode = new LongSparseArray<>(); + final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>(); + for (UserHandle user : mUserManager.getUserProfiles()) { + long serialNo = mUserManager.getSerialNumberForUser(user); + allUsers.put(serialNo, user); + quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user)); + + boolean userUnlocked = mUserManager.isUserUnlocked(user); + + // We can only query for shortcuts when the user is unlocked. + if (userUnlocked) { + List<ShortcutInfoCompat> pinnedShortcuts = + mShortcutManager.queryForPinnedShortcuts(null, user); + if (mShortcutManager.wasLastCallSuccess()) { + for (ShortcutInfoCompat shortcut : pinnedShortcuts) { + shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), + shortcut); + } + } else { + // Shortcut manager can fail due to some race condition when the + // lock state changes too frequently. For the purpose of the loading + // shortcuts, consider the user is still locked. + userUnlocked = false; + } + } + unlockedUsers.put(serialNo, userUnlocked); + } + + ShortcutInfo info; + LauncherAppWidgetInfo appWidgetInfo; + Intent intent; + String targetPkg; + + FolderIconPreviewVerifier verifier = + new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile()); + while (!mStopped && c.moveToNext()) { + try { + if (c.user == null) { + // User has been deleted, remove the item. + c.markDeleted("User has been deleted"); + continue; + } + + boolean allowMissingTarget = false; + switch (c.itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: + case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: + intent = c.parseIntent(); + if (intent == null) { + c.markDeleted("Invalid or null intent"); + continue; + } + + int disabledState = quietMode.get(c.serialNumber) ? + ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0; + ComponentName cn = intent.getComponent(); + targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); + + if (!Process.myUserHandle().equals(c.user)) { + if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { + c.markDeleted("Legacy shortcuts are only allowed for default user"); + continue; + } else if (c.restoreFlag != 0) { + // Don't restore items for other profiles. + c.markDeleted("Restore from managed profile not supported"); + continue; + } + } + if (TextUtils.isEmpty(targetPkg) && + c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { + c.markDeleted("Only legacy shortcuts can have null package"); + continue; + } + + // If there is no target package, its an implicit intent + // (legacy shortcut) which is always valid + boolean validTarget = TextUtils.isEmpty(targetPkg) || + mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user); + + if (cn != null && validTarget) { + // If the apk is present and the shortcut points to a specific + // component. + + // If the component is already present + if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) { + // no special handling necessary for this item + c.markRestored(); + } else { + if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) { + // We allow auto install apps to have their intent + // updated after an install. + intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); + if (intent != null) { + c.restoreFlag = 0; + c.updater().put( + LauncherSettings.Favorites.INTENT, + intent.toUri(0)).commit(); + cn = intent.getComponent(); + } else { + c.markDeleted("Unable to find a launch target"); + continue; + } + } else { + // The app is installed but the component is no + // longer available. + c.markDeleted("Invalid component removed: " + cn); + continue; + } + } + } + // else if cn == null => can't infer much, leave it + // else if !validPkg => could be restored icon or missing sd-card + + if (!TextUtils.isEmpty(targetPkg) && !validTarget) { + // Points to a valid app (superset of cn != null) but the apk + // is not available. + + if (c.restoreFlag != 0) { + // Package is not yet available but might be + // installed later. + FileLog.d(TAG, "package not yet restored: " + targetPkg); + + if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) { + // Restore has started once. + } else if (installingPkgs.containsKey(targetPkg)) { + // App restore has started. Update the flag + c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED; + c.updater().commit(); + } else { + c.markDeleted("Unrestored app removed: " + targetPkg); + continue; + } + } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { + // Package is present but not available. + disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE; + // Add the icon on the workspace anyway. + allowMissingTarget = true; + } else if (!isSdCardReady) { + // SdCard is not ready yet. Package might get available, + // once it is ready. + Log.d(TAG, "Missing pkg, will check later: " + targetPkg); + pendingPackages.addToList(c.user, targetPkg); + // Add the icon on the workspace anyway. + allowMissingTarget = true; + } else { + // Do not wait for external media load anymore. + c.markDeleted("Invalid package removed: " + targetPkg); + continue; + } + } + + if (validTarget) { + // The shortcut points to a valid target (either no target + // or something which is ready to be used) + c.markRestored(); + } + + boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() && + !verifier.isItemInPreview(c.getInt(rankIndex)); + + if (c.restoreFlag != 0) { + // Already verified above that user is same as default user + info = c.getRestoredItemInfo(intent); + } else if (c.itemType == + LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + info = c.getAppShortcutInfo( + intent, allowMissingTarget, useLowResIcon); + } else if (c.itemType == + LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + + ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); + if (unlockedUsers.get(c.serialNumber)) { + ShortcutInfoCompat pinnedShortcut = + shortcutKeyToPinnedShortcuts.get(key); + if (pinnedShortcut == null) { + // The shortcut is no longer valid. + c.markDeleted("Pinned shortcut not found"); + continue; + } + info = new ShortcutInfo(pinnedShortcut, context); + info.iconBitmap = LauncherIcons + .createShortcutIcon(pinnedShortcut, context); + if (pmHelper.isAppSuspended( + pinnedShortcut.getPackage(), info.user)) { + info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; + } + intent = info.intent; + } else { + // Create a shortcut info in disabled mode for now. + info = c.loadSimpleShortcut(); + info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER; + } + } else { // item type == ITEM_TYPE_SHORTCUT + info = c.loadSimpleShortcut(); + + // Shortcuts are only available on the primary profile + if (!TextUtils.isEmpty(targetPkg) + && pmHelper.isAppSuspended(targetPkg, c.user)) { + disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; + } + + // App shortcuts that used to be automatically added to Launcher + // didn't always have the correct intent flags set, so do that + // here + if (intent.getAction() != null && + intent.getCategories() != null && + intent.getAction().equals(Intent.ACTION_MAIN) && + intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + } + } + + if (info != null) { + c.applyCommonProperties(info); + + info.intent = intent; + info.rank = c.getInt(rankIndex); + info.spanX = 1; + info.spanY = 1; + info.isDisabled |= disabledState; + if (isSafeMode && !Utilities.isSystemApp(context, intent)) { + info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE; + } + + if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { + Integer progress = installingPkgs.get(targetPkg); + if (progress != null) { + info.setInstallProgress(progress); + } else { + info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; + } + } + + c.checkAndAddItem(info, mBgDataModel); + } else { + throw new RuntimeException("Unexpected null ShortcutInfo"); + } + break; + + case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: + FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); + c.applyCommonProperties(folderInfo); + + // Do not trim the folder label, as is was set by the user. + folderInfo.title = c.getString(c.titleIndex); + folderInfo.spanX = 1; + folderInfo.spanY = 1; + folderInfo.options = c.getInt(optionsIndex); + + // no special handling required for restored folders + c.markRestored(); + + c.checkAndAddItem(folderInfo, mBgDataModel); + break; + + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: + // Read all Launcher-specific widget details + boolean customWidget = c.itemType == + LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + + int appWidgetId = c.getInt(appWidgetIdIndex); + String savedProvider = c.getString(appWidgetProviderIndex); + + final ComponentName component = + ComponentName.unflattenFromString(savedProvider); + + final boolean isIdValid = !c.hasRestoreFlag( + LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + final boolean wasProviderReady = !c.hasRestoreFlag( + LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); + + if (widgetProvidersMap == null) { + widgetProvidersMap = mAppWidgetManager.getAllProvidersMap(); + } + final AppWidgetProviderInfo provider = widgetProvidersMap.get( + new ComponentKey( + ComponentName.unflattenFromString(savedProvider), + c.user)); + + final boolean isProviderReady = isValidProvider(provider); + if (!isSafeMode && !customWidget && + wasProviderReady && !isProviderReady) { + c.markDeleted( + "Deleting widget that isn't installed anymore: " + + provider); + } else { + if (isProviderReady) { + appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, + provider.provider); + + // The provider is available. So the widget is either + // available or not available. We do not need to track + // any future restore updates. + int status = c.restoreFlag & + ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; + if (!wasProviderReady) { + // If provider was not previously ready, update the + // status and UI flag. + + // Id would be valid only if the widget restore broadcast was received. + if (isIdValid) { + status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; + } else { + status &= ~LauncherAppWidgetInfo + .FLAG_PROVIDER_NOT_READY; + } + } + appWidgetInfo.restoreStatus = status; + } else { + Log.v(TAG, "Widget restore pending id=" + c.id + + " appWidgetId=" + appWidgetId + + " status =" + c.restoreFlag); + appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, + component); + appWidgetInfo.restoreStatus = c.restoreFlag; + Integer installProgress = installingPkgs.get(component.getPackageName()); + + if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { + // Restore has started once. + } else if (installProgress != null) { + // App restore has started. Update the flag + appWidgetInfo.restoreStatus |= + LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; + } else if (!isSafeMode) { + c.markDeleted("Unrestored widget removed: " + component); + continue; + } + + appWidgetInfo.installProgress = + installProgress == null ? 0 : installProgress; + } + if (appWidgetInfo.hasRestoreFlag( + LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { + appWidgetInfo.bindOptions = c.parseIntent(); + } + + c.applyCommonProperties(appWidgetInfo); + appWidgetInfo.spanX = c.getInt(spanXIndex); + appWidgetInfo.spanY = c.getInt(spanYIndex); + appWidgetInfo.user = c.user; + + if (!c.isOnWorkspaceOrHotseat()) { + c.markDeleted("Widget found where container != " + + "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); + continue; + } + + if (!customWidget) { + String providerName = + appWidgetInfo.providerName.flattenToString(); + if (!providerName.equals(savedProvider) || + (appWidgetInfo.restoreStatus != c.restoreFlag)) { + c.updater() + .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, + providerName) + .put(LauncherSettings.Favorites.RESTORED, + appWidgetInfo.restoreStatus) + .commit(); + } + } + + if (appWidgetInfo.restoreStatus != + LauncherAppWidgetInfo.RESTORE_COMPLETED) { + String pkg = appWidgetInfo.providerName.getPackageName(); + appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg); + appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user; + mIconCache.getTitleAndIconForApp( + appWidgetInfo.pendingItemInfo, false); + } + + c.checkAndAddItem(appWidgetInfo, mBgDataModel); + } + break; + } + } catch (Exception e) { + Log.e(TAG, "Desktop items loading interrupted", e); + } + } + } finally { + Utilities.closeSilently(c); + } + + // Break early if we've stopped loading + if (mStopped) { + mBgDataModel.clear(); + return; + } + + // Remove dead items + if (c.commitDeleted()) { + // Remove any empty folder + ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings + .call(contentResolver, + LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS) + .getSerializable(LauncherSettings.Settings.EXTRA_VALUE); + for (long folderId : deletedFolderIds) { + mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); + mBgDataModel.folders.remove(folderId); + mBgDataModel.itemsIdMap.remove(folderId); + } + + // Remove any ghost widgets + LauncherSettings.Settings.call(contentResolver, + LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS); + } + + // Unpin shortcuts that don't exist on the workspace. + HashSet<ShortcutKey> pendingShortcuts = + InstallShortcutReceiver.getPendingShortcuts(context); + for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) { + MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key); + if ((numTimesPinned == null || numTimesPinned.value == 0) + && !pendingShortcuts.contains(key)) { + // Shortcut is pinned but doesn't exist on the workspace; unpin it. + mShortcutManager.unpinShortcut(key); + } + } + + FolderIconPreviewVerifier verifier = + new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile()); + // Sort the folder items and make sure all items in the preview are high resolution. + for (FolderInfo folder : mBgDataModel.folders) { + Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); + verifier.setFolderInfo(folder); + + int numItemsInPreview = 0; + for (ShortcutInfo info : folder.contents) { + if (info.usingLowResIcon + && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION + && verifier.isItemInPreview(info.rank)) { + mIconCache.getTitleAndIcon(info, false); + numItemsInPreview++; + } + + if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) { + break; + } + } + } + + c.commitRestoredItems(); + if (!isSdCardReady && !pendingPackages.isEmpty()) { + context.registerReceiver( + new SdCardAvailableReceiver(mApp, pendingPackages), + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), + null, + new Handler(LauncherModel.getWorkerLooper())); + } + + // Remove any empty screens + ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens); + for (ItemInfo item: mBgDataModel.itemsIdMap) { + long screenId = item.screenId; + if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && + unusedScreens.contains(screenId)) { + unusedScreens.remove(screenId); + } + } + + // If there are any empty screens remove them, and update. + if (unusedScreens.size() != 0) { + mBgDataModel.workspaceScreens.removeAll(unusedScreens); + LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens); + } + } + if (LauncherAppState.PROFILE_STARTUP) { + Trace.endSection(); + } + } + + private void updateIconCache() { + // Ignore packages which have a promise icon. + HashSet<String> packagesToIgnore = new HashSet<>(); + synchronized (mBgDataModel) { + for (ItemInfo info : mBgDataModel.itemsIdMap) { + if (info instanceof ShortcutInfo) { + ShortcutInfo si = (ShortcutInfo) info; + if (si.isPromise() && si.getTargetComponent() != null) { + packagesToIgnore.add(si.getTargetComponent().getPackageName()); + } + } else if (info instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info; + if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { + packagesToIgnore.add(lawi.providerName.getPackageName()); + } + } + } + } + mIconCache.updateDbIcons(packagesToIgnore); + } + + private void loadAllApps() { + final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + + final List<UserHandle> profiles = mUserManager.getUserProfiles(); + + // Clear the list of apps + mBgAllAppsList.clear(); + for (UserHandle user : profiles) { + // Query for the set of apps + final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user); + if (DEBUG_LOADERS) { + Log.d(TAG, "getActivityList took " + + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user); + Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user); + } + // Fail if we don't have any apps + // TODO: Fix this. Only fail for the current user. + if (apps == null || apps.isEmpty()) { + return; + } + boolean quietMode = mUserManager.isQuietModeEnabled(user); + // Create the ApplicationInfos + for (int i = 0; i < apps.size(); i++) { + LauncherActivityInfo app = apps.get(i); + // This builds the icon bitmaps. + mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); + } + + ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user); + } + + if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { + // get all active sessions and add them to the all apps list + for (PackageInstaller.SessionInfo info : + mPackageInstaller.getAllVerifiedSessions()) { + mBgAllAppsList.addPromiseApp(mApp.getContext(), + PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info)); + } + } + + mBgAllAppsList.added = new ArrayList<>(); + if (DEBUG_LOADERS) { + Log.d(TAG, "All apps loaded in in " + + (SystemClock.uptimeMillis() - loadTime) + "ms"); + } + } + + private void loadDeepShortcuts() { + mBgDataModel.deepShortcutMap.clear(); + mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission(); + if (mBgDataModel.hasShortcutHostPermission) { + for (UserHandle user : mUserManager.getUserProfiles()) { + if (mUserManager.isUserUnlocked(user)) { + List<ShortcutInfoCompat> shortcuts = + mShortcutManager.queryForAllShortcuts(user); + mBgDataModel.updateDeepShortcutMap(null, user, shortcuts); + } + } + } + } + + public static boolean isValidProvider(AppWidgetProviderInfo provider) { + return (provider != null) && (provider.provider != null) + && (provider.provider.getPackageName() != null); + } +} |