Merge cherrypicks of ['googleplex-android-review.googlesource.com/26909021', 'googleplex-android-review.googlesource.com/26968636', 'googleplex-android-review.googlesource.com/26970063'] into 24Q2-release.

Change-Id: Ica24dd1bd4de67fefdb5602bfa2ea3f0a1f96755
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a4bc235..d6c4c8b 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1645,6 +1645,8 @@
     <string name="accessibility_phone_two_bars">Phone two bars.</string>
     <!-- Content description of the phone signal when it is three bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_phone_three_bars">Phone three bars.</string>
+    <!-- Content description of the phone signal when it is four bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_phone_four_bars">Phone four bars.</string>
     <!-- Content description of the phone signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_phone_signal_full">Phone signal full.</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
index ce466df..9073d28 100644
--- a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
@@ -33,6 +33,59 @@
         R.string.accessibility_phone_signal_full
     };
 
+    /**
+     * @param level int in range [0-4] that describes the signal level
+     * @return the appropriate content description for that signal strength, or 0 if the param is
+     *         invalid
+     */
+    public static int getDescriptionForLevel(int level) {
+        if (level > 4 || level < 0) {
+            return 0;
+        }
+
+        return PHONE_SIGNAL_STRENGTH[level];
+    }
+
+    public static final int[] PHONE_SIGNAL_STRENGTH_INFLATED = {
+            PHONE_SIGNAL_STRENGTH_NONE,
+            R.string.accessibility_phone_one_bar,
+            R.string.accessibility_phone_two_bars,
+            R.string.accessibility_phone_three_bars,
+            R.string.accessibility_phone_four_bars,
+            R.string.accessibility_phone_signal_full
+    };
+
+    /**
+     * @param level int in range [0-5] that describes the inflated signal level
+     * @return the appropriate content description for that signal strength, or 0 if the param is
+     *         invalid
+     */
+    public static int getDescriptionForInflatedLevel(int level) {
+        if (level > 5 || level < 0) {
+            return 0;
+        }
+
+        return PHONE_SIGNAL_STRENGTH_INFLATED[level];
+    }
+
+    /**
+     * @param level int in range [0-5] that describes the inflated signal level
+     * @param numberOfLevels one of (4, 5) that describes the default number of levels, or the
+     *                       inflated number of levels. The level param should be relative to the
+     *                       number of levels. This won't do any inflation.
+     * @return the appropriate content description for that signal strength, or 0 if the param is
+     *         invalid
+     */
+    public static int getDescriptionForLevel(int level, int numberOfLevels) {
+        if (numberOfLevels == 5) {
+            return getDescriptionForLevel(level);
+        } else if (numberOfLevels == 6) {
+            return getDescriptionForInflatedLevel(level);
+        } else {
+            return 0;
+        }
+    }
+
     public static final int[] DATA_CONNECTION_STRENGTH = {
         R.string.accessibility_no_data,
         R.string.accessibility_data_one_bar,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index ba7d410..acb48a9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -18,18 +18,12 @@
 
 import android.content.Context
 import android.content.res.Configuration
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
 import android.view.View
 import android.view.ViewGroup
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
 import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaHostState
@@ -43,7 +37,6 @@
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.println
-import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.withIncreasedIndent
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -61,8 +54,6 @@
     private val bypassController: KeyguardBypassController,
     private val statusBarStateController: SysuiStatusBarStateController,
     private val context: Context,
-    private val secureSettings: SecureSettings,
-    @Main private val handler: Handler,
     configurationController: ConfigurationController,
     private val splitShadeStateController: SplitShadeStateController,
     private val logger: KeyguardMediaControllerLogger,
@@ -91,26 +82,6 @@
             }
         )
 
-        val settingsObserver: ContentObserver =
-            object : ContentObserver(handler) {
-                override fun onChange(selfChange: Boolean, uri: Uri?) {
-                    if (uri == lockScreenMediaPlayerUri) {
-                        allowMediaPlayerOnLockScreen =
-                            secureSettings.getBoolForUser(
-                                Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
-                                true,
-                                UserHandle.USER_CURRENT
-                            )
-                        refreshMediaPosition(reason = "allowMediaPlayerOnLockScreen changed")
-                    }
-                }
-            }
-        secureSettings.registerContentObserverForUser(
-            Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
-            settingsObserver,
-            UserHandle.USER_ALL
-        )
-
         // First let's set the desired state that we want for this host
         mediaHost.expansion = MediaHostState.EXPANDED
         mediaHost.showsOnlyActiveMedia = true
