Merge "Fix an issue with concurrent modification on widget holders list" into tm-qpr-dev

This commit is contained in:
TreeHugger Robot 2023-03-06 20:05:29 +00:00 committed by Android (Google) Code Review
commit f22345eace
1 changed files with 42 additions and 34 deletions

View File

@ -22,6 +22,7 @@ import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.util.Log;
import android.util.SparseArray;
import android.widget.RemoteViews;
@ -33,14 +34,14 @@ import androidx.annotation.WorkerThread;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.BiConsumer;
import java.util.function.IntConsumer;
@ -50,6 +51,8 @@ import java.util.function.IntConsumer;
*/
public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
private static final String TAG = "QuickstepWidgetHolder";
private static final UpdateKey<AppWidgetProviderInfo> KEY_PROVIDER_UPDATE =
AppWidgetHostView::onUpdateProviderInfo;
private static final UpdateKey<RemoteViews> KEY_VIEWS_UPDATE =
@ -63,6 +66,8 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
private static AppWidgetHost sWidgetHost = null;
private final SparseArray<AppWidgetHostView> mViews = new SparseArray<>();
private final @Nullable RemoteViews.InteractionHandler mInteractionHandler;
private final @NonNull IntConsumer mAppWidgetRemovedCallback;
@ -71,15 +76,14 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
// Map to all pending updated keyed with appWidgetId;
private final SparseArray<PendingUpdate> mPendingUpdateMap = new SparseArray<>();
@Thunk
QuickstepWidgetHolder(@NonNull Context context,
private QuickstepWidgetHolder(@NonNull Context context,
@Nullable IntConsumer appWidgetRemovedCallback,
@Nullable RemoteViews.InteractionHandler interactionHandler) {
super(context, appWidgetRemovedCallback);
mAppWidgetRemovedCallback = appWidgetRemovedCallback != null ? appWidgetRemovedCallback
: i -> {};
mInteractionHandler = interactionHandler;
sHolders.add(this);
MAIN_EXECUTOR.execute(() -> sHolders.add(this));
}
@Override
@ -92,7 +96,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
sHolders.forEach(h -> h.mAppWidgetRemovedCallback.accept(i))),
() -> MAIN_EXECUTOR.execute(() ->
sHolders.forEach(h -> h.mProviderChangedListeners.forEach(
ProviderChangedListener::notifyWidgetProvidersChanged))),
ProviderChangedListener::notifyWidgetProvidersChanged))),
UI_HELPER_EXECUTOR.getLooper());
if (!WidgetsModel.GO_DISABLE_WIDGETS) {
sWidgetHost.startListening();
@ -107,11 +111,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
int count = mPendingUpdateMap.size();
for (int i = 0; i < count; i++) {
int widgetId = mPendingUpdateMap.keyAt(i);
QuickstepWidgetHolderListener listener = sListeners.get(widgetId);
if (listener == null) {
continue;
}
AppWidgetHostView view = listener.mView.get(this);
AppWidgetHostView view = mViews.get(widgetId);
if (view == null) {
continue;
}
@ -131,7 +131,16 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
mPendingUpdateMap.clear();
}
private <T> void addPendingAction(int widgetId, UpdateKey<T> key, T data) {
private <T> void onWidgetUpdate(int widgetId, UpdateKey<T> key, T data) {
if (isListening()) {
AppWidgetHostView view = mViews.get(widgetId);
if (view == null) {
return;
}
key.accept(view, data);
return;
}
PendingUpdate pendingUpdate = mPendingUpdateMap.get(widgetId);
if (pendingUpdate == null) {
pendingUpdate = new PendingUpdate();
@ -167,7 +176,11 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
*/
@Override
public void destroy() {
sHolders.remove(this);
try {
MAIN_EXECUTOR.submit(() -> sHolders.remove(this)).get();
} catch (Exception e) {
Log.e(TAG, "Failed to remove self from holder list", e);
}
}
@Override
@ -228,15 +241,16 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
}
widgetView.setInteractionHandler(mInteractionHandler);
widgetView.setAppWidget(appWidgetId, appWidget);
mViews.put(appWidgetId, widgetView);
QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId);
if (listener == null) {
listener = new QuickstepWidgetHolderListener(appWidgetId, this, widgetView);
listener = new QuickstepWidgetHolderListener(appWidgetId);
sWidgetHost.setListener(appWidgetId, listener);
sListeners.put(appWidgetId, listener);
} else {
listener.resetView(this, widgetView);
}
RemoteViews remoteViews = listener.addHolder(this);
widgetView.updateAppWidget(remoteViews);
return widgetView;
}
@ -247,31 +261,30 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
@Override
public void clearViews() {
for (int i = sListeners.size() - 1; i >= 0; i--) {
sListeners.valueAt(i).mView.remove(this);
sListeners.valueAt(i).mListeningHolders.remove(this);
}
}
private static class QuickstepWidgetHolderListener
implements AppWidgetHost.AppWidgetHostListener {
@NonNull
private final Map<QuickstepWidgetHolder, AppWidgetHostView> mView = new WeakHashMap<>();
// Static listeners should use a set that is backed by WeakHashMap to avoid memory leak
private final Set<QuickstepWidgetHolder> mListeningHolders = Collections.newSetFromMap(
new WeakHashMap<>());
private final int mWidgetId;
@Nullable private RemoteViews mRemoteViews = null;
private @Nullable RemoteViews mRemoteViews;
QuickstepWidgetHolderListener(int widgetId, @NonNull QuickstepWidgetHolder holder,
@NonNull LauncherAppWidgetHostView view) {
QuickstepWidgetHolderListener(int widgetId) {
mWidgetId = widgetId;
mView.put(holder, view);
}
@UiThread
public void resetView(@NonNull QuickstepWidgetHolder holder,
@NonNull AppWidgetHostView view) {
mView.put(holder, view);
view.updateAppWidget(mRemoteViews);
@Nullable
public RemoteViews addHolder(@NonNull QuickstepWidgetHolder holder) {
mListeningHolders.add(holder);
return mRemoteViews;
}
@Override
@ -295,13 +308,8 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
}
private <T> void executeOnMainExecutor(UpdateKey<T> key, T data) {
MAIN_EXECUTOR.execute(() -> mView.forEach((holder, view) -> {
if (holder.isListening()) {
key.accept(view, data);
} else {
holder.addPendingAction(mWidgetId, key, data);
}
}));
MAIN_EXECUTOR.execute(() -> mListeningHolders.forEach(holder ->
holder.onWidgetUpdate(mWidgetId, key, data)));
}
}