Defensively copy callback collections before invoking

This is done in order to prevent ConcurrentModificationExceptions,
caused by the callback unregistering itself when invoked. The pattern
used by this CL is also used by StatusBarStateControllerImpl.

Fixes: 192192372
Test: manual
Change-Id: Ibac2282d5c9242ae063adb2247d4297291fa3abf
Merged-In: Ibac2282d5c9242ae063adb2247d4297291fa3abf
This commit is contained in:
Steve Elliott
2021-07-13 12:18:23 -04:00
parent a40b1cb418
commit c6f61ee9a9
4 changed files with 21 additions and 16 deletions

View File

@@ -87,7 +87,7 @@ public class KeyguardBouncer {
private final Runnable mResetRunnable = ()-> {
if (mKeyguardViewController != null) {
mKeyguardViewController.resetSecurityContainer();
for (KeyguardResetCallback callback : mResetCallbacks) {
for (KeyguardResetCallback callback : new ArrayList<>(mResetCallbacks)) {
callback.onKeyguardReset();
}
}

View File

@@ -55,16 +55,18 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -100,7 +102,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private ForcePluginOpenListener mForcePluginOpenListener;
private Consumer<Integer> mScrimsVisibilityListener;
private final ArrayList<WeakReference<StatusBarWindowCallback>>
mCallbacks = Lists.newArrayList();
mCallbacks = new ArrayList<>();
private final SysuiColorExtractor mColorExtractor;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -464,13 +466,15 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
@Override
public void notifyStateChangedCallbacks() {
for (int i = 0; i < mCallbacks.size(); i++) {
StatusBarWindowCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onStateChanged(mCurrentState.mKeyguardShowing,
mCurrentState.mKeyguardOccluded,
mCurrentState.mBouncerShowing);
}
// Copy callbacks to separate ArrayList to avoid concurrent modification
List<StatusBarWindowCallback> activeCallbacks = mCallbacks.stream()
.map(Reference::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
for (StatusBarWindowCallback cb : activeCallbacks) {
cb.onStateChanged(mCurrentState.mKeyguardShowing,
mCurrentState.mKeyguardOccluded,
mCurrentState.mBouncerShowing);
}
}

View File

@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
/**
@@ -157,7 +158,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(),
entry.getSbn().getPackageName(), entry.getSbn().getInstanceId());
}
for (OnHeadsUpChangedListener listener : mListeners) {
for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) {
if (isPinned) {
listener.onHeadsUpPinned(entry);
} else {
@@ -177,7 +178,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
entry.setHeadsUp(true);
setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */);
for (OnHeadsUpChangedListener listener : mListeners) {
for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) {
listener.onHeadsUpStateChanged(entry, true);
}
}
@@ -188,7 +189,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
entry.setHeadsUp(false);
setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */);
for (OnHeadsUpChangedListener listener : mListeners) {
for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) {
listener.onHeadsUpStateChanged(entry, false);
}
}
@@ -206,7 +207,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
if (mHasPinnedNotification) {
MetricsLogger.count(mContext, "note_peek", 1);
}
for (OnHeadsUpChangedListener listener : mListeners) {
for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) {
listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
}
}

View File

@@ -807,7 +807,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) {
for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) {
for (View.OnFocusChangeListener listener : new ArrayList<>(mEditTextFocusChangeListeners)) {
listener.onFocusChange(remoteEditText, focused);
}
}