@@ -156,16 +127,6 @@
         private set
     private var splitShadeContainer: ViewGroup? = null
 
-    /** Track the media player setting status on lock screen. */
-    private var allowMediaPlayerOnLockScreen: Boolean =
-        secureSettings.getBoolForUser(
-            Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
-            true,
-            UserHandle.USER_CURRENT
-        )
-    private val lockScreenMediaPlayerUri =
-        secureSettings.getUriFor(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN)
-
     /**
      * Attaches media container in single pane mode, situated at the top of the notifications list
      */
@@ -229,14 +190,12 @@
         // mediaHost.visible required for proper animations handling
         val isMediaHostVisible = mediaHost.visible
         val isBypassNotEnabled = !bypassController.bypassEnabled
-        val currentAllowMediaPlayerOnLockScreen = allowMediaPlayerOnLockScreen
         val useSplitShade = useSplitShade
         val shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade()
         visible =
             isMediaHostVisible &&
                 isBypassNotEnabled &&
                 keyguardOrUserSwitcher &&
-                currentAllowMediaPlayerOnLockScreen &&
                 shouldBeVisibleForSplitShade
         logger.logRefreshMediaPosition(
             reason = reason,
@@ -246,7 +205,6 @@
             keyguardOrUserSwitcher = keyguardOrUserSwitcher,
             mediaHostVisible = isMediaHostVisible,
             bypassNotEnabled = isBypassNotEnabled,
-            currentAllowMediaPlayerOnLockScreen = currentAllowMediaPlayerOnLockScreen,
             shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade,
         )
         val currActiveContainer = activeContainer
@@ -321,7 +279,6 @@
                 println("Self", this@KeyguardMediaController)
                 println("visible", visible)
                 println("useSplitShade", useSplitShade)
-                println("allowMediaPlayerOnLockScreen", allowMediaPlayerOnLockScreen)
                 println("bypassController.bypassEnabled", bypassController.bypassEnabled)
                 println("isDozeWakeUpAnimationWaiting", isDozeWakeUpAnimationWaiting)
                 println("singlePaneContainer", singlePaneContainer)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
index c0d9dc2..4d1827e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
@@ -36,7 +36,6 @@
         keyguardOrUserSwitcher: Boolean,
         mediaHostVisible: Boolean,
         bypassNotEnabled: Boolean,
-        currentAllowMediaPlayerOnLockScreen: Boolean,
         shouldBeVisibleForSplitShade: Boolean,
     ) {
         logBuffer.log(
@@ -50,8 +49,7 @@
                 bool3 = keyguardOrUserSwitcher
                 bool4 = mediaHostVisible
                 int2 = if (bypassNotEnabled) 1 else 0
-                str2 = currentAllowMediaPlayerOnLockScreen.toString()
-                str3 = shouldBeVisibleForSplitShade.toString()
+                str2 = shouldBeVisibleForSplitShade.toString()
             },
             {
                 "refreshMediaPosition(reason=$str1, " +
@@ -60,8 +58,7 @@
                     "keyguardOrUserSwitcher=$bool3, " +
                     "mediaHostVisible=$bool4, " +
                     "bypassNotEnabled=${int2 == 1}, " +
-                    "currentAllowMediaPlayerOnLockScreen=$str2, " +
-                    "shouldBeVisibleForSplitShade=$str3)"
+                    "shouldBeVisibleForSplitShade=$str2)"
             }
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index dbd71f3..37a035c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -115,7 +115,7 @@
 ) {
 
     /** Track the media player setting status on lock screen. */
-    private var allowMediaPlayerOnLockScreen: Boolean = true
+    private var allowMediaPlayerOnLockScreen: Boolean = getMediaLockScreenSetting()
     private val lockScreenMediaPlayerUri =
         secureSettings.getUriFor(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN)
 
@@ -485,6 +485,7 @@
                         mediaCarouselController.logSmartspaceImpression(qsExpanded)
                     }
                     updateUserVisibility()
