From 1268629fda99896bda747a2531c6c3a4480d8cb1 Mon Sep 17 00:00:00 2001 From: timhypeng Date: Wed, 7 Oct 2020 15:19:53 +0800 Subject: [PATCH] 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 Change-Id: Ibfc11e1bd99ba2e578b5d9e7dcc9132e372b68dd --- .../RemoteVolumeGroupController.java | 97 +++++++++++++------ 1 file changed, 68 insertions(+), 29 deletions(-) diff --git a/src/com/android/settings/notification/RemoteVolumeGroupController.java b/src/com/android/settings/notification/RemoteVolumeGroupController.java index 320862c6075..ceee7488281 100644 --- a/src/com/android/settings/notification/RemoteVolumeGroupController.java +++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java @@ -102,43 +102,80 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem 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 = mLocalMediaManager.shouldDisableMediaOutput( 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 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem // Preference group is not ready. return; } - initRemoteMediaSession(); - refreshPreference(); + ThreadUtils.postOnMainThread(() -> { + initRemoteMediaSession(); + refreshPreference(); + }); } @Override