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 */
hasActivityInnull67 fun hasActivityIn(): Boolean {
68 return dataConnected && !carrierNetworkChangeMode && activityIn
69 }
70
71 /** @return if this state is considered to have outbound activity */
hasActivityOutnull72 fun hasActivityOut(): Boolean {
73 return dataConnected && !carrierNetworkChangeMode && activityOut
74 }
75
76 /** @return true if this state should show a RAT icon in quick settings */
showQuickSettingsRatIconnull77 fun showQuickSettingsRatIcon(): Boolean {
78 return dataConnected || isDataDisabledOrNotDefault
79 }
80
copyFromnull81 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
isDataConnectednull104 fun isDataConnected(): Boolean {
105 return connected && dataState == TelephonyManager.DATA_CONNECTED
106 }
107
108 /** @return the current voice service state, or -1 if null */
getVoiceServiceStatenull109 fun getVoiceServiceState(): Int {
110 return serviceState?.state ?: -1
111 }
112
isNoCallingnull113 fun isNoCalling(): Boolean {
114 return serviceState?.state != ServiceState.STATE_IN_SERVICE
115 }
116
getOperatorAlphaShortnull117 fun getOperatorAlphaShort(): String {
118 return serviceState?.operatorAlphaShort ?: ""
119 }
120
isCdmanull121 fun isCdma(): Boolean {
122 return signalStrength != null && !signalStrength!!.isGsm
123 }
124
isEmergencyOnlynull125 fun isEmergencyOnly(): Boolean {
126 return serviceState != null && serviceState!!.isEmergencyOnly
127 }
128
isInServicenull129 fun isInService(): Boolean {
130 return Utils.isInService(serviceState)
131 }
132
isRoamingnull133 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
getNetworkTypeIconnull147 fun getNetworkTypeIcon(context: Context): Int {
148 val icon = (iconGroup as MobileIconGroup)
149 return networkTypeResIdCache.get(icon, carrierId, context)
150 }
151
setFromMobileStatusnull152 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
toStringnull163 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
tableColumnsnull192 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
tableDatanull217 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
equalsnull242 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
hashCodenull269 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 */
ServiceStatenull292 private fun ServiceState.minLog(): String {
293 return "serviceState={" +
294 "state=$state," +
295 "isEmergencyOnly=$isEmergencyOnly," +
296 "roaming=$roaming," +
297 "operatorNameAlphaShort=$operatorAlphaShort}"
298 }
299
minLognull300 private fun SignalStrength.minLog(): String {
301 return "signalStrength={" +
302 "isGsm=$isGsm," +
303 "level=$level}"
304 }
305