+                    mediaCarouselController.updateHostVisibility()
                 }
 
                 override fun onDozeAmountChanged(linear: Float, eased: Float) {
@@ -569,12 +570,8 @@
             object : ContentObserver(handler) {
                 override fun onChange(selfChange: Boolean, uri: Uri?) {
                     if (uri == lockScreenMediaPlayerUri) {
-                        allowMediaPlayerOnLockScreen =
-                            secureSettings.getBoolForUser(
-                                Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
-                                true,
-                                UserHandle.USER_CURRENT
-                            )
+                        allowMediaPlayerOnLockScreen = getMediaLockScreenSetting()
+                        mediaCarouselController.updateHostVisibility()
                     }
                 }
             }
@@ -595,6 +592,14 @@
         }
     }
 
+    private fun getMediaLockScreenSetting(): Boolean {
+        return secureSettings.getBoolForUser(
+            Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
+            true,
+            UserHandle.USER_CURRENT
+        )
+    }
+
     private fun updateConfiguration() {
         distanceForFullShadeTransition =
             context.resources.getDimensionPixelSize(
@@ -634,6 +639,13 @@
         mediaCarouselController.closeGuts()
     }
 
+    /** Return true if the carousel should be hidden because lockscreen is currently visible */
+    fun isLockedAndHidden(): Boolean {
+        return !allowMediaPlayerOnLockScreen &&
+            (statusbarState == StatusBarState.SHADE_LOCKED ||
+                statusbarState == StatusBarState.KEYGUARD)
+    }
+
     private fun createUniqueObjectHost(): UniqueObjectHostView {
         val viewHost = UniqueObjectHostView(context)
         viewHost.addOnAttachStateChangeListener(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index d92168b..04f9a9d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -202,7 +202,9 @@
      */
     fun updateViewVisibility() {
         state.visible =
-            if (showsOnlyActiveMedia) {
+            if (mediaHierarchyManager.isLockedAndHidden()) {
+                false
+            } else if (showsOnlyActiveMedia) {
                 mediaDataManager.hasActiveMediaOrRecommendation()
             } else {
                 mediaDataManager.hasAnyMediaOrRecommendation()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 8f3b0e7..2278597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -44,6 +44,9 @@
     /** The carrierId for this connection. See [TelephonyManager.getSimCarrierId] */
     val carrierId: StateFlow<Int>
 
+    /** Reflects the value from the carrier config INFLATE_SIGNAL_STRENGTH for this connection */
+    val inflateSignalStrength: StateFlow<Boolean>
+
     /**
      * The table log buffer created for this connection. Will have the name "MobileConnectionLog
      * [subId]"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index 3cb138bd..83d5f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -43,6 +43,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -67,6 +68,17 @@
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _carrierId.value)
 
+    private val _inflateSignalStrength: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    override val inflateSignalStrength =
+        _inflateSignalStrength
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                columnName = "inflate",
+                _inflateSignalStrength.value
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), _inflateSignalStrength.value)
+
     private val _isEmergencyOnly = MutableStateFlow(false)
     override val isEmergencyOnly =
         _isEmergencyOnly
@@ -191,7 +203,16 @@
             .logDiffsForTable(tableLogBuffer, columnPrefix = "", _resolvedNetworkType.value)
             .stateIn(scope, SharingStarted.WhileSubscribed(), _resolvedNetworkType.value)
 
-    override val numberOfLevels = MutableStateFlow(MobileConnectionRepository.DEFAULT_NUM_LEVELS)
+    override val numberOfLevels =
+        _inflateSignalStrength
+            .map { shouldInflate ->
+                if (shouldInflate) {
+                    DEFAULT_NUM_LEVELS + 1
+                } else {
+                    DEFAULT_NUM_LEVELS
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
 
     override val dataEnabled = MutableStateFlow(true)
 
@@ -224,8 +245,7 @@
 
         _carrierId.value = event.carrierId ?: INVALID_SUBSCRIPTION_ID
 
-        numberOfLevels.value =
-            if (event.inflateStrength) DEFAULT_NUM_LEVELS + 1 else DEFAULT_NUM_LEVELS
+        _inflateSignalStrength.value = event.inflateStrength
 
         cdmaRoaming.value = event.roaming
         _isRoaming.value = event.roaming
@@ -256,7 +276,6 @@
         carrierName.value = NetworkNameModel.SubscriptionDerived(CARRIER_MERGED_NAME)
         // TODO(b/276943904): is carrierId a thing with carrier merged networks?
         _carrierId.value = INVALID_SUBSCRIPTION_ID
-        numberOfLevels.value = event.numberOfLevels
         cdmaRoaming.value = false
         _primaryLevel.value = event.level
         _cdmaLevel.value = event.level
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index f8858c5..a532e62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -165,6 +165,7 @@
 
     override val isRoaming = MutableStateFlow(false).asStateFlow()
     override val carrierId = MutableStateFlow(INVALID_SUBSCRIPTION_ID).asStateFlow()
+    override val inflateSignalStrength = MutableStateFlow(false).asStateFlow()
     override val isEmergencyOnly = MutableStateFlow(false).asStateFlow()
     override val operatorAlphaShort = MutableStateFlow(null).asStateFlow()
     override val isInService = MutableStateFlow(true).asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index cc72a1e..66b766a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -291,6 +291,21 @@
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.dataEnabled.value)
 
+    override val inflateSignalStrength =
+        activeRepo
+            .flatMapLatest { it.inflateSignalStrength }
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                columnName = "inflate",
+                initialValue = activeRepo.value.inflateSignalStrength.value,
+            )
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                activeRepo.value.inflateSignalStrength.value
+            )
+
     override val numberOfLevels =
         activeRepo
             .flatMapLatest { it.numberOfLevels }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index e4a2a68..b3885d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -302,8 +302,10 @@
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), UnknownNetworkType)
 
+    override val inflateSignalStrength = systemUiCarrierConfig.shouldInflateSignalStrength
+
     override val numberOfLevels =
-        systemUiCarrierConfig.shouldInflateSignalStrength
+        inflateSignalStrength
             .map { shouldInflate ->
                 if (shouldInflate) {
                     DEFAULT_NUM_LEVELS + 1
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index adcf736..ed9e405 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -323,8 +323,11 @@
         combine(
                 level,
                 isInService,
-            ) { level, isInService ->
-                if (isInService) level else 0
+                connectionRepository.inflateSignalStrength,
+            ) { level, isInService, inflate ->
+                if (isInService) {
+                    if (inflate) level + 1 else level
+                } else 0
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index eda5c44..103b0e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
-import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions
 import com.android.systemui.Flags.statusBarStaticInoutIndicators
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -50,7 +50,7 @@
     /** True if this view should be visible at all. */
     val isVisible: StateFlow<Boolean>
     val icon: Flow<SignalIconModel>
-    val contentDescription: Flow<ContentDescription>
+    val contentDescription: Flow<ContentDescription?>
     val roaming: Flow<Boolean>
     /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
     val networkTypeIcon: Flow<Icon.Resource?>
@@ -123,7 +123,7 @@
 
     override val icon: Flow<SignalIconModel> = vmProvider.flatMapLatest { it.icon }
 
-    override val contentDescription: Flow<ContentDescription> =
+    override val contentDescription: Flow<ContentDescription?> =
         vmProvider.flatMapLatest { it.contentDescription }
 
     override val roaming: Flow<Boolean> = vmProvider.flatMapLatest { it.roaming }
@@ -206,12 +206,26 @@
 
     override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon
 
-    override val contentDescription: Flow<ContentDescription> = run {
-        val initial = ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[0])
+    override val contentDescription: Flow<ContentDescription?> =
         iconInteractor.signalLevelIcon
-            .map { ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[it.level]) }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
-    }
+            .map {
+                // We expect the signal icon to be cellular here since this is the cellular vm
+                if (it !is SignalIconModel.Cellular) {
+                    null
+                } else {
+                    val resId =
+                        AccessibilityContentDescriptions.getDescriptionForLevel(
+                            it.level,
+                            it.numberOfLevels
+                        )
+                    if (resId != 0) {
+                        ContentDescription.Resource(resId)
+                    } else {
+                        null
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
 
     private val showNetworkTypeIcon: Flow<Boolean> =
         combine(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
index 9f5260c..37dea11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.media.controls.ui.controller
 
-import android.provider.Settings
 import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -37,8 +36,6 @@
 import com.android.systemui.util.animation.UniqueObjectHostView
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
-import com.android.systemui.utils.os.FakeHandler
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertTrue
 import org.junit.Before
@@ -65,10 +62,7 @@
 
     private val mediaContainerView: MediaContainerView = MediaContainerView(context, null)
     private val hostView = UniqueObjectHostView(context)
-    private val settings = FakeSettings()
     private lateinit var keyguardMediaController: KeyguardMediaController
-    private lateinit var testableLooper: TestableLooper
-    private lateinit var fakeHandler: FakeHandler
     private lateinit var statusBarStateListener: StatusBarStateController.StateListener
 
     @Before
@@ -84,16 +78,12 @@
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(mediaHost.hostView).thenReturn(hostView)
         hostView.layoutParams = FrameLayout.LayoutParams(100, 100)
-        testableLooper = TestableLooper.get(this)
-        fakeHandler = FakeHandler(testableLooper.looper)
         keyguardMediaController =
             KeyguardMediaController(
                 mediaHost,
                 bypassController,
                 statusBarStateController,
                 context,
-                settings,
-                fakeHandler,
                 configurationController,
                 ResourcesSplitShadeStateController(),
                 mock<KeyguardMediaControllerLogger>(),
@@ -126,24 +116,6 @@
     }
 
     @Test
-    fun testHiddenOnKeyguard_whenMediaOnLockScreenDisabled() {
-        settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 0)
-
-        keyguardMediaController.refreshMediaPosition(TEST_REASON)
-
-        assertThat(mediaContainerView.visibility).isEqualTo(GONE)
-    }
-
-    @Test
-    fun testAvailableOnKeyguard_whenMediaOnLockScreenEnabled() {
-        settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1)
-
-        keyguardMediaController.refreshMediaPosition(TEST_REASON)
-
-        assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
-    }
-
-    @Test
     fun testActivatesSplitShadeContainerInSplitShadeMode() {
         val splitShadeContainer = FrameLayout(context)
         keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 29820f7..e9b11c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -156,6 +156,7 @@
         verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
         verify(statusBarStateController).addCallback(statusBarCallback.capture())
         verify(dreamOverlayStateController).addCallback(dreamOverlayCallback.capture())
+        whenever(mediaCarouselController.updateHostVisibility).thenReturn({})
         setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP)
         setupHost(qsHost, MediaHierarchyManager.LOCATION_QS, QS_TOP)
         setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS, QQS_TOP)
@@ -613,6 +614,55 @@
         assertThat(mediaCarouselScrollHandler.visibleToUser).isFalse()
     }
 
+    @Test
+    fun keyguardState_allowedOnLockscreen_updateVisibility() {
+        settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true)
+        clearInvocations(mediaCarouselController)
+
+        statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+        statusBarCallback.value.onStateChanged(StatusBarState.KEYGUARD)
+
+        verify(mediaCarouselController).updateHostVisibility
+        assertThat(mediaHierarchyManager.isLockedAndHidden()).isFalse()
+    }
+
+    @Test
+    fun keyguardState_notAllowedOnLockscreen_updateVisibility() {
+        settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
+        clearInvocations(mediaCarouselController)
+
+        statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+        statusBarCallback.value.onStateChanged(StatusBarState.KEYGUARD)
+
+        verify(mediaCarouselController).updateHostVisibility
+        assertThat(mediaHierarchyManager.isLockedAndHidden()).isTrue()
+    }
+
+    @Test
+    fun keyguardGone_updateVisibility() {
+        settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
+        clearInvocations(mediaCarouselController)
+
+        statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD, StatusBarState.SHADE)
+        statusBarCallback.value.onStateChanged(StatusBarState.SHADE)
+
+        verify(mediaCarouselController).updateHostVisibility
+        assertThat(mediaHierarchyManager.isLockedAndHidden()).isFalse()
+    }
+
+    @Test
+    fun lockscreenSettingChanged_updateVisibility() {
+        settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true)
+        statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+        statusBarCallback.value.onStateChanged(StatusBarState.KEYGUARD)
+        clearInvocations(mediaCarouselController)
+
+        settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
+
+        verify(mediaCarouselController).updateHostVisibility
+        assertThat(mediaHierarchyManager.isLockedAndHidden()).isTrue()
+    }
+
     private fun enableSplitShade() {
         context
             .getOrCreateTestableResources()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 1fb6e2c..a24e252 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
 
 import android.net.ConnectivityManager
+import android.os.PersistableBundle
 import android.telephony.ServiceState
 import android.telephony.SignalStrength
 import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
@@ -98,6 +99,9 @@
             )
         )
 
