Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca1ee7a1 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "launcher-loading" into sc-v2-dev

* changes:
  Improve workspace loading times.
  Add tracing to help in launcher load time profiling.
parents 104ed0b9 b988ab77
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.Trace;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.Log;
@@ -280,6 +281,11 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche

    private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;

    private static final String DISPLAY_WORKSPACE_TRACE_METHOD_NAME = "DisplayWorkspaceFirstFrame";
    private static final String DISPLAY_ALL_APPS_TRACE_METHOD_NAME = "DisplayAllApps";
    public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0;
    public static final int DISPLAY_ALL_APPS_TRACE_COOKIE = 1;

    private Configuration mOldConfig;

    @Thunk
@@ -366,7 +372,15 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
    private LauncherState mPrevLauncherState;

    @Override
    @TargetApi(Build.VERSION_CODES.S)
    protected void onCreate(Bundle savedInstanceState) {
        // Only use a hard-coded cookie since we only want to trace this once.
        if (Utilities.ATLEAST_S) {
            Trace.beginAsyncSection(
                    DISPLAY_WORKSPACE_TRACE_METHOD_NAME, DISPLAY_WORKSPACE_TRACE_COOKIE);
            Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
                    DISPLAY_ALL_APPS_TRACE_COOKIE);
        }
        Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
                TraceHelper.FLAG_UI_EVENT);
        if (DEBUG_STRICT_MODE) {
@@ -2584,6 +2598,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
    }

    @Override
    @TargetApi(Build.VERSION_CODES.S)
    public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
        mSynchronouslyBoundPages = boundPages;
        mPagesToBindSynchronously = new IntSet();
