Merge "Add a content description for the illustration if it is an animation" into main
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 24839ff..6071b06 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6611,8 +6611,12 @@
     <string name="battery_usage_timestamps_content_description"><xliff:g id="from_timestamp">%1$s</xliff:g> to <xliff:g id="to_timestamp">%2$s</xliff:g></string>
     <!-- [CHAR_LIMIT=NONE] The first slot is a week day (e.g. "Monday"); the second slot is a hourly time span (e.g. "6 AM - 8 AM"). -->
     <string name="battery_usage_day_and_hour"><xliff:g id="day">%1$s</xliff:g> <xliff:g id="hour">%2$s</xliff:g></string>
-    <!-- [CHAR_LIMIT=NONE] Accessibility content description for each slot in battery chart view. -->
-    <string name="battery_usage_time_info_and_battery_level"><xliff:g id="time_info" example="Battery usage for Monday 6 AM - 8 AM">%1$s</xliff:g> <xliff:g id="battery_level" example="Battery level percentage from 83% to 59%">%2$s</xliff:g></string>
+    <!-- [CHAR_LIMIT=NONE] Accessibility content description for each slot in battery chart view. Please reuse the words in tc/6732629268310936155 -->
+    <string name="battery_usage_status_time_info_and_battery_level"><xliff:g id="selected_status" example="Selected">%1$s</xliff:g>, <xliff:g id="time_info" example="Battery usage for Monday 6 AM - 8 AM">%2$s</xliff:g> <xliff:g id="battery_level" example="Battery level percentage from 83% to 59%">%3$s</xliff:g></string>
+    <!-- [CHAR_LIMIT=NONE] Accessibility content description for the battery usage slot is selected. -->
+    <string name="battery_chart_slot_status_selected">Selected</string>
+    <!-- [CHAR_LIMIT=NONE] Accessibility content description for the battery usage slot is not selected -->
+    <string name="battery_chart_slot_status_unselected">Unselected</string>
     <!-- [CHAR_LIMIT=NONE] Accessibility content description for battery chart view. -->
     <string name="battery_usage_chart">Battery usage chart</string>
     <!-- [CHAR_LIMIT=NONE] Accessibility content description for daily battery chart view. -->
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
index a1bb84c..ec8d7bc 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
@@ -34,7 +34,10 @@
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Process;
 import android.util.Log;
 import android.view.KeyEvent;
 
@@ -51,24 +54,21 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.VolumeControlProfile;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.ThreadUtils;
 
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+import java.util.Map;
 
 public class AudioStreamMediaService extends Service {
     static final String BROADCAST_ID = "audio_stream_media_service_broadcast_id";
     static final String BROADCAST_TITLE = "audio_stream_media_service_broadcast_title";
     static final String DEVICES = "audio_stream_media_service_devices";
     private static final String TAG = "AudioStreamMediaService";
-    private static final int NOTIFICATION_ID = 1;
+    private static final int NOTIFICATION_ID = R.string.audio_streams_title;
     private static final int BROADCAST_LISTENING_NOW_TEXT = R.string.audio_streams_listening_now;
     private static final int BROADCAST_STREAM_PAUSED_TEXT = R.string.audio_streams_present_now;
     @VisibleForTesting static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action";
@@ -113,17 +113,16 @@
 
     private final MetricsFeatureProvider mMetricsFeatureProvider =
             FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
-    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
-    private final AtomicBoolean mIsMuted = new AtomicBoolean(false);
-    private final AtomicBoolean mIsHysteresis = new AtomicBoolean(false);
+    private final HandlerThread mHandlerThread = new HandlerThread(TAG,
+            Process.THREAD_PRIORITY_BACKGROUND);
+    private boolean mIsMuted = false;
     // Set 25 as default as the volume range from `VolumeControlProfile` is from 0 to 255.
     // If the initial volume from `onDeviceVolumeChanged` is larger than zero (not muted), we will
     // override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
-    private final AtomicInteger mLatestPositiveVolume = new AtomicInteger(25);
-    private final Object mLocalSessionLock = new Object();
+    private int mLatestPositiveVolume = 25;
     private boolean mHysteresisModeFixAvailable;
     private int mBroadcastId;
-    @Nullable private List<BluetoothDevice> mDevices;
+    @Nullable private Map<BluetoothDevice, LocalBluetoothLeBroadcastSourceState> mStateByDevice;
     @Nullable private LocalBluetoothManager mLocalBtManager;
     @Nullable private AudioStreamsHelper mAudioStreamsHelper;
     @Nullable private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
@@ -154,7 +153,6 @@
             Log.w(TAG, "onCreate() : mLeBroadcastAssistant is null!");
             return;
         }
