1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.connectivity 18 19 import android.annotation.DrawableRes 20 import android.content.Context 21 import android.telephony.ServiceState 22 import android.telephony.SignalStrength 23 import android.telephony.TelephonyDisplayInfo 24 import android.telephony.TelephonyManager 25 import com.android.internal.annotations.VisibleForTesting 26 import com.android.settingslib.SignalIcon.MobileIconGroup 27 import com.android.settingslib.Utils 28 import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus 29 import com.android.settingslib.mobile.TelephonyIcons 30 import java.lang.IllegalArgumentException 31 32 /** 33 * Box for all policy-related state used in [MobileSignalController] 34 */ 35 internal class MobileState( 36 @JvmField var networkName: String? = null, 37 @JvmField var networkNameData: String? = null, 38 @JvmField var dataSim: Boolean = false, 39 @JvmField var dataConnected: Boolean = false, 40 @JvmField var isEmergency: Boolean = false, 41 @JvmField var airplaneMode: Boolean = false, 42 @JvmField var carrierNetworkChangeMode: Boolean = false, 43 @JvmField var isDefault: Boolean = false, 44 @JvmField var userSetup: Boolean = false, 45 @JvmField var roaming: Boolean = false, 46 @JvmField var dataState: Int = TelephonyManager.DATA_DISCONNECTED, 47 // Tracks the on/off state of the defaultDataSubscription 48 @JvmField var defaultDataOff: Boolean = false, 49 ) : ConnectivityState() { 50 51 @JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, 52 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false) 53 @JvmField var serviceState: ServiceState? = null 54 @JvmField var signalStrength: SignalStrength? = null 55 56 var carrierId = TelephonyManager.UNKNOWN_CARRIER_ID 57 58 @VisibleForTesting 59 var networkTypeResIdCache: NetworkTypeResIdCache = NetworkTypeResIdCache() 60 61 /** @return true if this state is disabled or not default data */ 62 val isDataDisabledOrNotDefault: Boolean 63 get() = (iconGroup === TelephonyIcons.DATA_DISABLED || 64 iconGroup === TelephonyIcons.NOT_DEFAULT_DATA) && userSetup 65 66 /** @return if this state is considered to have inbound activity */ 67 fun hasActivityIn(): Boolean { 68 return dataConnected && !carrierNetworkChangeMode && activityIn 69 } 70 71 /** @return if this state is considered to have outbound activity */ 72 fun hasActivityOut(): Boolean { 73 return dataConnected && !carrierNetworkChangeMode && activityOut 74 } 75 76 /** @return true if this state should show a RAT icon in quick settings */ 77 fun showQuickSettingsRatIcon(): Boolean { 78 return dataConnected || isDataDisabledOrNotDefault 79 } 80 81 override fun copyFrom(other: ConnectivityState) { 82 val o = other as? MobileState ?: throw IllegalArgumentException( 83 "MobileState can only update from another MobileState") 84 85 super.copyFrom(o) 86 networkName = o.networkName 87 networkNameData = o.networkNameData 88 dataSim = o.dataSim 89 dataConnected = o.dataConnected 90 isEmergency = o.isEmergency 91 airplaneMode = o.airplaneMode 92 carrierNetworkChangeMode = o.carrierNetworkChangeMode 93 isDefault = o.isDefault 94 userSetup = o.userSetup 95 roaming = o.roaming 96 dataState = o.dataState 97 defaultDataOff = o.defaultDataOff 98 99 telephonyDisplayInfo = o.telephonyDisplayInfo 100 serviceState = o.serviceState 101 signalStrength = o.signalStrength 102 } 103 104 fun isDataConnected(): Boolean { 105 return connected && dataState == TelephonyManager.DATA_CONNECTED 106 } 107 108 /** @return the current voice service state, or -1 if null */ 109 fun getVoiceServiceState(): Int { 110 return serviceState?.state ?: -1 111 } 112 113 fun isNoCalling(): Boolean { 114 return serviceState?.state != ServiceState.STATE_IN_SERVICE 115 } 116 117 fun getOperatorAlphaShort(): String { 118 return serviceState?.operatorAlphaShort ?: "" 119 } 120 121 fun isCdma(): Boolean { 122 return signalStrength != null && !signalStrength!!.isGsm 123 } 124 125 fun isEmergencyOnly(): Boolean { 126 return serviceState != null && serviceState!!.isEmergencyOnly 127 } 128 129 fun isInService(): Boolean { 130 return Utils.isInService(serviceState) 131 } 132 133 fun isRoaming(): Boolean { 134 return telephonyDisplayInfo != null && telephonyDisplayInfo.isRoaming 135 } 136 137 /** 138 * 139 * Load the (potentially customized) icon resource id for the current network type. Note that 140 * this operation caches the result. Note that reading the [MobileIconGroup.dataType] field 141 * directly will not yield correct results in cases where the carrierId has an associated 142 * override. This is the preferred method for getting the network type indicator. 143 * 144 * @return a drawable res id appropriate for the current (carrierId, networkType) pair 145 */ 146 @DrawableRes 147 fun getNetworkTypeIcon(context: Context): Int { 148 val icon = (iconGroup as MobileIconGroup) 149 return networkTypeResIdCache.get(icon, carrierId, context) 150 } 151 152 fun setFromMobileStatus(mobileStatus: MobileStatus) { 153 activityIn = mobileStatus.activityIn 154 activityOut = mobileStatus.activityOut 155 dataSim = mobileStatus.dataSim 156 carrierNetworkChangeMode = mobileStatus.carrierNetworkChangeMode 157 dataState = mobileStatus.dataState 158 signalStrength = mobileStatus.signalStrength 159 telephonyDisplayInfo = mobileStatus.telephonyDisplayInfo 160 serviceState = mobileStatus.serviceState 161 } 162 163 override fun toString(builder: StringBuilder) { 164 super.toString(builder) 165 builder.append(',') 166 builder.append("dataSim=$dataSim,") 167 builder.append("carrierId=$carrierId") 168 builder.append("networkName=$networkName,") 169 builder.append("networkNameData=$networkNameData,") 170 builder.append("dataConnected=$dataConnected,") 171 builder.append("roaming=$roaming,") 172 builder.append("isDefault=$isDefault,") 173 builder.append("isEmergency=$isEmergency,") 174 builder.append("airplaneMode=$airplaneMode,") 175 builder.append("carrierNetworkChangeMode=$carrierNetworkChangeMode,") 176 builder.append("userSetup=$userSetup,") 177 builder.append("dataState=$dataState,") 178 builder.append("defaultDataOff=$defaultDataOff,") 179 180 // Computed properties 181 builder.append("showQuickSettingsRatIcon=${showQuickSettingsRatIcon()},") 182 builder.append("voiceServiceState=${getVoiceServiceState()},") 183 builder.append("isInService=${isInService()},") 184 185 builder.append("networkTypeIconCache=$networkTypeResIdCache") 186 187 builder.append("serviceState=${serviceState?.minLog() ?: "(null)"},") 188 builder.append("signalStrength=${signalStrength?.minLog() ?: "(null)"},") 189 builder.append("displayInfo=$telephonyDisplayInfo") 190 } 191 192 override fun tableColumns(): List<String> { 193 val columns = listOf("dataSim", 194 "carrierId", 195 "networkName", 196 "networkNameData", 197 "dataConnected", 198 "roaming", 199 "isDefault", 200 "isEmergency", 201 "airplaneMode", 202 "carrierNetworkChangeMode", 203 "userSetup", 204 "dataState", 205 "defaultDataOff", 206 "showQuickSettingsRatIcon", 207 "voiceServiceState", 208 "isInService", 209 "networkTypeIconCache", 210 "serviceState", 211 "signalStrength", 212 "displayInfo") 213 214 return super.tableColumns() + columns 215 } 216 217 override fun tableData(): List<String> { 218 val columns = listOf(dataSim, 219 carrierId, 220 networkName, 221 networkNameData, 222 dataConnected, 223 roaming, 224 isDefault, 225 isEmergency, 226 airplaneMode, 227 carrierNetworkChangeMode, 228 userSetup, 229 dataState, 230 defaultDataOff, 231 showQuickSettingsRatIcon(), 232 getVoiceServiceState(), 233 isInService(), 234 networkTypeResIdCache, 235 serviceState?.minLog() ?: "(null)", 236 signalStrength?.minLog() ?: "(null)", 237 telephonyDisplayInfo).map { it.toString() } 238 239 return super.tableData() + columns 240 } 241 242 override fun equals(other: Any?): Boolean { 243 if (this === other) return true 244 if (javaClass != other?.javaClass) return false 245 if (!super.equals(other)) return false 246 247 other as MobileState 248 249 if (networkName != other.networkName) return false 250 if (networkNameData != other.networkNameData) return false 251 if (carrierId != other.carrierId) return false 252 if (dataSim != other.dataSim) return false 253 if (dataConnected != other.dataConnected) return false 254 if (isEmergency != other.isEmergency) return false 255 if (airplaneMode != other.airplaneMode) return false 256 if (carrierNetworkChangeMode != other.carrierNetworkChangeMode) return false 257 if (isDefault != other.isDefault) return false 258 if (userSetup != other.userSetup) return false 259 if (roaming != other.roaming) return false 260 if (dataState != other.dataState) return false 261 if (defaultDataOff != other.defaultDataOff) return false 262 if (telephonyDisplayInfo != other.telephonyDisplayInfo) return false 263 if (serviceState != other.serviceState) return false 264 if (signalStrength != other.signalStrength) return false 265 266 return true 267 } 268 269 override fun hashCode(): Int { 270 var result = super.hashCode() 271 result = 31 * result + (networkName?.hashCode() ?: 0) 272 result = 31 * result + (networkNameData?.hashCode() ?: 0) 273 result = 31 * result + (carrierId.hashCode()) 274 result = 31 * result + dataSim.hashCode() 275 result = 31 * result + dataConnected.hashCode() 276 result = 31 * result + isEmergency.hashCode() 277 result = 31 * result + airplaneMode.hashCode() 278 result = 31 * result + carrierNetworkChangeMode.hashCode() 279 result = 31 * result + isDefault.hashCode() 280 result = 31 * result + userSetup.hashCode() 281 result = 31 * result + roaming.hashCode() 282 result = 31 * result + dataState 283 result = 31 * result + defaultDataOff.hashCode() 284 result = 31 * result + telephonyDisplayInfo.hashCode() 285 result = 31 * result + (serviceState?.hashCode() ?: 0) 286 result = 31 * result + (signalStrength?.hashCode() ?: 0) 287 return result 288 } 289 } 290 291 /** toString() is a little more verbose than we need. Just log the fields we read */ 292 private fun ServiceState.minLog(): String { 293 return "serviceState={" + 294 "state=$state," + 295 "isEmergencyOnly=$isEmergencyOnly," + 296 "roaming=$roaming," + 297 "operatorNameAlphaShort=$operatorAlphaShort}" 298 } 299 300 private fun SignalStrength.minLog(): String { 301 return "signalStrength={" + 302 "isGsm=$isGsm," + 303 "level=$level}" 304 } 305