Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca2f7297 authored by Evan Laird's avatar Evan Laird Committed by Android (Google) Code Review
Browse files

Merge "[mobile] Device-based emergency call capable" into main

parents 4d9e1d9d dcfc485a
Loading
Loading
Loading
Loading
+0 −31
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.pipeline.mobile.data.model

import android.telephony.ServiceState

/**
 * Simplified representation of a [ServiceState] for use in SystemUI. Add any fields that we need to
 * extract from service state here for consumption downstream
 */
data class ServiceStateModel(val isEmergencyOnly: Boolean) {
    companion object {
        fun fromServiceState(serviceState: ServiceState): ServiceStateModel {
            return ServiceStateModel(isEmergencyOnly = serviceState.isEmergencyOnly)
        }
    }
}
+6 −9
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.telephony.SubscriptionManager
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -93,17 +92,15 @@ interface MobileConnectionsRepository {
    val defaultMobileIconGroup: Flow<MobileIconGroup>

    /**
     * [deviceServiceState] is equivalent to the last [Intent.ACTION_SERVICE_STATE] broadcast with a
     * subscriptionId of -1 (aka [SubscriptionManager.INVALID_SUBSCRIPTION_ID]).
     * Can the device make emergency calls using the device-based service state? This field is only
     * useful when all known active subscriptions are OOS and not emergency call capable.
     *
     * While each [MobileConnectionsRepository] listens for the service state of each subscription,
     * there is potentially a service state associated with the device itself. This value can be
     * used to calculate e.g., the emergency calling capability of the device (as opposed to the
     * emergency calling capability of an individual mobile connection)
     * Specifically, this checks every [ServiceState] of the device, and looks for any that report
     * [ServiceState.isEmergencyOnly].
     *
     * Note: this is a [StateFlow] using an eager sharing strategy.
     * This is an eager flow, and re-evaluates whenever ACTION_SERVICE_STATE is sent for subId = -1.
     */
    val deviceServiceState: StateFlow<ServiceStateModel?>
    val isDeviceEmergencyCallCapable: StateFlow<Boolean>

    /**
     * If any active SIM on the device is in
+4 −4
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
@@ -152,16 +151,17 @@ constructor(
    override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
        activeRepo.flatMapLatest { it.defaultMobileIconGroup }

    override val deviceServiceState: StateFlow<ServiceStateModel?> =
    override val isDeviceEmergencyCallCapable: StateFlow<Boolean> =
        activeRepo
            .flatMapLatest { it.deviceServiceState }
            .flatMapLatest { it.isDeviceEmergencyCallCapable }
            .stateIn(
                scope,
                SharingStarted.WhileSubscribed(),
                realRepository.deviceServiceState.value
                realRepository.isDeviceEmergencyCallCapable.value
            )

    override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }

    override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure()

    override val defaultDataSubId: StateFlow<Int> =
+3 −3
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
@@ -137,10 +136,11 @@ constructor(

    override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)

    // TODO(b/339023069): demo command for device-based connectivity state
    override val deviceServiceState: StateFlow<ServiceStateModel?> = MutableStateFlow(null)
    // TODO(b/339023069): demo command for device-based emergency calls state
    override val isDeviceEmergencyCallCapable: StateFlow<Boolean> = MutableStateFlow(false)

    override val isAnySimSecure: Flow<Boolean> = flowOf(getIsAnySimSecure())

    override fun getIsAnySimSecure(): Boolean = false

    override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
+27 −20
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.telephony.CarrierConfigManager
import android.telephony.ServiceState
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -49,7 +48,6 @@ import com.android.systemui.statusbar.pipeline.airplane.data.repository.Airplane
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
@@ -72,7 +70,6 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
@@ -175,8 +172,8 @@ constructor(
            }
            .flowOn(bgDispatcher)

    /** Note that this flow is eager, so we don't miss any state */
    override val deviceServiceState: StateFlow<ServiceStateModel?> =
    /** Turn ACTION_SERVICE_STATE (for subId = -1) into an event */
    private val serviceStateChangedEvent: Flow<Unit> =
        broadcastDispatcher
            .broadcastFlow(IntentFilter(Intent.ACTION_SERVICE_STATE)) { intent, _ ->
                val subId =
@@ -185,24 +182,34 @@ constructor(
                        INVALID_SUBSCRIPTION_ID
                    )

                val extras = intent.extras
                if (extras == null) {
                    logger.logTopLevelServiceStateBroadcastMissingExtras(subId)
                    return@broadcastFlow null
                }

                val serviceState = ServiceState.newFromBundle(extras)
                logger.logTopLevelServiceStateBroadcastEmergencyOnly(subId, serviceState)
                // Only emit if the subId is not associated with an active subscription
                if (subId == INVALID_SUBSCRIPTION_ID) {
                    // Assume that -1 here is the device's service state. We don't care about
                    // other ones.
                    ServiceStateModel.fromServiceState(serviceState)
                } else {
                    null
                    Unit
                }
            }
            .filterNotNull()
            .stateIn(scope, SharingStarted.Eagerly, null)
            // Emit on start so that we always check the state at least once
            .onStart { emit(Unit) }

    /** Eager flow to determine the device-based emergency calls only state */
    override val isDeviceEmergencyCallCapable: StateFlow<Boolean> =
        serviceStateChangedEvent
            .mapLatest {
                val modems = telephonyManager.activeModemCount
                // Check the service state for every modem. If any state reports emergency calling
                // capable, then consider the device to have emergency call capabilities
                (0..<modems)
                    .map { telephonyManager.getServiceStateForSlot(it) }
                    .any { it?.isEmergencyOnly == true }
            }
            .flowOn(bgDispatcher)
            .distinctUntilChanged()
            .logDiffsForTable(
                tableLogger,
                columnPrefix = LOGGING_PREFIX,
                columnName = "deviceEmergencyOnly",
                initialValue = false,
            )
            .stateIn(scope, SharingStarted.Eagerly, false)

    /**
     * State flow that emits the set of mobile data subscriptions, each represented by its own
Loading