Fix java.lang.ArrayIndexOutOfBoundsException in RemoteVolumeGroupController

-Caused by removing and adding preference at the same time
-Make preference operation method synchronized
-Not to update preference by removing and adding. To check session status and update its content to preference
-Post to UI thread to handle the onDeviceListUpdate() callback from framework

Bug: 170049403
Test: make -j50 RunSettingsRoboTests
Merged-In: Ibfc11e1bd99ba2e578b5d9e7dcc9132e372b68dd
Change-Id: Ibfc11e1bd99ba2e578b5d9e7dcc9132e372b68dd
(cherry picked from commit 1268629fda99896bda747a2531c6c3a4480d8cb1)
diff --git a/src/com/android/settings/notification/RemoteVolumeGroupController.java b/src/com/android/settings/notification/RemoteVolumeGroupController.java
index bb62a56..85bb801 100644
--- a/src/com/android/settings/notification/RemoteVolumeGroupController.java
+++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java
@@ -102,43 +102,80 @@
         mLocalMediaManager.stopScan();
     }
 
-    private void refreshPreference() {
-        mPreferenceCategory.removeAll();
+    private synchronized void refreshPreference() {
         if (!isAvailable()) {
             mPreferenceCategory.setVisible(false);
             return;
         }
         final CharSequence castVolume = mContext.getText(R.string.remote_media_volume_option_title);
         mPreferenceCategory.setVisible(true);
-
         for (RoutingSessionInfo info : mRoutingSessionInfos) {
-            if (mPreferenceCategory.findPreference(info.getId()) != null) {
-                continue;
+            final CharSequence appName = Utils.getApplicationLabel(mContext,
+                    info.getClientPackageName());
+            RemoteVolumeSeekBarPreference seekBarPreference = mPreferenceCategory.findPreference(
+                    info.getId());
+            if (seekBarPreference != null) {
+                // Update slider
+                if (seekBarPreference.getProgress() != info.getVolume()) {
+                    seekBarPreference.setProgress(info.getVolume());
+                }
+            } else {
+                // Add slider
+                seekBarPreference = new RemoteVolumeSeekBarPreference(mContext);
+                seekBarPreference.setKey(info.getId());
+                seekBarPreference.setTitle(castVolume);
+                seekBarPreference.setMax(info.getVolumeMax());
+                seekBarPreference.setProgress(info.getVolume());
+                seekBarPreference.setMin(0);
+                seekBarPreference.setOnPreferenceChangeListener(this);
+                seekBarPreference.setIcon(R.drawable.ic_volume_remote);
+                mPreferenceCategory.addPreference(seekBarPreference);
             }
-            final CharSequence appName = Utils.getApplicationLabel(
-                    mContext, info.getClientPackageName());
-            final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title,
-                    appName);
-            // Add slider
-            final RemoteVolumeSeekBarPreference seekBarPreference =
-                    new RemoteVolumeSeekBarPreference(mContext);
-            seekBarPreference.setKey(info.getId());
-            seekBarPreference.setTitle(castVolume);
-            seekBarPreference.setMax(info.getVolumeMax());
-            seekBarPreference.setProgress(info.getVolume());
-            seekBarPreference.setMin(0);
-            seekBarPreference.setOnPreferenceChangeListener(this);
-            seekBarPreference.setIcon(R.drawable.ic_volume_remote);
-            mPreferenceCategory.addPreference(seekBarPreference);
-            // Add output indicator
+
+            Preference switcherPreference = mPreferenceCategory.findPreference(
+                    SWITCHER_PREFIX + info.getId());
             final boolean isMediaOutputDisabled = Utils.isMediaOutputDisabled(
                     mRouterManager, info.getClientPackageName());
-            final Preference preference = new Preference(mContext);
-            preference.setKey(SWITCHER_PREFIX + info.getId());
-            preference.setTitle(isMediaOutputDisabled ? appName : outputTitle);
-            preference.setSummary(info.getName());
-            preference.setEnabled(!isMediaOutputDisabled);
-            mPreferenceCategory.addPreference(preference);
+            final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title,
+                    appName);
+            if (switcherPreference != null) {
+                // Update output indicator
+                switcherPreference.setTitle(isMediaOutputDisabled ? appName : outputTitle);
+                switcherPreference.setSummary(info.getName());
+                switcherPreference.setEnabled(!isMediaOutputDisabled);
+            } else {
+                // Add output indicator
+                switcherPreference = new Preference(mContext);
+                switcherPreference.setKey(SWITCHER_PREFIX + info.getId());
+                switcherPreference.setTitle(isMediaOutputDisabled ? appName : outputTitle);
+                switcherPreference.setSummary(info.getName());
+                switcherPreference.setEnabled(!isMediaOutputDisabled);
+                mPreferenceCategory.addPreference(switcherPreference);
+            }
+        }
+
+        // Check and remove non-active session preference
+        // There is a pair of preferences for each session. First one is a seekBar preference.
+        // The second one shows the session information and provide an entry-point to launch output
+        // switcher. It is unnecessary to go through all preferences. It is fine ignore the second
+        // preference and only to check the seekBar's key value.
+        for (int i = 0; i < mPreferenceCategory.getPreferenceCount(); i = i + 2) {
+            final Preference preference = mPreferenceCategory.getPreference(i);
+            boolean isActive = false;
+            for (RoutingSessionInfo info : mRoutingSessionInfos) {
+                if (TextUtils.equals(preference.getKey(), info.getId())) {
+                    isActive = true;
+                    break;
+                }
+            }
+            if (isActive) {
+                continue;
+            }
+            final Preference switcherPreference = mPreferenceCategory.getPreference(i + 1);
+            if (switcherPreference != null) {
+                mPreferenceCategory.removePreference(preference);
+                mPreferenceCategory.removePreference(switcherPreference);
+            }
         }
     }
 
@@ -181,8 +218,10 @@
             // Preference group is not ready.
             return;
         }
-        initRemoteMediaSession();
-        refreshPreference();
+        ThreadUtils.postOnMainThread(() -> {
+            initRemoteMediaSession();
+            refreshPreference();
+        });
     }
 
     @Override