-        mHysteresisModeFixAvailable = BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(this);
 
         mNotificationManager = getSystemService(NotificationManager.class);
         if (mNotificationManager == null) {
@@ -162,7 +160,8 @@
             return;
         }
 
-        mExecutor.execute(
+        mHandlerThread.start();
+        getHandler().post(
                 () -> {
                     if (mLocalBtManager == null
                             || mLeBroadcastAssistant == null
@@ -184,45 +183,49 @@
                     mVolumeControl = mLocalBtManager.getProfileManager().getVolumeControlProfile();
                     if (mVolumeControl != null) {
                         mVolumeControlCallback = new VolumeControlCallback();
-                        mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback);
+                        mVolumeControl.registerCallback(getHandler()::post, mVolumeControlCallback);
                     }
 
                     mBroadcastAssistantCallback = new AssistantCallback();
                     mLeBroadcastAssistant.registerServiceCallBack(
-                            mExecutor, mBroadcastAssistantCallback);
+                            getHandler()::post, mBroadcastAssistantCallback);
+
+                    mHysteresisModeFixAvailable =
+                            BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(this);
                 });
     }
 
+    @VisibleForTesting
+    Handler getHandler() {
+        return mHandlerThread.getThreadHandler();
+    }
+
     @Override
     public void onDestroy() {
         Log.d(TAG, "onDestroy()");
-        super.onDestroy();
-        if (BluetoothUtils.isAudioSharingUIAvailable(this)) {
-            if (mDevices != null) {
-                mDevices.clear();
-                mDevices = null;
-            }
-            synchronized (mLocalSessionLock) {
-                if (mLocalSession != null) {
-                    mLocalSession.release();
-                    mLocalSession = null;
-                }
-            }
-            mExecutor.execute(
-                    () -> {
-                        if (mLocalBtManager != null) {
-                            mLocalBtManager.getEventManager().unregisterCallback(
-                                    mBluetoothCallback);
-                        }
-                        if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) {
-                            mLeBroadcastAssistant.unregisterServiceCallBack(
-                                    mBroadcastAssistantCallback);
-                        }
-                        if (mVolumeControl != null && mVolumeControlCallback != null) {
-                            mVolumeControl.unregisterCallback(mVolumeControlCallback);
-                        }
-                    });
-        }
+        getHandler().post(
+                () -> {
+                    if (mStateByDevice != null) {
+                        mStateByDevice.clear();
+                        mStateByDevice = null;
+                    }
+                    if (mLocalSession != null) {
+                        mLocalSession.release();
+                        mLocalSession = null;
+                    }
+                    if (mLocalBtManager != null) {
+                        mLocalBtManager.getEventManager().unregisterCallback(
+                                mBluetoothCallback);
+                    }
+                    if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) {
+                        mLeBroadcastAssistant.unregisterServiceCallBack(
+                                mBroadcastAssistantCallback);
+                    }
+                    if (mVolumeControl != null && mVolumeControlCallback != null) {
+                        mVolumeControl.unregisterCallback(mVolumeControlCallback);
+                    }
+                });
+        mHandlerThread.quitSafely();
     }
 
     @Override
@@ -233,53 +236,59 @@
             stopSelf();
             return START_NOT_STICKY;
         }