+    // Use a real config, with no overrides
+    private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_ID, PersistableBundle())
+
     private lateinit var mobileRepo: FakeMobileConnectionRepository
     private lateinit var carrierMergedRepo: FakeMobileConnectionRepository
 
@@ -689,7 +693,7 @@
                 SEP,
                 connectivityManager,
                 telephonyManager,
-                systemUiCarrierConfig = mock(),
+                systemUiCarrierConfig = systemUiCarrierConfig,
                 fakeBroadcastDispatcher,
                 mobileMappingsProxy = mock(),
                 testDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index f761bcf..9d14116 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -1030,6 +1030,26 @@
         }
 
     @Test
+    fun inflateSignalStrength_usesCarrierConfig() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.inflateSignalStrength)
+
+            assertThat(latest).isEqualTo(false)
+
+            systemUiCarrierConfig.processNewCarrierConfig(
+                configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, true)
+            )
+
+            assertThat(latest).isEqualTo(true)
+
+            systemUiCarrierConfig.processNewCarrierConfig(
+                configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
+            )
+
+            assertThat(latest).isEqualTo(false)
+        }
+
+    @Test
     fun isAllowedDuringAirplaneMode_alwaysFalse() =
         testScope.runTest {
             val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 49953a1..dfe8023 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -178,6 +178,22 @@
         }
 
     @Test
