Fix java.lang.ArrayIndexOutOfBoundsException at ArraySet in GenericWindowPolicyController.onRunningAppsChanged()

- Before iterating over mRunningAppsChangedListener, we need to confirm
  whether there is a listener in mRunningAppsChangedListener to avoid
  ArrayIndexOutOfBoundsException.
- Use mGenericWindowPolicyControllerLock to protect
  mRunningAppsChangedListener access.

Bug: 248192490
Test: Manual
Change-Id: Iedd031f50659f027576086788392110a3432858a
This commit is contained in:
Cliff Wu
2022-10-14 13:46:25 +08:00
parent 4489239d9e
commit 25da3dcd99
2 changed files with 56 additions and 8 deletions

View File

@@ -44,6 +44,7 @@ import android.view.Display;
import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppStreamingActivity;
import java.util.List;
@@ -112,7 +113,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
final ArraySet<Integer> mRunningUids = new ArraySet<>();
@Nullable private final ActivityListener mActivityListener;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListener =
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners =
new ArraySet<>();
@Nullable
private final @AssociationRequest.DeviceProfile String mDeviceProfile;
@@ -178,12 +181,16 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
/** Register a listener for running applications changes. */
public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) {
mRunningAppsChangedListener.add(listener);
synchronized (mGenericWindowPolicyControllerLock) {
mRunningAppsChangedListeners.add(listener);
}
}
/** Unregister a listener for running applications changes. */
public void unregisterRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) {
mRunningAppsChangedListener.remove(listener);
synchronized (mGenericWindowPolicyControllerLock) {
mRunningAppsChangedListeners.remove(listener);
}
}
@Override
@@ -283,12 +290,16 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
// Post callback on the main thread so it doesn't block activity launching
mHandler.post(() -> mActivityListener.onDisplayEmpty(mDisplayId));
}
}
mHandler.post(() -> {
for (RunningAppsChangedListener listener : mRunningAppsChangedListener) {
listener.onRunningAppsChanged(runningUids);
if (!mRunningAppsChangedListeners.isEmpty()) {
final ArraySet<RunningAppsChangedListener> listeners =
new ArraySet<>(mRunningAppsChangedListeners);
mHandler.post(() -> {
for (RunningAppsChangedListener listener : listeners) {
listener.onRunningAppsChanged(runningUids);
}
});
}
});
}
}
@Override
@@ -354,4 +365,11 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
}
return true;
}
@VisibleForTesting
int getRunningAppsChangedListenersSizeForTesting() {
synchronized (mGenericWindowPolicyControllerLock) {
return mRunningAppsChangedListeners.size();
}
}
}

View File

@@ -891,4 +891,34 @@ public class VirtualDeviceManagerServiceTest {
verify(mContext).startActivityAsUser(argThat(intent ->
intent.filterEquals(blockedAppIntent)), any(), any());
}
@Test
public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
mDeviceImpl.onVirtualDisplayCreatedLocked(
mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
gwpc.onRunningAppsChanged(uids);
mDeviceImpl.onRunningAppsChanged(uids);
assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(1);
verify(mRunningAppsChangedCallback).accept(new ArraySet<>(Arrays.asList(UID_1, UID_2)));
}
@Test
public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
mDeviceImpl.onVirtualDisplayCreatedLocked(
mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID);
// This call should not throw any exceptions.
gwpc.onRunningAppsChanged(uids);
assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
}
}