diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt index 6c37f94007cba..1aa954ff48cf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt @@ -26,6 +26,8 @@ import android.telephony.TelephonyCallback.ServiceStateListener import android.telephony.TelephonyCallback.SignalStrengthsListener import android.telephony.TelephonyDisplayInfo import android.telephony.TelephonyManager +import com.android.systemui.log.table.Diffable +import com.android.systemui.log.table.TableRowLogger import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel @@ -79,4 +81,72 @@ data class MobileConnectionModel( * [TelephonyDisplayInfo.getNetworkType]. This is used to look up the proper network type icon */ val resolvedNetworkType: ResolvedNetworkType = ResolvedNetworkType.UnknownNetworkType, -) +) : Diffable { + override fun logDiffs(prevVal: MobileConnectionModel, row: TableRowLogger) { + if (prevVal.dataConnectionState != dataConnectionState) { + row.logChange(COL_CONNECTION_STATE, dataConnectionState.toString()) + } + + if (prevVal.isEmergencyOnly != isEmergencyOnly) { + row.logChange(COL_EMERGENCY, isEmergencyOnly) + } + + if (prevVal.isRoaming != isRoaming) { + row.logChange(COL_ROAMING, isRoaming) + } + + if (prevVal.operatorAlphaShort != operatorAlphaShort) { + row.logChange(COL_OPERATOR, operatorAlphaShort) + } + + if (prevVal.isGsm != isGsm) { + row.logChange(COL_IS_GSM, isGsm) + } + + if (prevVal.cdmaLevel != cdmaLevel) { + row.logChange(COL_CDMA_LEVEL, cdmaLevel) + } + + if (prevVal.primaryLevel != primaryLevel) { + row.logChange(COL_PRIMARY_LEVEL, primaryLevel) + } + + if (prevVal.dataActivityDirection != dataActivityDirection) { + row.logChange(COL_ACTIVITY_DIRECTION, dataActivityDirection.toString()) + } + + if (prevVal.carrierNetworkChangeActive != carrierNetworkChangeActive) { + row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChangeActive) + } + + if (prevVal.resolvedNetworkType != resolvedNetworkType) { + row.logChange(COL_RESOLVED_NETWORK_TYPE, resolvedNetworkType.toString()) + } + } + + override fun logFull(row: TableRowLogger) { + row.logChange(COL_CONNECTION_STATE, dataConnectionState.toString()) + row.logChange(COL_EMERGENCY, isEmergencyOnly) + row.logChange(COL_ROAMING, isRoaming) + row.logChange(COL_OPERATOR, operatorAlphaShort) + row.logChange(COL_IS_GSM, isGsm) + row.logChange(COL_CDMA_LEVEL, cdmaLevel) + row.logChange(COL_PRIMARY_LEVEL, primaryLevel) + row.logChange(COL_ACTIVITY_DIRECTION, dataActivityDirection.toString()) + row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChangeActive) + row.logChange(COL_RESOLVED_NETWORK_TYPE, resolvedNetworkType.toString()) + } + + companion object { + const val COL_EMERGENCY = "EmergencyOnly" + const val COL_ROAMING = "Roaming" + const val COL_OPERATOR = "OperatorName" + const val COL_IS_GSM = "IsGsm" + const val COL_CDMA_LEVEL = "CdmaLevel" + const val COL_PRIMARY_LEVEL = "PrimaryLevel" + const val COL_CONNECTION_STATE = "ConnectionState" + const val COL_ACTIVITY_DIRECTION = "DataActivity" + const val COL_CARRIER_NETWORK_CHANGE = "CarrierNetworkChangeActive" + const val COL_RESOLVED_NETWORK_TYPE = "NetworkType" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt index a8cf35ad30298..c50d82a66c76e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt @@ -21,22 +21,48 @@ import android.telephony.TelephonyManager.EXTRA_DATA_SPN import android.telephony.TelephonyManager.EXTRA_PLMN import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN import android.telephony.TelephonyManager.EXTRA_SHOW_SPN +import com.android.systemui.log.table.Diffable +import com.android.systemui.log.table.TableRowLogger /** * Encapsulates the data needed to show a network name for a mobile network. The data is parsed from * the intent sent by [android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED]. */ -sealed interface NetworkNameModel { +sealed interface NetworkNameModel : Diffable { val name: String /** The default name is read from [com.android.internal.R.string.lockscreen_carrier_default] */ - data class Default(override val name: String) : NetworkNameModel + data class Default(override val name: String) : NetworkNameModel { + override fun logDiffs(prevVal: NetworkNameModel, row: TableRowLogger) { + if (prevVal !is Default || prevVal.name != name) { + row.logChange(COL_NETWORK_NAME, "Default($name)") + } + } + + override fun logFull(row: TableRowLogger) { + row.logChange(COL_NETWORK_NAME, "Default($name)") + } + } /** * This name has been derived from telephony intents. see * [android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED] */ - data class Derived(override val name: String) : NetworkNameModel + data class Derived(override val name: String) : NetworkNameModel { + override fun logDiffs(prevVal: NetworkNameModel, row: TableRowLogger) { + if (prevVal !is Derived || prevVal.name != name) { + row.logChange(COL_NETWORK_NAME, "Derived($name)") + } + } + + override fun logFull(row: TableRowLogger) { + row.logChange(COL_NETWORK_NAME, "Derived($name)") + } + } + + companion object { + const val COL_NETWORK_NAME = "networkName" + } } fun Intent.toNetworkNameModel(separator: String): NetworkNameModel? { 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 7e9a9cea9b95b..50b29f4c90281 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 @@ -36,6 +36,9 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.TableLogBufferFactory +import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType @@ -46,7 +49,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameMo import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger -import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel import com.android.systemui.util.settings.GlobalSettings import javax.inject.Inject @@ -59,6 +61,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach @@ -79,6 +82,7 @@ class MobileConnectionRepositoryImpl( mobileMappingsProxy: MobileMappingsProxy, bgDispatcher: CoroutineDispatcher, logger: ConnectivityPipelineLogger, + mobileLogger: TableLogBuffer, scope: CoroutineScope, ) : MobileConnectionRepository { init { @@ -95,7 +99,6 @@ class MobileConnectionRepositoryImpl( override val connectionInfo: StateFlow = run { var state = MobileConnectionModel() conflatedCallbackFlow { - // TODO (b/240569788): log all of these into the connectivity logger val callback = object : TelephonyCallback(), @@ -106,6 +109,7 @@ class MobileConnectionRepositoryImpl( TelephonyCallback.CarrierNetworkListener, TelephonyCallback.DisplayInfoListener { override fun onServiceStateChanged(serviceState: ServiceState) { + logger.logOnServiceStateChanged(serviceState, subId) state = state.copy( isEmergencyOnly = serviceState.isEmergencyOnly, @@ -116,6 +120,7 @@ class MobileConnectionRepositoryImpl( } override fun onSignalStrengthsChanged(signalStrength: SignalStrength) { + logger.logOnSignalStrengthsChanged(signalStrength, subId) val cdmaLevel = signalStrength .getCellSignalStrengths(CellSignalStrengthCdma::class.java) @@ -142,12 +147,14 @@ class MobileConnectionRepositoryImpl( dataState: Int, networkType: Int ) { + logger.logOnDataConnectionStateChanged(dataState, networkType, subId) state = state.copy(dataConnectionState = dataState.toDataConnectionType()) trySend(state) } override fun onDataActivity(direction: Int) { + logger.logOnDataActivity(direction, subId) state = state.copy( dataActivityDirection = direction.toMobileDataActivityModel() @@ -156,6 +163,7 @@ class MobileConnectionRepositoryImpl( } override fun onCarrierNetworkChange(active: Boolean) { + logger.logOnCarrierNetworkChange(active, subId) state = state.copy(carrierNetworkChangeActive = active) trySend(state) } @@ -163,6 +171,7 @@ class MobileConnectionRepositoryImpl( override fun onDisplayInfoChanged( telephonyDisplayInfo: TelephonyDisplayInfo ) { + logger.logOnDisplayInfoChanged(telephonyDisplayInfo, subId) val networkType = if (telephonyDisplayInfo.networkType == NETWORK_TYPE_UNKNOWN) { @@ -193,7 +202,11 @@ class MobileConnectionRepositoryImpl( awaitClose { telephonyManager.unregisterTelephonyCallback(callback) } } .onEach { telephonyCallbackEvent.tryEmit(Unit) } - .logOutputChange(logger, "MobileSubscriptionModel") + .logDiffsForTable( + mobileLogger, + columnPrefix = "MobileConnection ($subId)", + initialValue = state, + ) .stateIn(scope, SharingStarted.WhileSubscribed(), state) } @@ -243,19 +256,43 @@ class MobileConnectionRepositoryImpl( intent.toNetworkNameModel(networkNameSeparator) ?: defaultNetworkName } } + .distinctUntilChanged() + .logDiffsForTable( + mobileLogger, + columnPrefix = "", + initialValue = defaultNetworkName, + ) .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName) - override val dataEnabled: StateFlow = + override val dataEnabled: StateFlow = run { + val initial = dataConnectionAllowed() telephonyPollingEvent .mapLatest { dataConnectionAllowed() } - .stateIn(scope, SharingStarted.WhileSubscribed(), dataConnectionAllowed()) + .distinctUntilChanged() + .logDiffsForTable( + mobileLogger, + columnPrefix = "", + columnName = "dataEnabled", + initialValue = initial, + ) + .stateIn(scope, SharingStarted.WhileSubscribed(), initial) + } private fun dataConnectionAllowed(): Boolean = telephonyManager.isDataConnectionAllowed - override val isDefaultDataSubscription: StateFlow = + override val isDefaultDataSubscription: StateFlow = run { + val initialValue = defaultDataSubId.value == subId defaultDataSubId .mapLatest { it == subId } - .stateIn(scope, SharingStarted.WhileSubscribed(), defaultDataSubId.value == subId) + .distinctUntilChanged() + .logDiffsForTable( + mobileLogger, + columnPrefix = "", + columnName = "isDefaultDataSub", + initialValue = initialValue, + ) + .stateIn(scope, SharingStarted.WhileSubscribed(), initialValue) + } class Factory @Inject @@ -266,6 +303,7 @@ class MobileConnectionRepositoryImpl( private val logger: ConnectivityPipelineLogger, private val globalSettings: GlobalSettings, private val mobileMappingsProxy: MobileMappingsProxy, + private val logFactory: TableLogBufferFactory, @Background private val bgDispatcher: CoroutineDispatcher, @Application private val scope: CoroutineScope, ) { @@ -276,6 +314,8 @@ class MobileConnectionRepositoryImpl( defaultDataSubId: StateFlow, globalMobileDataSettingChangedEvent: Flow, ): MobileConnectionRepository { + val mobileLogger = logFactory.create(tableBufferLogName(subId), 100) + return MobileConnectionRepositoryImpl( context, subId, @@ -289,8 +329,13 @@ class MobileConnectionRepositoryImpl( mobileMappingsProxy, bgDispatcher, logger, + mobileLogger, scope, ) } } + + companion object { + fun tableBufferLogName(subId: Int): String = "MobileConnectionLog [$subId]" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt index a9b3d18774fd1..d407abeb23150 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt @@ -51,6 +51,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConn import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger +import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange import com.android.systemui.util.settings.GlobalSettings import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -120,6 +121,7 @@ constructor( awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) } } .mapLatest { fetchSubscriptionsList().map { it.toSubscriptionModel() } } + .logInputChange(logger, "onSubscriptionsChanged") .onEach { infos -> dropUnusedReposFromCache(infos) } .stateIn(scope, started = SharingStarted.WhileSubscribed(), listOf()) @@ -136,6 +138,8 @@ constructor( telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback) awaitClose { telephonyManager.unregisterTelephonyCallback(callback) } } + .distinctUntilChanged() + .logInputChange(logger, "onActiveDataSubscriptionIdChanged") .stateIn(scope, started = SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID) private val defaultDataSubIdChangeEvent: MutableSharedFlow = @@ -149,6 +153,7 @@ constructor( intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID) } .distinctUntilChanged() + .logInputChange(logger, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED") .onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) } .stateIn( scope, @@ -157,13 +162,15 @@ constructor( ) private val carrierConfigChangedEvent = - broadcastDispatcher.broadcastFlow( - IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) - ) + broadcastDispatcher + .broadcastFlow(IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) + .logInputChange(logger, "ACTION_CARRIER_CONFIG_CHANGED") override val defaultDataSubRatConfig: StateFlow = merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent) .mapLatest { Config.readConfig(context) } + .distinctUntilChanged() + .logInputChange(logger, "defaultDataSubRatConfig") .stateIn( scope, SharingStarted.WhileSubscribed(), @@ -171,10 +178,16 @@ constructor( ) override val defaultMobileIconMapping: Flow> = - defaultDataSubRatConfig.map { mobileMappingsProxy.mapIconSets(it) } + defaultDataSubRatConfig + .map { mobileMappingsProxy.mapIconSets(it) } + .distinctUntilChanged() + .logInputChange(logger, "defaultMobileIconMapping") override val defaultMobileIconGroup: Flow = - defaultDataSubRatConfig.map { mobileMappingsProxy.getDefaultIcons(it) } + defaultDataSubRatConfig + .map { mobileMappingsProxy.getDefaultIcons(it) } + .distinctUntilChanged() + .logInputChange(logger, "defaultMobileIconGroup") override fun getRepoForSubId(subId: Int): MobileConnectionRepository { if (!isValidSubId(subId)) { @@ -191,22 +204,24 @@ constructor( * In single-SIM devices, the [MOBILE_DATA] setting is phone-wide. For multi-SIM, the individual * connection repositories also observe the URI for [MOBILE_DATA] + subId. */ - override val globalMobileDataSettingChangedEvent: Flow = conflatedCallbackFlow { - val observer = - object : ContentObserver(null) { - override fun onChange(selfChange: Boolean) { - trySend(Unit) - } + override val globalMobileDataSettingChangedEvent: Flow = + conflatedCallbackFlow { + val observer = + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + trySend(Unit) + } + } + + globalSettings.registerContentObserver( + globalSettings.getUriFor(MOBILE_DATA), + true, + observer + ) + + awaitClose { context.contentResolver.unregisterContentObserver(observer) } } - - globalSettings.registerContentObserver( - globalSettings.getUriFor(MOBILE_DATA), - true, - observer - ) - - awaitClose { context.contentResolver.unregisterContentObserver(observer) } - } + .logInputChange(logger, "globalMobileDataSettingChangedEvent") @SuppressLint("MissingPermission") override val defaultMobileNetworkConnectivity: StateFlow = @@ -236,6 +251,8 @@ constructor( awaitClose { connectivityManager.unregisterNetworkCallback(callback) } } + .distinctUntilChanged() + .logInputChange(logger, "defaultMobileNetworkConnectivity") .stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel()) private fun isValidSubId(subId: Int): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt index d3cf32fb44ce5..d3ff3573dae2a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt @@ -18,8 +18,11 @@ package com.android.systemui.statusbar.pipeline.shared import android.net.Network import android.net.NetworkCapabilities -import com.android.systemui.log.dagger.StatusBarConnectivityLog +import android.telephony.ServiceState +import android.telephony.SignalStrength +import android.telephony.TelephonyDisplayInfo import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.dagger.StatusBarConnectivityLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.toString @@ -28,7 +31,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.onEach @SysUISingleton -class ConnectivityPipelineLogger @Inject constructor( +class ConnectivityPipelineLogger +@Inject +constructor( @StatusBarConnectivityLog private val buffer: LogBuffer, ) { /** @@ -37,34 +42,23 @@ class ConnectivityPipelineLogger @Inject constructor( * Use this method for inputs that don't have any extra information besides their callback name. */ fun logInputChange(callbackName: String) { + buffer.log(SB_LOGGING_TAG, LogLevel.INFO, { str1 = callbackName }, { "Input: $str1" }) + } + + /** Logs a change in one of the **raw inputs** to the connectivity pipeline. */ + fun logInputChange(callbackName: String, changeInfo: String?) { buffer.log( SB_LOGGING_TAG, LogLevel.INFO, - { str1 = callbackName }, - { "Input: $str1" } + { + str1 = callbackName + str2 = changeInfo + }, + { "Input: $str1: $str2" } ) } - /** - * Logs a change in one of the **raw inputs** to the connectivity pipeline. - */ - fun logInputChange(callbackName: String, changeInfo: String?) { - buffer.log( - SB_LOGGING_TAG, - LogLevel.INFO, - { - str1 = callbackName - str2 = changeInfo - }, - { - "Input: $str1: $str2" - } - ) - } - - /** - * Logs a **data transformation** that we performed within the connectivity pipeline. - */ + /** Logs a **data transformation** that we performed within the connectivity pipeline. */ fun logTransformation(transformationName: String, oldValue: Any?, newValue: Any?) { if (oldValue == newValue) { buffer.log( @@ -74,9 +68,7 @@ class ConnectivityPipelineLogger @Inject constructor( str1 = transformationName str2 = oldValue.toString() }, - { - "Transform: $str1: $str2 (transformation didn't change it)" - } + { "Transform: $str1: $str2 (transformation didn't change it)" } ) } else { buffer.log( @@ -87,27 +79,21 @@ class ConnectivityPipelineLogger @Inject constructor( str2 = oldValue.toString() str3 = newValue.toString() }, - { - "Transform: $str1: $str2 -> $str3" - } + { "Transform: $str1: $str2 -> $str3" } ) } } - /** - * Logs a change in one of the **outputs** to the connectivity pipeline. - */ + /** Logs a change in one of the **outputs** to the connectivity pipeline. */ fun logOutputChange(outputParamName: String, changeInfo: String) { buffer.log( - SB_LOGGING_TAG, - LogLevel.INFO, - { - str1 = outputParamName - str2 = changeInfo - }, - { - "Output: $str1: $str2" - } + SB_LOGGING_TAG, + LogLevel.INFO, + { + str1 = outputParamName + str2 = changeInfo + }, + { "Output: $str1: $str2" } ) } @@ -119,31 +105,101 @@ class ConnectivityPipelineLogger @Inject constructor( int1 = network.getNetId() str1 = networkCapabilities.toString() }, - { - "onCapabilitiesChanged: net=$int1 capabilities=$str1" - } + { "onCapabilitiesChanged: net=$int1 capabilities=$str1" } ) } fun logOnLost(network: Network) { + buffer.log( + SB_LOGGING_TAG, + LogLevel.INFO, + { int1 = network.getNetId() }, + { "onLost: net=$int1" } + ) + } + + fun logOnServiceStateChanged(serviceState: ServiceState, subId: Int) { buffer.log( SB_LOGGING_TAG, LogLevel.INFO, { - int1 = network.getNetId() + int1 = subId + bool1 = serviceState.isEmergencyOnly + bool2 = serviceState.roaming + str1 = serviceState.operatorAlphaShort }, { - "onLost: net=$int1" + "onServiceStateChanged: subId=$int1 emergencyOnly=$bool1 roaming=$bool2" + + " operator=$str1" } ) } + fun logOnSignalStrengthsChanged(signalStrength: SignalStrength, subId: Int) { + buffer.log( + SB_LOGGING_TAG, + LogLevel.INFO, + { + int1 = subId + str1 = signalStrength.toString() + }, + { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" } + ) + } + + fun logOnDataConnectionStateChanged(dataState: Int, networkType: Int, subId: Int) { + buffer.log( + SB_LOGGING_TAG, + LogLevel.INFO, + { + int1 = subId + int2 = dataState + str1 = networkType.toString() + }, + { "onDataConnectionStateChanged: subId=$int1 dataState=$int2 networkType=$str1" }, + ) + } + + fun logOnDataActivity(direction: Int, subId: Int) { + buffer.log( + SB_LOGGING_TAG, + LogLevel.INFO, + { + int1 = subId + int2 = direction + }, + { "onDataActivity: subId=$int1 direction=$int2" }, + ) + } + + fun logOnCarrierNetworkChange(active: Boolean, subId: Int) { + buffer.log( + SB_LOGGING_TAG, + LogLevel.INFO, + { + int1 = subId + bool1 = active + }, + { "onCarrierNetworkChange: subId=$int1 active=$bool1" }, + ) + } + + fun logOnDisplayInfoChanged(displayInfo: TelephonyDisplayInfo, subId: Int) { + buffer.log( + SB_LOGGING_TAG, + LogLevel.INFO, + { + int1 = subId + str1 = displayInfo.toString() + }, + { "onDisplayInfoChanged: subId=$int1 displayInfo=$str1" }, + ) + } + companion object { const val SB_LOGGING_TAG = "SbConnectivity" - /** - * Log a change in one of the **inputs** to the connectivity pipeline. - */ + /** Log a change in one of the **inputs** to the connectivity pipeline. */ fun Flow.logInputChange( logger: ConnectivityPipelineLogger, inputParamName: String, @@ -155,26 +211,26 @@ class ConnectivityPipelineLogger @Inject constructor( * Log a change in one of the **inputs** to the connectivity pipeline. * * @param prettyPrint an optional function to transform the value into a readable string. - * [toString] is used if no custom function is provided. + * [toString] is used if no custom function is provided. */ fun Flow.logInputChange( logger: ConnectivityPipelineLogger, inputParamName: String, prettyPrint: (T) -> String = { it.toString() } ): Flow { - return this.onEach {logger.logInputChange(inputParamName, prettyPrint(it)) } + return this.onEach { logger.logInputChange(inputParamName, prettyPrint(it)) } } /** * Log a change in one of the **outputs** to the connectivity pipeline. * * @param prettyPrint an optional function to transform the value into a readable string. - * [toString] is used if no custom function is provided. + * [toString] is used if no custom function is provided. */ fun Flow.logOutputChange( - logger: ConnectivityPipelineLogger, - outputParamName: String, - prettyPrint: (T) -> String = { it.toString() } + logger: ConnectivityPipelineLogger, + outputParamName: String, + prettyPrint: (T) -> String = { it.toString() } ): Flow { return this.onEach { logger.logOutputChange(outputParamName, prettyPrint(it)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModelTest.kt new file mode 100644 index 0000000000000..f822ba0f0a622 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModelTest.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 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 androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.log.table.TableRowLogger +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_ACTIVITY_DIRECTION +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_CARRIER_NETWORK_CHANGE +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_CDMA_LEVEL +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_CONNECTION_STATE +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_EMERGENCY +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_IS_GSM +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_OPERATOR +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_PRIMARY_LEVEL +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_RESOLVED_NETWORK_TYPE +import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_ROAMING +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +@SmallTest +class MobileConnectionModelTest : SysuiTestCase() { + + @Test + fun `log diff - initial log contains all columns`() { + val logger = TestLogger() + val connection = MobileConnectionModel() + + connection.logFull(logger) + + assertThat(logger.changes) + .contains(Pair(COL_EMERGENCY, connection.isEmergencyOnly.toString())) + assertThat(logger.changes).contains(Pair(COL_ROAMING, connection.isRoaming.toString())) + assertThat(logger.changes) + .contains(Pair(COL_OPERATOR, connection.operatorAlphaShort.toString())) + assertThat(logger.changes).contains(Pair(COL_IS_GSM, connection.isGsm.toString())) + assertThat(logger.changes).contains(Pair(COL_CDMA_LEVEL, connection.cdmaLevel.toString())) + assertThat(logger.changes) + .contains(Pair(COL_PRIMARY_LEVEL, connection.primaryLevel.toString())) + assertThat(logger.changes) + .contains(Pair(COL_CONNECTION_STATE, connection.dataConnectionState.toString())) + assertThat(logger.changes) + .contains(Pair(COL_ACTIVITY_DIRECTION, connection.dataActivityDirection.toString())) + assertThat(logger.changes) + .contains( + Pair(COL_CARRIER_NETWORK_CHANGE, connection.carrierNetworkChangeActive.toString()) + ) + assertThat(logger.changes) + .contains(Pair(COL_RESOLVED_NETWORK_TYPE, connection.resolvedNetworkType.toString())) + } + + @Test + fun `log diff - primary level changes - only level is logged`() { + val logger = TestLogger() + val connectionOld = MobileConnectionModel(primaryLevel = 1) + + val connectionNew = MobileConnectionModel(primaryLevel = 2) + + connectionNew.logDiffs(connectionOld, logger) + + assertThat(logger.changes).isEqualTo(listOf(Pair(COL_PRIMARY_LEVEL, "2"))) + } + + private class TestLogger : TableRowLogger { + val changes = mutableListOf>() + + override fun logChange(columnName: String, value: String?) { + changes.add(Pair(columnName, value.toString())) + } + + override fun logChange(columnName: String, value: Int) { + changes.add(Pair(columnName, value.toString())) + } + + override fun logChange(columnName: String, value: Boolean) { + changes.add(Pair(columnName, value.toString())) + } + } +} 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 7fa80653f29c9..9a3e95826c84b 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 @@ -50,6 +50,7 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_LTE import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel @@ -90,6 +91,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { @Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var logger: ConnectivityPipelineLogger + @Mock private lateinit var tableLogger: TableLogBuffer private val scope = CoroutineScope(IMMEDIATE) private val mobileMappings = FakeMobileMappingsProxy() @@ -116,6 +118,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { mobileMappings, IMMEDIATE, logger, + tableLogger, scope, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index 3cc1e8b74668e..b8cd7a4f6e0a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -34,12 +34,15 @@ import com.android.internal.telephony.PhoneConstants import com.android.settingslib.R import com.android.settingslib.mobile.MobileMappings import com.android.systemui.SysuiTestCase +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.FakeSettings @@ -57,6 +60,7 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -72,6 +76,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { @Mock private lateinit var subscriptionManager: SubscriptionManager @Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var logger: ConnectivityPipelineLogger + @Mock private lateinit var logBufferFactory: TableLogBufferFactory private val mobileMappings = FakeMobileMappingsProxy() @@ -89,6 +94,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { } } + whenever(logBufferFactory.create(anyString(), anyInt())).thenAnswer { _ -> + mock() + } + connectionFactory = MobileConnectionRepositoryImpl.Factory( fakeBroadcastDispatcher, @@ -99,6 +108,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { logger = logger, mobileMappingsProxy = mobileMappings, scope = scope, + logFactory = logBufferFactory, ) underTest = @@ -270,6 +280,32 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { job.cancel() } + @Test + fun `connection repository - log buffer contains sub id in its name`() = + runBlocking(IMMEDIATE) { + val job = underTest.subscriptions.launchIn(this) + + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1, SUB_2)) + getSubscriptionCallback().onSubscriptionsChanged() + + // Get repos to trigger creation + underTest.getRepoForSubId(SUB_1_ID) + verify(logBufferFactory) + .create( + eq(MobileConnectionRepositoryImpl.tableBufferLogName(SUB_1_ID)), + anyInt(), + ) + underTest.getRepoForSubId(SUB_2_ID) + verify(logBufferFactory) + .create( + eq(MobileConnectionRepositoryImpl.tableBufferLogName(SUB_2_ID)), + anyInt(), + ) + + job.cancel() + } + @Test fun testDefaultDataSubId_updatesOnBroadcast() = runBlocking(IMMEDIATE) {