+    fun inflateSignalStrength_arbitrarilyAddsOneToTheReportedLevel() =
+        testScope.runTest {
+            connectionRepository.inflateSignalStrength.value = false
+            val latest by collectLastValue(underTest.signalLevelIcon)
+
+            connectionRepository.primaryLevel.value = 4
+            assertThat(latest!!.level).isEqualTo(4)
+
+            connectionRepository.inflateSignalStrength.value = true
+            connectionRepository.primaryLevel.value = 4
+
+            // when INFLATE_SIGNAL_STRENGTH is true, we add 1 to the reported signal level
+            assertThat(latest!!.level).isEqualTo(5)
+        }
+
+    @Test
     fun iconGroup_three_g() =
         testScope.runTest {
             connectionRepository.resolvedNetworkType.value =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 83d0fe8..cec4155 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -54,6 +54,7 @@
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.launchIn
@@ -279,6 +280,76 @@
         }
 
     @Test
+    fun contentDescription_nonInflated_invalidLevelIsNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.inflateSignalStrength.value = false
+            repository.setAllLevels(-1)
+            assertThat(latest).isNull()
+
+            repository.setAllLevels(100)
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun contentDescription_inflated_invalidLevelIsNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.inflateSignalStrength.value = true
+            repository.numberOfLevels.value = 6
+            repository.setAllLevels(-2)
+            assertThat(latest).isNull()
+
+            repository.setAllLevels(100)
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun contentDescription_nonInflated_testABunchOfLevelsForNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.inflateSignalStrength.value = false
+            repository.numberOfLevels.value = 5
+
+            // -1 and 5 are out of the bounds for non-inflated content descriptions
+            for (i in -1..5) {
+                repository.setAllLevels(i)
+                when (i) {
+                    -1,
+                    5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
+                    else ->
+                        assertWithMessage("Level $i is expected not to be null")
+                            .that(latest)
+                            .isNotNull()
+                }
+            }
+        }
+
+    @Test
+    fun contentDescription_inflated_testABunchOfLevelsForNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+            repository.inflateSignalStrength.value = true
+            repository.numberOfLevels.value = 6
+            // -1 and 6 are out of the bounds for inflated content descriptions
+            // Note that the interactor adds 1 to the reported level, hence the -2 to 5 range
+            for (i in -2..5) {
+                repository.setAllLevels(i)
+                when (i) {
+                    -2,
+                    5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
+                    else ->
+                        assertWithMessage("Level $i is not expected to be null")
+                            .that(latest)
+                            .isNotNull()
+                }
+            }
+        }
+
+    @Test
     fun networkType_dataEnabled_groupIsRepresented() =
         testScope.runTest {
             val expected =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index 32d572e..eb2d6c0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -31,6 +31,7 @@
     override val tableLogBuffer: TableLogBuffer,
 ) : MobileConnectionRepository {
     override val carrierId = MutableStateFlow(UNKNOWN_CARRIER_ID)
+    override val inflateSignalStrength: MutableStateFlow<Boolean> = MutableStateFlow(false)
     override val isEmergencyOnly = MutableStateFlow(false)
     override val isRoaming = MutableStateFlow(false)
     override val operatorAlphaShort: MutableStateFlow<String?> = MutableStateFlow(null)