-        mBroadcastId = intent.getIntExtra(BROADCAST_ID, -1);
-        if (mBroadcastId == -1) {
-            Log.w(TAG, "Invalid broadcast ID. Service will not start.");
-            stopSelf();
-            return START_NOT_STICKY;
-        }
-        var extra = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class);
-        if (extra == null || extra.isEmpty()) {
-            Log.w(TAG, "No device. Service will not start.");
-            stopSelf();
-            return START_NOT_STICKY;
-        }
-        mDevices = Collections.synchronizedList(extra);
-        MediaSession.Token token =
-                getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
-        startForeground(NOTIFICATION_ID, buildNotification(token));
+        getHandler().post(() -> {
+            mBroadcastId = intent.getIntExtra(BROADCAST_ID, -1);
+            if (mBroadcastId == -1) {
+                Log.w(TAG, "Invalid broadcast ID. Service will not start.");
+                stopSelf();
+                return;
+            }
+            var devices = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class);
+            if (devices == null || devices.isEmpty()) {
+                Log.w(TAG, "No device. Service will not start.");
+                stopSelf();
+            } else {
+                mStateByDevice = new HashMap<>();
+                devices.forEach(d -> mStateByDevice.put(d, STREAMING));
+                MediaSession.Token token =
+                        getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
+                startForeground(NOTIFICATION_ID, buildNotification(token));
+            }
+        });
         return START_NOT_STICKY;
     }
 
     private MediaSession.Token getOrCreateLocalMediaSession(String title) {
-        synchronized (mLocalSessionLock) {
-            if (mLocalSession != null) {
-                return mLocalSession.getSessionToken();
-            }
-            mLocalSession = new MediaSession(this, TAG);
-            mLocalSession.setMetadata(
-                    new MediaMetadata.Builder()
-                            .putString(MediaMetadata.METADATA_KEY_TITLE, title)
-                            .putLong(MediaMetadata.METADATA_KEY_DURATION, STATIC_PLAYBACK_DURATION)
-                            .build());
-            mLocalSession.setActive(true);
-            mLocalSession.setPlaybackState(getPlaybackState());
-            mMediaSessionCallback = new MediaSessionCallback();
-            mLocalSession.setCallback(mMediaSessionCallback);
+        if (mLocalSession != null) {
             return mLocalSession.getSessionToken();
         }
+        mLocalSession = new MediaSession(this, TAG);
+        mLocalSession.setMetadata(
+                new MediaMetadata.Builder()
+                        .putString(MediaMetadata.METADATA_KEY_TITLE, title)
+                        .putLong(MediaMetadata.METADATA_KEY_DURATION, STATIC_PLAYBACK_DURATION)
+                        .build());
+        mLocalSession.setActive(true);
+        mLocalSession.setPlaybackState(getPlaybackState());
+        mMediaSessionCallback = new MediaSessionCallback();
+        mLocalSession.setCallback(mMediaSessionCallback, getHandler());
+        return mLocalSession.getSessionToken();
     }
 
     private PlaybackState getPlaybackState() {
-        if (mIsHysteresis.get()) {
+        if (isAllDeviceHysteresis()) {
             return mPlayStateHysteresisBuilder.build();
         }
-        return mIsMuted.get() ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
+        return mIsMuted ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
+    }
+
+    private boolean isAllDeviceHysteresis() {
+        return mHysteresisModeFixAvailable && mStateByDevice != null
+                && mStateByDevice.values().stream().allMatch(v -> v == PAUSED);
     }
 
     private String getDeviceName() {
-        if (mDevices == null || mDevices.isEmpty() || mLocalBtManager == null) {
+        if (mStateByDevice == null || mStateByDevice.isEmpty() || mLocalBtManager == null) {
             return DEFAULT_DEVICE_NAME;
         }
 
@@ -288,7 +297,8 @@
             return DEFAULT_DEVICE_NAME;
         }
 
-        CachedBluetoothDevice device = manager.findDevice(mDevices.get(0));
+        CachedBluetoothDevice device = manager.findDevice(
+                mStateByDevice.keySet().iterator().next());
         return device != null ? device.getName() : DEFAULT_DEVICE_NAME;
     }
 
@@ -304,7 +314,7 @@
                         .setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
                         .setStyle(mediaStyle)
                         .setContentText(getString(
-                                mIsHysteresis.get() ? BROADCAST_STREAM_PAUSED_TEXT :
+                                isAllDeviceHysteresis() ? BROADCAST_STREAM_PAUSED_TEXT :
                                         BROADCAST_LISTENING_NOW_TEXT))
                         .setSilent(true);
         return notificationBuilder.build();
@@ -333,7 +343,8 @@
         public void onReceiveStateChanged(
                 BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) {
             super.onReceiveStateChanged(sink, sourceId, state);
-            if (!mHysteresisModeFixAvailable || mDevices == null || !mDevices.contains(sink)) {
+            if (!mHysteresisModeFixAvailable || mStateByDevice == null
+                    || !mStateByDevice.containsKey(sink)) {
                 return;
             }
             var sourceState = LocalBluetoothLeBroadcastAssistant.getLocalSourceState(state);
@@ -343,12 +354,10 @@
             if (!streaming && !paused) {
                 return;
             }
-            // Atomically update mIsHysteresis if its current value is not the current paused state
-            if (mIsHysteresis.compareAndSet(!paused, paused)) {
-                synchronized (mLocalSessionLock) {
-                    if (mLocalSession == null) {
-                        return;
-                    }
+            boolean shouldUpdate = mStateByDevice.get(sink) != sourceState;
+            if (shouldUpdate) {
+                mStateByDevice.put(sink, sourceState);
+                if (mLocalSession != null) {
                     mLocalSession.setPlaybackState(getPlaybackState());
                     if (mNotificationManager != null) {
                         mNotificationManager.notify(
@@ -356,7 +365,7 @@
                                 buildNotification(mLocalSession.getSessionToken())
                         );
                     }
-                    Log.d(TAG, "updating hysteresis mode to : " + paused);
+                    Log.d(TAG, "updating source state to : " + sourceState);
                 }
             }
         }
@@ -374,24 +383,22 @@
         @Override
         public void onDeviceVolumeChanged(
                 @NonNull BluetoothDevice device, @IntRange(from = -255, to = 255) int volume) {
-            if (mDevices == null || mDevices.isEmpty()) {
+            if (mStateByDevice == null || mStateByDevice.isEmpty()) {
                 Log.w(TAG, "active device or device has source is null!");
                 return;
             }
             Log.d(
                     TAG,
                     "onDeviceVolumeChanged() bluetoothDevice : " + device + " volume: " + volume);
-            if (mDevices.contains(device)) {
+            if (mStateByDevice.containsKey(device)) {
                 if (volume == 0) {
-                    mIsMuted.set(true);
+                    mIsMuted = true;
                 } else {
-                    mIsMuted.set(false);
-                    mLatestPositiveVolume.set(volume);
+                    mIsMuted = false;
+                    mLatestPositiveVolume = volume;
                 }
-                synchronized (mLocalSessionLock) {
-                    if (mLocalSession != null) {
-                        mLocalSession.setPlaybackState(getPlaybackState());
-                    }
+                if (mLocalSession != null) {
+                    mLocalSession.setPlaybackState(getPlaybackState());
                 }
             }
         }
@@ -400,10 +407,12 @@
     private class BtCallback implements BluetoothCallback {
         @Override
         public void onBluetoothStateChanged(int bluetoothState) {
-            if (BluetoothAdapter.STATE_OFF == bluetoothState) {
-                Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
-                stopSelf();
-            }
+            getHandler().post(() -> {
+                if (BluetoothAdapter.STATE_OFF == bluetoothState) {
+                    Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
+                    stopSelf();
+                }
+            });
         }
 
         @Override
@@ -411,24 +420,17 @@
                 @NonNull CachedBluetoothDevice cachedDevice,
                 @ConnectionState int state,
                 int bluetoothProfile) {
-            if (state == BluetoothAdapter.STATE_DISCONNECTED
-                    && bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
-                    && mDevices != null) {
-                mDevices.remove(cachedDevice.getDevice());
-                cachedDevice
-                        .getMemberDevice()
-                        .forEach(
-                                m -> {
-                                    // Check nullability to pass NullAway check
-                                    if (mDevices != null) {
-                                        mDevices.remove(m.getDevice());
-                                    }
-                                });
-            }
-            if (mDevices == null || mDevices.isEmpty()) {
-                Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
-                stopSelf();
-            }
+            getHandler().post(() -> {
+                if (state == BluetoothAdapter.STATE_DISCONNECTED
+                        && bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+                        && mStateByDevice != null) {
+                    mStateByDevice.remove(cachedDevice.getDevice());
+                }
+                if (mStateByDevice == null || mStateByDevice.isEmpty()) {
+                    Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
+                    stopSelf();
+                }
+            });
         }
     }
 
@@ -454,10 +456,8 @@
         @Override
         public void onSeekTo(long pos) {
             Log.d(TAG, "onSeekTo: " + pos);
-            synchronized (mLocalSessionLock) {
-                if (mLocalSession != null) {
-                    mLocalSession.setPlaybackState(getPlaybackState());
-                }
+            if (mLocalSession != null) {
+                mLocalSession.setPlaybackState(getPlaybackState());
             }
         }
 
@@ -484,28 +484,26 @@
     }
 
     private void handleOnPlay() {
-        if (mDevices == null || mDevices.isEmpty()) {
+        if (mStateByDevice == null || mStateByDevice.isEmpty()) {
             Log.w(TAG, "active device or device has source is null!");
             return;
         }
-        Log.d(
-                TAG,
-                "onPlay() setting volume for device : "
-                        + mDevices.getFirst()
-                        + " volume: "
-                        + mLatestPositiveVolume.get());
-        setDeviceVolume(mDevices.getFirst(), mLatestPositiveVolume.get());
+        mStateByDevice.keySet().forEach(device -> {
+            Log.d(TAG, "onPlay() setting volume for device : " + device + " volume: "
+                    + mLatestPositiveVolume);
+            setDeviceVolume(device, mLatestPositiveVolume);
+        });
     }
 
     private void handleOnPause() {
-        if (mDevices == null || mDevices.isEmpty()) {
+        if (mStateByDevice == null || mStateByDevice.isEmpty()) {
             Log.w(TAG, "active device or device has source is null!");
             return;
         }
-        Log.d(
-                TAG,
-                "onPause() setting volume for device : " + mDevices.getFirst() + " volume: " + 0);
-        setDeviceVolume(mDevices.getFirst(), /* volume= */ 0);
+        mStateByDevice.keySet().forEach(device -> {
+            Log.d(TAG, "onPause() setting volume for device : " + device + " volume: " + 0);
+            setDeviceVolume(device, /* volume= */ 0);
+        });
     }
 
     private void setDeviceVolume(BluetoothDevice device, int volume) {
@@ -514,7 +512,7 @@
                 ThreadUtils.postOnBackgroundThread(
                         () -> {
                             if (mVolumeControl != null) {
-                                mVolumeControl.setDeviceVolume(device, volume, true);
+                                mVolumeControl.setDeviceVolume(device, volume, false);
                                 mMetricsFeatureProvider.action(
                                         getApplicationContext(), event, volume == 0 ? 1 : 0);
                             }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index a248bdf..2681067 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.fuelgauge.batteryusage;
 
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_ALL;
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_INVALID;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.settings.SettingsEnums;
@@ -82,10 +85,10 @@
     @VisibleForTesting TextView mChartSummaryTextView;
     @VisibleForTesting BatteryChartView mDailyChartView;
     @VisibleForTesting BatteryChartView mHourlyChartView;
-    @VisibleForTesting int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
-    @VisibleForTesting int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
-    @VisibleForTesting int mDailyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
-    @VisibleForTesting int mHourlyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
+    @VisibleForTesting int mDailyChartIndex = SELECTED_INDEX_ALL;
+    @VisibleForTesting int mHourlyChartIndex = SELECTED_INDEX_ALL;
+    @VisibleForTesting int mDailyHighlightSlotIndex = SELECTED_INDEX_INVALID;
+    @VisibleForTesting int mHourlyHighlightSlotIndex = SELECTED_INDEX_INVALID;
 
     private boolean mIs24HourFormat;
     private View mBatteryChartViewGroup;
@@ -198,8 +201,8 @@
                 getTotalHours(batteryLevelData));
 
         if (batteryLevelData == null) {
-            mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
-            mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+            mDailyChartIndex = SELECTED_INDEX_ALL;
+            mHourlyChartIndex = SELECTED_INDEX_ALL;
             mDailyViewModel = null;
             mHourlyViewModels = null;
             refreshUi();
@@ -226,9 +229,9 @@
     }
 
     boolean isHighlightSlotFocused() {
-        return (mDailyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID
+        return (mDailyHighlightSlotIndex != SELECTED_INDEX_INVALID
                 && mDailyHighlightSlotIndex == mDailyChartIndex
-                && mHourlyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID
+                && mHourlyHighlightSlotIndex != SELECTED_INDEX_INVALID
                 && mHourlyHighlightSlotIndex == mHourlyChartIndex);
     }
 
@@ -242,8 +245,8 @@
     }
 
     void selectHighlightSlotIndex() {
-        if (mDailyHighlightSlotIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
-                || mHourlyHighlightSlotIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID) {
+        if (mDailyHighlightSlotIndex == SELECTED_INDEX_INVALID
+                || mHourlyHighlightSlotIndex == SELECTED_INDEX_INVALID) {
             return;
         }
         if (mDailyHighlightSlotIndex == mDailyChartIndex
@@ -258,8 +261,11 @@
                         "onDailyChartSelect:%d, onHourlyChartSelect:%d",
                         mDailyChartIndex, mHourlyChartIndex));
         refreshUi();
+        // The highlight slot must be selected.
         mHandler.post(
-                () -> mDailyChartView.setAccessibilityPaneTitle(getAccessibilityAnnounceMessage()));
+                () ->
+                        mDailyChartView.setAccessibilityPaneTitle(
+                                getAccessibilityAnnounceMessage(/* isSlotSelected= */ true)));
         if (mOnSelectedIndexUpdatedListener != null) {
             mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
         }
@@ -295,15 +301,16 @@
                     }
                     Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex);
                     mDailyChartIndex = trapezoidIndex;
-                    mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+                    mHourlyChartIndex = SELECTED_INDEX_ALL;
                     refreshUi();
                     mHandler.post(
                             () ->
                                     mDailyChartView.setAccessibilityPaneTitle(
-                                            getAccessibilityAnnounceMessage()));
+                                            getAccessibilityAnnounceMessage(
+                                                    mDailyChartIndex != SELECTED_INDEX_ALL)));
                     mMetricsFeatureProvider.action(
                             mPrefContext,
-                            trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
+                            trapezoidIndex == SELECTED_INDEX_ALL
                                     ? SettingsEnums.ACTION_BATTERY_USAGE_DAILY_SHOW_ALL
                                     : SettingsEnums.ACTION_BATTERY_USAGE_DAILY_TIME_SLOT,
                             mDailyChartIndex);
@@ -314,7 +321,7 @@
         mHourlyChartView = hourlyChartView;
         mHourlyChartView.setOnSelectListener(
                 trapezoidIndex -> {
-                    if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+                    if (mDailyChartIndex == SELECTED_INDEX_ALL) {
                         // This will happen when a daily slot and an hour slot are clicked together.
                         return;
                     }
@@ -327,10 +334,11 @@
                     mHandler.post(
                             () ->
                                     mHourlyChartView.setAccessibilityPaneTitle(
-                                            getAccessibilityAnnounceMessage()));
+                                            getAccessibilityAnnounceMessage(
+                                                    mHourlyChartIndex != SELECTED_INDEX_ALL)));
                     mMetricsFeatureProvider.action(
                             mPrefContext,
-                            trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
+                            trapezoidIndex == SELECTED_INDEX_ALL
                                     ? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
                                     : SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT,
                             mHourlyChartIndex);
@@ -378,27 +386,27 @@
         } else {
             mDailyChartView.setVisibility(View.VISIBLE);
             if (mDailyChartIndex >= mDailyViewModel.size()) {
-                mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+                mDailyChartIndex = SELECTED_INDEX_ALL;
             }
             mDailyViewModel.setSelectedIndex(mDailyChartIndex);
             mDailyViewModel.setHighlightSlotIndex(mDailyHighlightSlotIndex);
             mDailyChartView.setViewModel(mDailyViewModel);
         }
 
-        if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+        if (mDailyChartIndex == SELECTED_INDEX_ALL) {
             // Multiple days are selected, hide the hourly chart view.
             animateBatteryHourlyChartView(/* visible= */ false);
         } else {
             animateBatteryHourlyChartView(/* visible= */ true);
             final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex);
             if (mHourlyChartIndex >= hourlyViewModel.size()) {
-                mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+                mHourlyChartIndex = SELECTED_INDEX_ALL;
             }
             hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
             hourlyViewModel.setHighlightSlotIndex(
                     (mDailyChartIndex == mDailyHighlightSlotIndex)
                             ? mHourlyHighlightSlotIndex
-                            : BatteryChartViewModel.SELECTED_INDEX_INVALID);
+                            : SELECTED_INDEX_INVALID);
             mHourlyChartView.setViewModel(hourlyViewModel);
         }
     }
@@ -416,7 +424,7 @@
                 isAccessibilityText
                         ? mDailyViewModel.getContentDescription(mDailyChartIndex)
                         : mDailyViewModel.getFullText(mDailyChartIndex);
-        if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+        if (mHourlyChartIndex == SELECTED_INDEX_ALL) {
             return selectedDayText;
         }
 
@@ -441,15 +449,19 @@
             return "";
         }
 
-        if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
-                || mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+        if (mDailyChartIndex == SELECTED_INDEX_ALL || mHourlyChartIndex == SELECTED_INDEX_ALL) {
             return mDailyViewModel.getSlotBatteryLevelText(mDailyChartIndex);
         }
 
         return mHourlyViewModels.get(mDailyChartIndex).getSlotBatteryLevelText(mHourlyChartIndex);
     }
 
-    private String getAccessibilityAnnounceMessage() {
+    private String getAccessibilityAnnounceMessage(final boolean isSlotSelected) {
+        final String selectedInformation =
+                mPrefContext.getString(
+                        isSlotSelected
+                                ? R.string.battery_chart_slot_status_selected
+                                : R.string.battery_chart_slot_status_unselected);
         final String slotInformation = getSlotInformation(/* isAccessibilityText= */ true);
         final String slotInformationMessage =
                 slotInformation == null
@@ -460,7 +472,8 @@
         final String batteryLevelPercentageMessage = getBatteryLevelPercentageInfo();
 
         return mPrefContext.getString(
-                R.string.battery_usage_time_info_and_battery_level,
+                R.string.battery_usage_status_time_info_and_battery_level,
+                selectedInformation,
                 slotInformationMessage,
                 batteryLevelPercentageMessage);
     }
@@ -533,9 +546,8 @@
     }
 
     private boolean isAllSelected() {
-        return (isBatteryLevelDataInOneDay()
-                        || mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL)
-                && mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
+        return (isBatteryLevelDataInOneDay() || mDailyChartIndex == SELECTED_INDEX_ALL)
+                && mHourlyChartIndex == SELECTED_INDEX_ALL;
     }
 
     @VisibleForTesting
@@ -571,9 +583,7 @@
             return null;
         }
         BatteryDiffData allBatteryDiffData =
