Merge "Fixing lock during RecentsModel initialization" into ub-launcher3-master

This commit is contained in:
TreeHugger Robot
2020-09-28 19:32:07 +00:00
committed by Android (Google) Code Review
7 changed files with 142 additions and 80 deletions
@@ -46,7 +46,6 @@ public class OverviewCommandHelper {
private final Context mContext;
private final RecentsAnimationDeviceState mDeviceState;
private final RecentsModel mRecentsModel;
private final OverviewComponentObserver mOverviewComponentObserver;
private long mLastToggleTime;
@@ -55,7 +54,6 @@ public class OverviewCommandHelper {
OverviewComponentObserver observer) {
mContext = context;
mDeviceState = deviceState;
mRecentsModel = RecentsModel.INSTANCE.get(mContext);
mOverviewComponentObserver = observer;
}
@@ -158,7 +156,7 @@ public class OverviewCommandHelper {
ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
// Preload the plan
mRecentsModel.getTasks(null);
RecentsModel.INSTANCE.get(mContext).getTasks(null);
}
@Override
@@ -18,7 +18,6 @@ package com.android.quickstep;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import android.annotation.TargetApi;
@@ -26,11 +25,11 @@ import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.os.Build;
import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -40,6 +39,8 @@ import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
/**
@@ -52,6 +53,9 @@ public class RecentsModel extends TaskStackChangeListener {
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
new MainThreadInitializedObject<>(RecentsModel::new);
private static final Executor RECENTS_MODEL_EXECUTOR = Executors.newSingleThreadExecutor(
new SimpleThreadFactory("TaskThumbnailIconCache-", THREAD_PRIORITY_BACKGROUND));
private final List<TaskVisualsChangeListener> mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
@@ -61,12 +65,10 @@ public class RecentsModel extends TaskStackChangeListener {
private RecentsModel(Context context) {
mContext = context;
Looper looper =
createAndStartNewLooper("TaskThumbnailIconCache", THREAD_PRIORITY_BACKGROUND);
mTaskList = new RecentTasksList(MAIN_EXECUTOR,
new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
mIconCache = new TaskIconCache(context, looper);
mThumbnailCache = new TaskThumbnailCache(context, looper);
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR);
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
IconProvider.registerIconChangeListener(context,
@@ -17,7 +17,6 @@ package com.android.quickstep;
import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.ActivityManager.TaskDescription;
import android.content.Context;
@@ -27,8 +26,6 @@ import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityManager;
@@ -37,12 +34,11 @@ import androidx.annotation.WorkerThread;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -50,6 +46,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskDescriptionCompat;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -57,7 +54,7 @@ import java.util.function.Consumer;
*/
public class TaskIconCache {
private final Handler mBackgroundHandler;
private final Executor mBgExecutor;
private final AccessibilityManager mAccessibilityManager;
private final Context mContext;
@@ -65,9 +62,9 @@ public class TaskIconCache {
private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
private final IconProvider mIconProvider;
public TaskIconCache(Context context, Looper backgroundLooper) {
public TaskIconCache(Context context, Executor bgExecutor) {
mContext = context;
mBackgroundHandler = new Handler(backgroundLooper);
mBgExecutor = bgExecutor;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Resources res = context.getResources();
@@ -83,31 +80,27 @@ public class TaskIconCache {
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
public IconLoadRequest updateIconInBackground(Task task, Consumer<Task> callback) {
public CancellableTask updateIconInBackground(Task task, Consumer<Task> callback) {
Preconditions.assertUIThread();
if (task.icon != null) {
// Nothing to load, the icon is already loaded
callback.accept(task);
return null;
}
IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
CancellableTask<TaskCacheEntry> request = new CancellableTask<TaskCacheEntry>() {
@Override
public void run() {
TaskCacheEntry entry = getCacheEntry(task);
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
MAIN_EXECUTOR.execute(() -> {
task.icon = entry.icon;
task.titleDescription = entry.contentDescription;
callback.accept(task);
onEnd();
});
public TaskCacheEntry getResultOnBg() {
return getCacheEntry(task);
}
@Override
public void handleResult(TaskCacheEntry result) {
task.icon = result.icon;
task.titleDescription = result.contentDescription;
callback.accept(task);
}
};
Utilities.postAsyncCallback(mBackgroundHandler, request);
mBgExecutor.execute(request);
return request;
}
@@ -120,9 +113,8 @@ public class TaskIconCache {
}
void invalidateCacheEntries(String pkg, UserHandle handle) {
Utilities.postAsyncCallback(mBackgroundHandler,
() -> mIconCache.removeAll(key ->
pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
mBgExecutor.execute(() -> mIconCache.removeAll(key ->
pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
}
@WorkerThread
@@ -208,12 +200,6 @@ public class TaskIconCache {
}
}
public static abstract class IconLoadRequest extends HandlerRunnable {
IconLoadRequest(Handler handler) {
super(handler, null);
}
}
private static class TaskCacheEntry {
public Drawable icon;
public String contentDescription = "";
@@ -15,17 +15,12 @@
*/
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -33,11 +28,12 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.util.ArrayList;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
public class TaskThumbnailCache {
private final Handler mBackgroundHandler;
private final Executor mBgExecutor;
private final int mCacheSize;
private final TaskKeyLruCache<ThumbnailData> mCache;
@@ -94,8 +90,8 @@ public class TaskThumbnailCache {
}
}
public TaskThumbnailCache(Context context, Looper backgroundLooper) {
mBackgroundHandler = new Handler(backgroundLooper);
public TaskThumbnailCache(Context context, Executor bgExecutor) {
mBgExecutor = bgExecutor;
mHighResLoadingState = new HighResLoadingState(context);
Resources res = context.getResources();
@@ -130,7 +126,7 @@ public class TaskThumbnailCache {
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
public ThumbnailLoadRequest updateThumbnailInBackground(
public CancellableTask updateThumbnailInBackground(
Task task, Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
@@ -142,14 +138,13 @@ public class TaskThumbnailCache {
return null;
}
return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
task.thumbnail = t;
callback.accept(t);
});
}
private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean lowResolution,
private CancellableTask updateThumbnailInBackground(TaskKey key, boolean lowResolution,
Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
@@ -160,26 +155,20 @@ public class TaskThumbnailCache {
return null;
}
ThumbnailLoadRequest request = new ThumbnailLoadRequest(mBackgroundHandler,
lowResolution) {
CancellableTask<ThumbnailData> request = new CancellableTask<ThumbnailData>() {
@Override
public void run() {
ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
public ThumbnailData getResultOnBg() {
return ActivityManagerWrapper.getInstance().getTaskThumbnail(
key.id, lowResolution);
}
MAIN_EXECUTOR.execute(() -> {
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
mCache.put(key, thumbnail);
callback.accept(thumbnail);
onEnd();
});
@Override
public void handleResult(ThumbnailData result) {
mCache.put(key, result);
callback.accept(result);
}
};
Utilities.postAsyncCallback(mBackgroundHandler, request);
mBgExecutor.execute(request);
return request;
}
@@ -218,15 +207,6 @@ public class TaskThumbnailCache {
return mEnableTaskSnapshotPreloading && mHighResLoadingState.mVisible;
}
public static abstract class ThumbnailLoadRequest extends HandlerRunnable {
public final boolean mLowResolution;
ThumbnailLoadRequest(Handler handler, boolean lowResolution) {
super(handler, null);
mLowResolution = lowResolution;
}
}
/**
* @return Whether device supports low-res thumbnails. Low-res files are an optimization
* for faster load times of snapshots. Devices can optionally disable low-res files so that
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2020 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.quickstep.util;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
/**
* Utility class to executore a task on background and post the result on UI thread
*/
public abstract class CancellableTask<T> implements Runnable {
private boolean mCancelled = false;
@Override
public final void run() {
if (mCancelled) {
return;
}
T result = getResultOnBg();
if (mCancelled) {
return;
}
MAIN_EXECUTOR.execute(() -> {
if (mCancelled) {
return;
}
handleResult(result);
});
}
/**
* Called on the worker thread to process the request. The return object is passed to
* {@link #handleResult(Object)}
*/
@WorkerThread
public abstract T getResultOnBg();
/**
* Called on the UI thread to handle the final result.
* @param result
*/
@UiThread
public abstract void handleResult(T result);
/**
* Cancels the request. If it is called before {@link #handleResult(Object)}, that method
* will not be called
*/
public void cancel() {
mCancelled = true;
}
}
@@ -86,6 +86,7 @@ import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.views.RecentsView.PageCallbacks;
@@ -185,8 +186,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private boolean mShowScreenshot;
// The current background requests to load the task thumbnail and icon
private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
private TaskIconCache.IconLoadRequest mIconLoadRequest;
private CancellableTask mThumbnailLoadRequest;
private CancellableTask mIconLoadRequest;
// Order in which the footers appear. Lower order appear below higher order.
public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
@@ -20,8 +20,10 @@ import android.os.Looper;
import android.os.Process;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Various different executors used in Launcher
@@ -83,4 +85,29 @@ public class Executors {
*/
public static final LooperExecutor MODEL_EXECUTOR =
new LooperExecutor(createAndStartNewLooper("launcher-loader"));
/**
* A simple ThreadFactory to set the thread name and priority when used with executors.
*/
public static class SimpleThreadFactory implements ThreadFactory {
private final int mPriority;
private final String mNamePrefix;
private final AtomicInteger mCount = new AtomicInteger(0);
public SimpleThreadFactory(String namePrefix, int priority) {
mNamePrefix = namePrefix;
mPriority = priority;
}
@Override
public Thread newThread(Runnable runnable) {
Thread t = new Thread(() -> {
Process.setThreadPriority(mPriority);
runnable.run();
}, mNamePrefix + mCount.incrementAndGet());
return t;
}
}
}