From fc9af34b9d0815a27077d29c5aea360a3e73ae57 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Fri, 5 May 2017 14:45:11 -0700 Subject: [PATCH] Fixed a concurrent modification crash Test: runtest -x packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserTrackerTest.java Change-Id: I23261843b7366d3a66a795a41c61b7661f7ca3a6 Fixes: 38006784 --- .../systemui/settings/CurrentUserTracker.java | 23 +++++-- .../settings/CurrentUserTrackerTest.java | 65 +++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserTrackerTest.java diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java index 005206fcd14c5..0b89dc1070e12 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java @@ -22,6 +22,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -32,7 +34,12 @@ public abstract class CurrentUserTracker { private Consumer mCallback = this::onUserSwitched; public CurrentUserTracker(Context context) { - mUserReceiver = UserReceiver.getInstance(context); + this(UserReceiver.getInstance(context)); + } + + @VisibleForTesting + CurrentUserTracker(UserReceiver receiver) { + mUserReceiver = receiver; } public int getCurrentUserId() { @@ -49,7 +56,8 @@ public abstract class CurrentUserTracker { public abstract void onUserSwitched(int newUserId); - private static class UserReceiver extends BroadcastReceiver { + @VisibleForTesting + static class UserReceiver extends BroadcastReceiver { private static UserReceiver sInstance; private Context mAppContext; @@ -58,7 +66,8 @@ public abstract class CurrentUserTracker { private List> mCallbacks = new ArrayList<>(); - private UserReceiver(Context context) { + @VisibleForTesting + UserReceiver(Context context) { mAppContext = context.getApplicationContext(); } @@ -105,8 +114,12 @@ public abstract class CurrentUserTracker { private void notifyUserSwitched(int newUserId) { if (mCurrentUserId != newUserId) { mCurrentUserId = newUserId; - for (Consumer consumer : mCallbacks) { - consumer.accept(newUserId); + List> callbacks = new ArrayList<>(mCallbacks); + for (Consumer consumer : callbacks) { + // Accepting may modify this list + if (mCallbacks.contains(consumer)) { + consumer.accept(newUserId); + } } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserTrackerTest.java new file mode 100644 index 0000000000000..9e15a054d22fc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserTrackerTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 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.systemui.settings; + +import android.content.Intent; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; + +/** + * Testing functionality of the current user tracker + */ +public class CurrentUserTrackerTest extends SysuiTestCase { + + private CurrentUserTracker mTracker; + private CurrentUserTracker.UserReceiver mReceiver; + + @Before + public void setUp() { + mReceiver = new CurrentUserTracker.UserReceiver(getContext()); + mTracker = new CurrentUserTracker(mReceiver) { + @Override + public void onUserSwitched(int newUserId) { + stopTracking(); + } + }; + } + + @Test + public void testBroadCastDoesntCrashOnConcurrentModification() { + mTracker.startTracking(); + CurrentUserTracker secondTracker = new CurrentUserTracker(mReceiver) { + @Override + public void onUserSwitched(int newUserId) { + stopTracking(); + } + }; + secondTracker.startTracking(); + triggerUserSwitch(); + } + /** + * Simulates a user switch event. + */ + private void triggerUserSwitch() { + Intent intent = new Intent(Intent.ACTION_USER_SWITCHED); + intent.putExtra(Intent.EXTRA_USER_HANDLE, 1); + mReceiver.onReceive(getContext(), intent); + } +}