-                batteryUsageData
-                        .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
-                        .get(BatteryChartViewModel.SELECTED_INDEX_ALL);
+                batteryUsageData.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL);
         return allBatteryDiffData == null ? null : allBatteryDiffData.getAppDiffEntryList();
     }
 
@@ -613,12 +623,9 @@
 
         @Override
         public String generateSlotBatteryLevelText(List<Integer> levels, int index) {
-            final int fromBatteryLevelIndex =
-                    index == BatteryChartViewModel.SELECTED_INDEX_ALL ? 0 : index;
+            final int fromBatteryLevelIndex = index == SELECTED_INDEX_ALL ? 0 : index;
             final int toBatteryLevelIndex =
-                    index == BatteryChartViewModel.SELECTED_INDEX_ALL
-                            ? levels.size() - 1
-                            : index + 1;
+                    index == SELECTED_INDEX_ALL ? levels.size() - 1 : index + 1;
             return mPrefContext.getString(
                     R.string.battery_level_percentage,
                     generateBatteryLevelText(levels.get(fromBatteryLevelIndex)),
@@ -687,9 +694,9 @@
             return index == timestamps.size() - 1
                     ? generateText(timestamps, index)
                     : mContext.getString(
-                    R.string.battery_usage_timestamps_content_description,
-                    generateText(timestamps, index),
-                    generateText(timestamps, index + 1));
+                            R.string.battery_usage_timestamps_content_description,
+                            generateText(timestamps, index),
+                            generateText(timestamps, index + 1));
         }
 
         HourlyChartLabelTextGenerator updateSpecialCaseContext(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
index eafccdb..393d751 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
@@ -17,6 +17,8 @@
 
 import static com.android.settings.Utils.formatPercentage;
 import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS;
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_ALL;
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_INVALID;
 import static com.android.settingslib.fuelgauge.BatteryStatus.BATTERY_LEVEL_UNKNOWN;
 
 import static java.lang.Math.abs;
@@ -81,7 +83,7 @@
             getContext().getResources().getConfiguration().getLayoutDirection();
 
     private BatteryChartViewModel mViewModel;
-    private int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
+    private int mHoveredIndex = SELECTED_INDEX_INVALID;
     private int mDividerWidth;
     private int mDividerHeight;
     private float mTrapezoidVOffset;
@@ -245,9 +247,9 @@
                 // sent here.
                 return true;
             case MotionEvent.ACTION_HOVER_EXIT:
-                if (mHoveredIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID) {
+                if (mHoveredIndex != SELECTED_INDEX_INVALID) {
                     sendAccessibilityEventForHover(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
-                    mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
+                    mHoveredIndex = SELECTED_INDEX_INVALID; // reset
                     invalidate();
                 }
                 // Ignore the super.onHoverEvent() because the hovered trapezoid has already been
@@ -262,7 +264,7 @@
     public void onHoverChanged(boolean hovered) {
         super.onHoverChanged(hovered);
         if (!hovered) {
-            mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
+            mHoveredIndex = SELECTED_INDEX_INVALID; // reset
             invalidate();
         }
     }
@@ -295,9 +297,7 @@
         if (mOnSelectListener != null) {
             // Selects all if users click the same trapezoid item two times.
             mOnSelectListener.onSelect(
-                    index == mViewModel.selectedIndex()
-                            ? BatteryChartViewModel.SELECTED_INDEX_ALL
-                            : index);
+                    index == mViewModel.selectedIndex() ? SELECTED_INDEX_ALL : index);
         }
         view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
     }
@@ -332,8 +332,8 @@
         setBackgroundColor(Color.TRANSPARENT);
         mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
         mTrapezoidColor = Utils.getDisabled(context, mTrapezoidSolidColor);
-        mTrapezoidHoverColor = context.getColor(
-                com.android.internal.R.color.materialColorSecondaryContainer);
+        mTrapezoidHoverColor =
+                context.getColor(com.android.internal.R.color.materialColorSecondaryContainer);
         // Initializes the divider line paint.
         final Resources resources = getContext().getResources();
         mDividerWidth = resources.getDimensionPixelSize(R.dimen.chartview_divider_width);
@@ -623,8 +623,7 @@
             // Configures the trapezoid paint color.
             final int trapezoidColor =
                     (mViewModel.selectedIndex() == index
-                                    || mViewModel.selectedIndex()
-                                            == BatteryChartViewModel.SELECTED_INDEX_ALL)
+                                    || mViewModel.selectedIndex() == SELECTED_INDEX_ALL)
                             ? mTrapezoidSolidColor
                             : mTrapezoidColor;
             final boolean isHoverState =
