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