Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt +4 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.satellite.data import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow /** * Device-based satellite refers to the capability of a device to connect directly to a satellite Loading @@ -25,6 +26,9 @@ import kotlinx.coroutines.flow.Flow * given mobile data subscription. */ interface DeviceBasedSatelliteRepository { /** The current status of satellite provisioning. If not false, we don't want to show an icon */ val isSatelliteProvisioned: StateFlow<Boolean> /** See [SatelliteConnectionState] for available states */ val connectionState: Flow<SatelliteConnectionState> Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +38 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.telephony.satellite.NtnSignalStrengthCallback import android.telephony.satellite.SatelliteManager import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS import android.telephony.satellite.SatelliteModemStateCallback import android.telephony.satellite.SatelliteProvisionStateCallback import android.telephony.satellite.SatelliteSupportedStateCallback import androidx.annotation.VisibleForTesting import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow Loading Loading @@ -337,6 +338,43 @@ constructor( } } override val isSatelliteProvisioned: StateFlow<Boolean> = satelliteSupport .whenSupported( supported = ::satelliteProvisioned, orElse = flowOf(false), retrySignal = telephonyProcessCrashedEvent, ) .stateIn(scope, SharingStarted.WhileSubscribed(), false) private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> = conflatedCallbackFlow { val callback = SatelliteProvisionStateCallback { provisioned -> logBuffer.i { "onSatelliteProvisionStateChanged: " + if (provisioned) "provisioned" else "not provisioned" } trySend(provisioned) } var registered = false try { sm.registerForProvisionStateChanged( bgDispatcher.asExecutor(), callback, ) registered = true } catch (e: Exception) { logBuffer.e("error registering for provisioning state callback", e) } awaitClose { if (registered) { sm.unregisterForProvisionStateChanged(callback) } } } /** * Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there * are active listeners to [isSatelliteAllowedForCurrentLocation] Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt +1 −3 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelli import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi Loading @@ -45,7 +44,6 @@ class DeviceBasedSatelliteInteractor constructor( val repo: DeviceBasedSatelliteRepository, iconsInteractor: MobileIconsInteractor, deviceProvisioningInteractor: DeviceProvisioningInteractor, wifiInteractor: WifiInteractor, @Application scope: CoroutineScope, @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer, Loading Loading @@ -78,7 +76,7 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), 0) val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned val isSatelliteProvisioned = repo.isSatelliteProvisioned val isWifiActive: Flow<Boolean> = wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active } Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt +3 −3 Original line number Diff line number Diff line Loading @@ -79,11 +79,11 @@ constructor( } else { combine( interactor.isSatelliteAllowed, interactor.isDeviceProvisioned, interactor.isSatelliteProvisioned, interactor.isWifiActive, airplaneModeRepository.isAirplaneMode ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode -> isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode -> isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode } } } Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt +93 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAI import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN import android.telephony.satellite.SatelliteManager.SatelliteException import android.telephony.satellite.SatelliteModemStateCallback import android.telephony.satellite.SatelliteProvisionStateCallback import android.telephony.satellite.SatelliteSupportedStateCallback import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase Loading Loading @@ -325,6 +326,98 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { assertThat(latest).isFalse() } @Test fun satelliteProvisioned_notSupported_defaultFalse() = testScope.runTest { // GIVEN satellite is not supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false, ) assertThat(underTest.isSatelliteProvisioned.value).isFalse() } @Test fun satelliteProvisioned_supported_defaultFalse() = testScope.runTest { // GIVEN satellite is supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true, ) // THEN default provisioned state is false assertThat(underTest.isSatelliteProvisioned.value).isFalse() } @Test fun satelliteProvisioned_supported_tracksCallback() = testScope.runTest { // GIVEN satellite is not supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true, ) val provisioned by collectLastValue(underTest.isSatelliteProvisioned) runCurrent() val callback = withArgCaptor<SatelliteProvisionStateCallback> { verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) } // WHEN provisioning state changes callback.onSatelliteProvisionStateChanged(true) // THEN the value is reflected in the repo assertThat(provisioned).isTrue() } @Test fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() = testScope.runTest { // GIVEN satellite is supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true, ) val provisioned by collectLastValue(underTest.isSatelliteProvisioned) runCurrent() val callback = withArgCaptor<SatelliteProvisionStateCallback> { verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) } val telephonyCallback = MobileTelephonyHelpers.getTelephonyCallbackForType< TelephonyCallback.RadioPowerStateListener >( telephonyManager ) // GIVEN satellite is currently provisioned callback.onSatelliteProvisionStateChanged(true) assertThat(provisioned).isTrue() // WHEN a crash event happens (detected by radio state change) telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON) runCurrent() telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF) runCurrent() // THEN listeners are re-registered verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any()) } @Test fun satelliteNotSupported_listenersAreNotRegistered() = testScope.runTest { Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt +4 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.satellite.data import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow /** * Device-based satellite refers to the capability of a device to connect directly to a satellite Loading @@ -25,6 +26,9 @@ import kotlinx.coroutines.flow.Flow * given mobile data subscription. */ interface DeviceBasedSatelliteRepository { /** The current status of satellite provisioning. If not false, we don't want to show an icon */ val isSatelliteProvisioned: StateFlow<Boolean> /** See [SatelliteConnectionState] for available states */ val connectionState: Flow<SatelliteConnectionState> Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +38 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.telephony.satellite.NtnSignalStrengthCallback import android.telephony.satellite.SatelliteManager import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS import android.telephony.satellite.SatelliteModemStateCallback import android.telephony.satellite.SatelliteProvisionStateCallback import android.telephony.satellite.SatelliteSupportedStateCallback import androidx.annotation.VisibleForTesting import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow Loading Loading @@ -337,6 +338,43 @@ constructor( } } override val isSatelliteProvisioned: StateFlow<Boolean> = satelliteSupport .whenSupported( supported = ::satelliteProvisioned, orElse = flowOf(false), retrySignal = telephonyProcessCrashedEvent, ) .stateIn(scope, SharingStarted.WhileSubscribed(), false) private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> = conflatedCallbackFlow { val callback = SatelliteProvisionStateCallback { provisioned -> logBuffer.i { "onSatelliteProvisionStateChanged: " + if (provisioned) "provisioned" else "not provisioned" } trySend(provisioned) } var registered = false try { sm.registerForProvisionStateChanged( bgDispatcher.asExecutor(), callback, ) registered = true } catch (e: Exception) { logBuffer.e("error registering for provisioning state callback", e) } awaitClose { if (registered) { sm.unregisterForProvisionStateChanged(callback) } } } /** * Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there * are active listeners to [isSatelliteAllowedForCurrentLocation] Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt +1 −3 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelli import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi Loading @@ -45,7 +44,6 @@ class DeviceBasedSatelliteInteractor constructor( val repo: DeviceBasedSatelliteRepository, iconsInteractor: MobileIconsInteractor, deviceProvisioningInteractor: DeviceProvisioningInteractor, wifiInteractor: WifiInteractor, @Application scope: CoroutineScope, @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer, Loading Loading @@ -78,7 +76,7 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), 0) val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned val isSatelliteProvisioned = repo.isSatelliteProvisioned val isWifiActive: Flow<Boolean> = wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active } Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt +3 −3 Original line number Diff line number Diff line Loading @@ -79,11 +79,11 @@ constructor( } else { combine( interactor.isSatelliteAllowed, interactor.isDeviceProvisioned, interactor.isSatelliteProvisioned, interactor.isWifiActive, airplaneModeRepository.isAirplaneMode ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode -> isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode -> isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode } } } Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt +93 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAI import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN import android.telephony.satellite.SatelliteManager.SatelliteException import android.telephony.satellite.SatelliteModemStateCallback import android.telephony.satellite.SatelliteProvisionStateCallback import android.telephony.satellite.SatelliteSupportedStateCallback import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase Loading Loading @@ -325,6 +326,98 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { assertThat(latest).isFalse() } @Test fun satelliteProvisioned_notSupported_defaultFalse() = testScope.runTest { // GIVEN satellite is not supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false, ) assertThat(underTest.isSatelliteProvisioned.value).isFalse() } @Test fun satelliteProvisioned_supported_defaultFalse() = testScope.runTest { // GIVEN satellite is supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true, ) // THEN default provisioned state is false assertThat(underTest.isSatelliteProvisioned.value).isFalse() } @Test fun satelliteProvisioned_supported_tracksCallback() = testScope.runTest { // GIVEN satellite is not supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true, ) val provisioned by collectLastValue(underTest.isSatelliteProvisioned) runCurrent() val callback = withArgCaptor<SatelliteProvisionStateCallback> { verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) } // WHEN provisioning state changes callback.onSatelliteProvisionStateChanged(true) // THEN the value is reflected in the repo assertThat(provisioned).isTrue() } @Test fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() = testScope.runTest { // GIVEN satellite is supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true, ) val provisioned by collectLastValue(underTest.isSatelliteProvisioned) runCurrent() val callback = withArgCaptor<SatelliteProvisionStateCallback> { verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) } val telephonyCallback = MobileTelephonyHelpers.getTelephonyCallbackForType< TelephonyCallback.RadioPowerStateListener >( telephonyManager ) // GIVEN satellite is currently provisioned callback.onSatelliteProvisionStateChanged(true) assertThat(provisioned).isTrue() // WHEN a crash event happens (detected by radio state change) telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON) runCurrent() telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF) runCurrent() // THEN listeners are re-registered verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any()) } @Test fun satelliteNotSupported_listenersAreNotRegistered() = testScope.runTest { Loading