@@ -659,9 +658,7 @@
     }
 
     private boolean isHighlightSlotValid() {
-        return mViewModel != null
-                && mViewModel.getHighlightSlotIndex()
-                        != BatteryChartViewModel.SELECTED_INDEX_INVALID;
+        return mViewModel != null && mViewModel.getHighlightSlotIndex() != SELECTED_INDEX_INVALID;
     }
 
     private void drawTransomLine(Canvas canvas) {
@@ -715,7 +712,7 @@
     // Searches the corresponding trapezoid index from x location.
     private int getTrapezoidIndex(float x) {
         if (mTrapezoidSlots == null) {
-            return BatteryChartViewModel.SELECTED_INDEX_INVALID;
+            return SELECTED_INDEX_INVALID;
         }
         for (int index = 0; index < mTrapezoidSlots.length; index++) {
             final TrapezoidSlot slot = mTrapezoidSlots[index];
@@ -723,7 +720,7 @@
                 return index;
             }
         }
-        return BatteryChartViewModel.SELECTED_INDEX_INVALID;
+        return SELECTED_INDEX_INVALID;
     }
 
     private void initializeAxisLabelsBounds() {
@@ -796,7 +793,11 @@
             childInfo.setText(slotTimeInfo);
             childInfo.setContentDescription(
                     mContext.getString(
-                            R.string.battery_usage_time_info_and_battery_level,
+                            R.string.battery_usage_status_time_info_and_battery_level,
+                            mContext.getString(
+                                    mViewModel.selectedIndex() == virtualViewId
+                                            ? R.string.battery_chart_slot_status_selected
+                                            : R.string.battery_chart_slot_status_unselected),
                             slotTimeInfo,
                             batteryLevelInfo));
             childInfo.setAccessibilityFocused(virtualViewId == mAccessibilityFocusNodeViewId);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
index 6a72c7d..d318e06 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
@@ -34,6 +34,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Looper;
 import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.Pair;
@@ -175,6 +176,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
     public void onClicked_deviceNotBonded_shouldLogBluetoothPairEvent() {
         when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
         when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
@@ -192,6 +194,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
     public void onClicked_deviceNotBonded_shouldLogBluetoothPairEventAndPairWithoutNameEvent() {
         when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
         when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
index a0e971b..c82c978 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
@@ -28,7 +28,6 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -52,7 +51,9 @@
 import android.media.session.ISessionController;
 import android.media.session.MediaSessionManager;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.DisplayMetrics;
@@ -81,14 +82,12 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.android.util.concurrent.InlineExecutorService;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 
 @RunWith(RobolectricTestRunner.class)
 @Config(
@@ -122,6 +121,7 @@
     @Mock private PackageManager mPackageManager;
     @Mock private DisplayMetrics mDisplayMetrics;
     @Mock private Context mContext;
+    @Mock private Handler mHandler;
     private FakeFeatureFactory mFeatureFactory;
     private AudioStreamMediaService mAudioStreamMediaService;
 
@@ -145,11 +145,18 @@
         when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME);
         when(mLocalBluetoothProfileManager.getVolumeControlProfile())
                 .thenReturn(mVolumeControlProfile);
-
-        mAudioStreamMediaService = spy(new AudioStreamMediaService());
+        when(mHandler.post(any(Runnable.class))).thenAnswer(invocation -> {
+            ((Runnable) invocation.getArgument(0)).run();
+            return null;
+        });
+        when(mHandler.getLooper()).thenReturn(Looper.getMainLooper());
+        mAudioStreamMediaService = spy(new AudioStreamMediaService() {
+            @Override
+            Handler getHandler() {
+                return mHandler;
+            }
+        });
         ReflectionHelpers.setField(mAudioStreamMediaService, "mBase", mContext);
-        ReflectionHelpers.setField(
-                mAudioStreamMediaService, "mExecutor", new InlineExecutorService());
         when(mAudioStreamMediaService.getSystemService(anyString()))
                 .thenReturn(mMediaSessionManager);
         when(mMediaSessionManager.createSession(any(), anyString(), any())).thenReturn(mISession);
@@ -392,31 +399,6 @@
     }
 
     @Test
-    public void bluetoothCallback_onMemberDeviceDisconnect_stopSelf() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(mock(BluetoothDevice.class));
-        CachedBluetoothDevice member = mock(CachedBluetoothDevice.class);
-        when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(Set.of(member));
-        when(member.getDevice()).thenReturn(mDevice);
-        var devices = new ArrayList<BluetoothDevice>();
-        devices.add(mDevice);
-
-        Intent intent = new Intent();
-        intent.putExtra(BROADCAST_ID, 1);
-        intent.putParcelableArrayListExtra(DEVICES, devices);
-
-        mAudioStreamMediaService.onCreate();
-        assertThat(mAudioStreamMediaService.mBluetoothCallback).isNotNull();
-        mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
-        mAudioStreamMediaService.mBluetoothCallback.onProfileConnectionStateChanged(
-                mCachedBluetoothDevice,
-                BluetoothAdapter.STATE_DISCONNECTED,
-                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
-
-        verify(mAudioStreamMediaService).stopSelf();
-    }
-
-    @Test
     public void mediaSessionCallback_onPause_setVolume() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);