Merge "Fixing lock during RecentsModel initialization" into ub-launcher3-master
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 2b10989..b1b9396 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -46,7 +46,6 @@
private final Context mContext;
private final RecentsAnimationDeviceState mDeviceState;
- private final RecentsModel mRecentsModel;
private final OverviewComponentObserver mOverviewComponentObserver;
private long mLastToggleTime;
@@ -55,7 +54,6 @@
OverviewComponentObserver observer) {
mContext = context;
mDeviceState = deviceState;
- mRecentsModel = RecentsModel.INSTANCE.get(mContext);
mOverviewComponentObserver = observer;
}
@@ -158,7 +156,7 @@
ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
// Preload the plan
- mRecentsModel.getTasks(null);
+ RecentsModel.INSTANCE.get(mContext).getTasks(null);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 6c302ae..d47217b 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -18,7 +18,6 @@
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.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 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 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 @@
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,
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 7ff799e..65e89cf 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -17,7 +17,6 @@
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.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 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.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskDescriptionCompat;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -57,7 +54,7 @@
*/
public class TaskIconCache {
- private final Handler mBackgroundHandler;
+ private final Executor mBgExecutor;
private final AccessibilityManager mAccessibilityManager;
private final Context mContext;
@@ -65,9 +62,9 @@
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 @@
* @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 @@
}
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 static abstract class IconLoadRequest extends HandlerRunnable {
- IconLoadRequest(Handler handler) {
- super(handler, null);
- }
- }
-
private static class TaskCacheEntry {
public Drawable icon;
public String contentDescription = "";
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 2b7a8ec..a8a0219 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -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.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 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 @@
* @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 @@
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 @@
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 @@
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
diff --git a/quickstep/src/com/android/quickstep/util/CancellableTask.java b/quickstep/src/com/android/quickstep/util/CancellableTask.java
new file mode 100644
index 0000000..a6e2e81
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/CancellableTask.java
@@ -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;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 034d9fb..8fbf44b 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -86,6 +86,7 @@
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 @@
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;
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index 0a32734..a85ae45 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -20,8 +20,10 @@
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 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;
+ }
+ }
}