@@ -2606,6 +2621,10 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
            executor.onLoadAnimationCompleted();
        }
        executor.attachTo(this);
        if (Utilities.ATLEAST_S) {
            Trace.endAsyncSection(DISPLAY_WORKSPACE_TRACE_METHOD_NAME,
                    DISPLAY_WORKSPACE_TRACE_COOKIE);
        }
    }

    /**
@@ -2669,9 +2688,14 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
     * Implementation of the method from LauncherModel.Callbacks.
     */
    @Override
    @TargetApi(Build.VERSION_CODES.S)
    public void bindAllApplications(AppInfo[] apps, int flags) {
        mAppsView.getAppsStore().setApps(apps, flags);
        PopupContainerWithArrow.dismissInvalidPopup(this);
        if (Utilities.ATLEAST_S) {
            Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
                    DISPLAY_ALL_APPS_TRACE_COOKIE);
        }
    }

    /**
+5 −0
Original line number Diff line number Diff line
@@ -147,6 +147,11 @@ public final class FeatureFlags {
    public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag(
            "ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace");

    public static final BooleanFlag ENABLE_BULK_WORKSPACE_ICON_LOADING = getDebugFlag(
            "ENABLE_BULK_WORKSPACE_ICON_LOADING",
            false,
            "Enable loading workspace icons in bulk.");

    // Keep as DeviceFlag for remote disable in emergency.
    public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
            "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
+94 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.launcher3.icons;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;

import static java.util.stream.Collectors.groupingBy;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -30,10 +32,15 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.graphics.drawable.Drawable;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;

import androidx.annotation.NonNull;

@@ -47,6 +54,7 @@ import com.android.launcher3.icons.cache.BaseIconCache;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -56,8 +64,13 @@ import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * Cache of application icons.  Icons can be made from any thread.
@@ -306,6 +319,87 @@ public class IconCache extends BaseIconCache {
        applyCacheEntry(entry, infoInOut);
    }

    /**
     * Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles.
     *
     * @param iconRequestInfos List of IconRequestInfos representing titles and icons to query.
     * @param user UserHandle all the given iconRequestInfos share
     * @param useLowResIcons whether we should exclude the icon column from the sql results.
     */
    private <T extends ItemInfoWithIcon> Cursor createBulkQueryCursor(
            List<IconRequestInfo<T>> iconRequestInfos, UserHandle user, boolean useLowResIcons)
            throws SQLiteException {
        String[] queryParams = Stream.concat(
                iconRequestInfos.stream()
                        .map(r -> r.itemInfo.getTargetComponent())
                        .filter(Objects::nonNull)
                        .distinct()
                        .map(ComponentName::flattenToString),
                Stream.of(Long.toString(getSerialNumberForUser(user)))).toArray(String[]::new);
        String componentNameQuery = TextUtils.join(
                ",", Collections.nCopies(queryParams.length - 1, "?"));

        return mIconDb.query(
                useLowResIcons ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES,
                IconDB.COLUMN_COMPONENT
                        + " IN ( " + componentNameQuery + " )"
                        + " AND " + IconDB.COLUMN_USER + " = ?",
                queryParams);
    }

    /**
     * Load and fill icons requested in iconRequestInfos using a single bulk sql query.
     */
    public synchronized <T extends ItemInfoWithIcon> void getTitlesAndIconsInBulk(
            List<IconRequestInfo<T>> iconRequestInfos) {
        Map<Pair<UserHandle, Boolean>, List<IconRequestInfo<T>>> iconLoadSubsectionsMap =
                iconRequestInfos.stream()
                        .collect(groupingBy(iconRequest ->
                                Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon)));

        Trace.beginSection("loadIconsInBulk");
        iconLoadSubsectionsMap.forEach((sectionKey, filteredList) -> {
            Map<ComponentName, List<IconRequestInfo<T>>> duplicateIconRequestsMap =
                    filteredList.stream()
                            .collect(groupingBy(iconRequest ->
                                    iconRequest.itemInfo.getTargetComponent()));

            Trace.beginSection("loadIconSubsectionInBulk");
            try (Cursor c = createBulkQueryCursor(
                    filteredList,
                    /* user = */ sectionKey.first,
                    /* useLowResIcons = */ sectionKey.second)) {
                int componentNameColumnIndex = c.getColumnIndexOrThrow(IconDB.COLUMN_COMPONENT);
                while (c.moveToNext()) {
                    ComponentName cn = ComponentName.unflattenFromString(
                            c.getString(componentNameColumnIndex));
                    List<IconRequestInfo<T>> duplicateIconRequests =
                            duplicateIconRequestsMap.get(cn);

                    if (cn != null) {
                        CacheEntry entry = cacheLocked(
                                cn,
                                /* user = */ sectionKey.first,
                                () -> duplicateIconRequests.get(0).launcherActivityInfo,
                                mLauncherActivityInfoCachingLogic,
                                c,
                                /* usePackageIcon= */ false,
                                /* useLowResIcons = */ sectionKey.second);

                        for (IconRequestInfo<T> iconRequest : duplicateIconRequests) {
                            applyCacheEntry(entry, iconRequest.itemInfo);
                        }
                    }
                }
            } catch (SQLiteException e) {
                Log.d(TAG, "Error reading icon cache", e);
            } finally {
                Trace.endSection();
            }
        });
        Trace.endSection();
    }


    /**
     * Fill in {@param infoInOut} with the corresponding icon and label.
+25 −33
Original line number Diff line number Diff line
@@ -16,13 +16,10 @@

package com.android.launcher3.model;

import static android.graphics.BitmapFactory.decodeByteArray;

import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
@@ -45,11 +42,10 @@ import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -184,32 +180,21 @@ public class LoaderCursor extends CursorWrapper {
     * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
     */
    protected boolean loadIcon(WorkspaceItemInfo info) {
        try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
            if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
                String packageName = getString(iconPackageIndex);
                String resourceName = getString(iconResourceIndex);
                if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
                    info.iconResource = new ShortcutIconResource();
                    info.iconResource.packageName = packageName;
                    info.iconResource.resourceName = resourceName;
                    BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
                    if (iconInfo != null) {
                        info.bitmap = iconInfo;
                        return true;
                    }
                }
        return createIconRequestInfo(info, false).loadWorkspaceIcon(mContext);
    }

            // Failed to load from resource, try loading from DB.
            byte[] data = getBlob(iconIndex);
            try {
                info.bitmap = li.createIconBitmap(decodeByteArray(data, 0, data.length));
                return true;
            } catch (Exception e) {
                Log.e(TAG, "Failed to decode byte array for info " + info, e);
                return false;
            }
        }
    public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
            WorkspaceItemInfo wai, boolean useLowResIcon) {
        String packageName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
                ? getString(iconPackageIndex) : null;
        String resourceName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
                ? getString(iconResourceIndex) : null;
        byte[] iconBlob = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
                || restoreFlag != 0
                ? getBlob(iconIndex) : null;

        return new IconRequestInfo<>(
                wai, mActivityInfo, packageName, resourceName, iconBlob, useLowResIcon);
    }

    /**
@@ -262,6 +247,11 @@ public class LoaderCursor extends CursorWrapper {
     */
    public WorkspaceItemInfo getAppShortcutInfo(
            Intent intent, boolean allowMissingTarget, boolean useLowResIcon) {
        return getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon, true);
    }

    public WorkspaceItemInfo getAppShortcutInfo(
            Intent intent, boolean allowMissingTarget, boolean useLowResIcon, boolean loadIcon) {
        if (user == null) {
            Log.d(TAG, "Null user found in getShortcutInfo");
            return null;
@@ -288,10 +278,12 @@ public class LoaderCursor extends CursorWrapper {
        info.user = user;
        info.intent = newIntent;

        if (loadIcon) {
            mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
            if (mIconCache.isDefaultIcon(info.bitmap, user)) {
                loadIcon(info);
            }
        }

        if (mActivityInfo != null) {
            AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo);
+37 −3
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -71,6 +72,7 @@ import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -197,7 +199,12 @@ public class LoaderTask implements Runnable {
        TimingLogger logger = new TimingLogger(TAG, "run");
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            List<ShortcutInfo> allShortcuts = new ArrayList<>();
            Trace.beginSection("LoadWorkspace");
            try {
                loadWorkspace(allShortcuts);
            } finally {
                Trace.endSection();
            }
            logASplit(logger, "loadWorkspace");

            // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
@@ -225,7 +232,13 @@ public class LoaderTask implements Runnable {
            verifyNotStopped();

            // second step
            List<LauncherActivityInfo> allActivityList = loadAllApps();
            Trace.beginSection("LoadAllApps");
            List<LauncherActivityInfo> allActivityList;
            try {
               allActivityList = loadAllApps();
            } finally {
                Trace.endSection();
            }
            logASplit(logger, "loadAllApps");

            verifyNotStopped();
@@ -408,6 +421,7 @@ public class LoaderTask implements Runnable {
                LauncherAppWidgetProviderInfo widgetProviderInfo;
                Intent intent;
                String targetPkg;
                List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();

                while (!mStopped && c.moveToNext()) {
                    try {
@@ -530,7 +544,10 @@ public class LoaderTask implements Runnable {
                            } else if (c.itemType ==
                                    LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                                info = c.getAppShortcutInfo(
                                        intent, allowMissingTarget, useLowResIcon);
                                        intent,
                                        allowMissingTarget,
                                        useLowResIcon,
                                        !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get());
                            } else if (c.itemType ==
                                    LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {

@@ -582,6 +599,8 @@ public class LoaderTask implements Runnable {
                            }

                            if (info != null) {
                                iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon));

                                c.applyCommonProperties(info);

                                info.intent = intent;
@@ -799,6 +818,21 @@ public class LoaderTask implements Runnable {
                        Log.e(TAG, "Desktop items loading interrupted", e);
                    }
                }
                if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) {
                    Trace.beginSection("LoadWorkspaceIconsInBulk");
                    try {
                        mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
                        for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo :
                                iconRequestInfos) {
                            WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
                            if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
                                iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
                            }
                        }
                    } finally {
                        Trace.endSection();
                    }
                }
            } finally {
                IOUtils.closeSilently(c);
            }
Loading