1 /* 2 * Copyright (C) 2022 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.pipeline.wifi.shared.model 18 19 import android.telephony.SubscriptionManager 20 import androidx.annotation.VisibleForTesting 21 import com.android.systemui.log.table.Diffable 22 import com.android.systemui.log.table.TableRowLogger 23 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository 24 25 /** Provides information about the current wifi network. */ 26 sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { 27 28 // TODO(b/238425913): Have a better, more unified strategy for diff-logging instead of 29 // copy-pasting the column names for each sub-object. 30 31 /** 32 * A model representing that we couldn't fetch any wifi information. 33 * 34 * This is only used with [DisabledWifiRepository], where [WifiManager] is null. 35 */ 36 object Unavailable : WifiNetworkModel() { toStringnull37 override fun toString() = "WifiNetwork.Unavailable" 38 override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) { 39 if (prevVal is Unavailable) { 40 return 41 } 42 43 logFull(row) 44 } 45 logFullnull46 override fun logFull(row: TableRowLogger) { 47 row.logChange(COL_NETWORK_TYPE, TYPE_UNAVAILABLE) 48 row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT) 49 row.logChange(COL_SUB_ID, SUB_ID_DEFAULT) 50 row.logChange(COL_VALIDATED, false) 51 row.logChange(COL_LEVEL, LEVEL_DEFAULT) 52 row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT) 53 row.logChange(COL_SSID, null) 54 row.logChange(COL_PASSPOINT_ACCESS_POINT, false) 55 row.logChange(COL_ONLINE_SIGN_UP, false) 56 row.logChange(COL_PASSPOINT_NAME, null) 57 } 58 } 59 60 /** A model representing that the wifi information we received was invalid in some way. */ 61 data class Invalid( 62 /** A description of why the wifi information was invalid. */ 63 val invalidReason: String, 64 ) : WifiNetworkModel() { toStringnull65 override fun toString() = "WifiNetwork.Invalid[$invalidReason]" 66 override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) { 67 if (prevVal !is Invalid) { 68 logFull(row) 69 return 70 } 71 72 if (invalidReason != prevVal.invalidReason) { 73 row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason") 74 } 75 } 76 logFullnull77 override fun logFull(row: TableRowLogger) { 78 row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason") 79 row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT) 80 row.logChange(COL_SUB_ID, SUB_ID_DEFAULT) 81 row.logChange(COL_VALIDATED, false) 82 row.logChange(COL_LEVEL, LEVEL_DEFAULT) 83 row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT) 84 row.logChange(COL_SSID, null) 85 row.logChange(COL_PASSPOINT_ACCESS_POINT, false) 86 row.logChange(COL_ONLINE_SIGN_UP, false) 87 row.logChange(COL_PASSPOINT_NAME, null) 88 } 89 } 90 91 /** A model representing that we have no active wifi network. */ 92 object Inactive : WifiNetworkModel() { toStringnull93 override fun toString() = "WifiNetwork.Inactive" 94 95 override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) { 96 if (prevVal is Inactive) { 97 return 98 } 99 100 // When changing to Inactive, we need to log diffs to all the fields. 101 logFull(row) 102 } 103 logFullnull104 override fun logFull(row: TableRowLogger) { 105 row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE) 106 row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT) 107 row.logChange(COL_SUB_ID, SUB_ID_DEFAULT) 108 row.logChange(COL_VALIDATED, false) 109 row.logChange(COL_LEVEL, LEVEL_DEFAULT) 110 row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT) 111 row.logChange(COL_SSID, null) 112 row.logChange(COL_PASSPOINT_ACCESS_POINT, false) 113 row.logChange(COL_ONLINE_SIGN_UP, false) 114 row.logChange(COL_PASSPOINT_NAME, null) 115 } 116 } 117 118 /** 119 * A model representing that our wifi network is actually a carrier merged network, meaning it's 120 * treated as more of a mobile network. 121 * 122 * See [android.net.wifi.WifiInfo.isCarrierMerged] for more information. 123 */ 124 data class CarrierMerged( 125 /** 126 * The [android.net.Network.netId] we received from 127 * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network. 128 * 129 * Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId]. 130 */ 131 val networkId: Int, 132 133 /** 134 * The subscription ID that this connection represents. 135 * 136 * Comes from [android.net.wifi.WifiInfo.getSubscriptionId]. 137 * 138 * Per that method, this value must not be [INVALID_SUBSCRIPTION_ID] (if it was invalid, 139 * then this is *not* a carrier merged network). 140 */ 141 val subscriptionId: Int, 142 143 /** The signal level, guaranteed to be 0 <= level <= numberOfLevels. */ 144 val level: Int, 145 146 /** The maximum possible level. */ 147 val numberOfLevels: Int = MobileConnectionRepository.DEFAULT_NUM_LEVELS, 148 ) : WifiNetworkModel() { 149 init { <lambda>null150 require(level in MIN_VALID_LEVEL..numberOfLevels) { 151 "0 <= wifi level <= $numberOfLevels required; level was $level" 152 } <lambda>null153 require(subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 154 "subscription ID cannot be invalid" 155 } 156 } 157 logDiffsnull158 override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) { 159 if (prevVal !is CarrierMerged) { 160 logFull(row) 161 return 162 } 163 164 if (prevVal.networkId != networkId) { 165 row.logChange(COL_NETWORK_ID, networkId) 166 } 167 if (prevVal.subscriptionId != subscriptionId) { 168 row.logChange(COL_SUB_ID, subscriptionId) 169 } 170 if (prevVal.level != level) { 171 row.logChange(COL_LEVEL, level) 172 } 173 if (prevVal.numberOfLevels != numberOfLevels) { 174 row.logChange(COL_NUM_LEVELS, numberOfLevels) 175 } 176 } 177 logFullnull178 override fun logFull(row: TableRowLogger) { 179 row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED) 180 row.logChange(COL_NETWORK_ID, networkId) 181 row.logChange(COL_SUB_ID, subscriptionId) 182 row.logChange(COL_VALIDATED, true) 183 row.logChange(COL_LEVEL, level) 184 row.logChange(COL_NUM_LEVELS, numberOfLevels) 185 row.logChange(COL_SSID, null) 186 row.logChange(COL_PASSPOINT_ACCESS_POINT, false) 187 row.logChange(COL_ONLINE_SIGN_UP, false) 188 row.logChange(COL_PASSPOINT_NAME, null) 189 } 190 } 191 192 /** Provides information about an active wifi network. */ 193 data class Active( 194 /** 195 * The [android.net.Network.netId] we received from 196 * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network. 197 * 198 * Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId]. 199 */ 200 val networkId: Int, 201 202 /** See [android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED]. */ 203 val isValidated: Boolean = false, 204 205 /** The wifi signal level, guaranteed to be 0 <= level <= 4. */ 206 val level: Int, 207 208 /** See [android.net.wifi.WifiInfo.ssid]. */ 209 val ssid: String? = null, 210 211 /** See [android.net.wifi.WifiInfo.isPasspointAp]. */ 212 val isPasspointAccessPoint: Boolean = false, 213 214 /** See [android.net.wifi.WifiInfo.isOsuAp]. */ 215 val isOnlineSignUpForPasspointAccessPoint: Boolean = false, 216 217 /** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */ 218 val passpointProviderFriendlyName: String? = null, 219 ) : WifiNetworkModel() { 220 init { <lambda>null221 require(level in MIN_VALID_LEVEL..MAX_VALID_LEVEL) { 222 "0 <= wifi level <= 4 required; level was $level" 223 } 224 } 225 logDiffsnull226 override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) { 227 if (prevVal !is Active) { 228 logFull(row) 229 return 230 } 231 232 if (prevVal.networkId != networkId) { 233 row.logChange(COL_NETWORK_ID, networkId) 234 } 235 if (prevVal.isValidated != isValidated) { 236 row.logChange(COL_VALIDATED, isValidated) 237 } 238 if (prevVal.level != level) { 239 row.logChange(COL_LEVEL, level) 240 } 241 if (prevVal.ssid != ssid) { 242 row.logChange(COL_SSID, ssid) 243 } 244 245 // TODO(b/238425913): The passpoint-related values are frequently never used, so it 246 // would be great to not log them when they're not used. 247 if (prevVal.isPasspointAccessPoint != isPasspointAccessPoint) { 248 row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint) 249 } 250 if ( 251 prevVal.isOnlineSignUpForPasspointAccessPoint != 252 isOnlineSignUpForPasspointAccessPoint 253 ) { 254 row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint) 255 } 256 if (prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) { 257 row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName) 258 } 259 } 260 logFullnull261 override fun logFull(row: TableRowLogger) { 262 row.logChange(COL_NETWORK_TYPE, TYPE_ACTIVE) 263 row.logChange(COL_NETWORK_ID, networkId) 264 row.logChange(COL_SUB_ID, null) 265 row.logChange(COL_VALIDATED, isValidated) 266 row.logChange(COL_LEVEL, level) 267 row.logChange(COL_NUM_LEVELS, null) 268 row.logChange(COL_SSID, ssid) 269 row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint) 270 row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint) 271 row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName) 272 } 273 toStringnull274 override fun toString(): String { 275 // Only include the passpoint-related values in the string if we have them. (Most 276 // networks won't have them so they'll be mostly clutter.) 277 val passpointString = 278 if ( 279 isPasspointAccessPoint || 280 isOnlineSignUpForPasspointAccessPoint || 281 passpointProviderFriendlyName != null 282 ) { 283 ", isPasspointAp=$isPasspointAccessPoint, " + 284 "isOnlineSignUpForPasspointAp=$isOnlineSignUpForPasspointAccessPoint, " + 285 "passpointName=$passpointProviderFriendlyName" 286 } else { 287 "" 288 } 289 290 return "WifiNetworkModel.Active(networkId=$networkId, isValidated=$isValidated, " + 291 "level=$level, ssid=$ssid$passpointString)" 292 } 293 294 companion object { 295 @VisibleForTesting internal const val MAX_VALID_LEVEL = 4 296 } 297 } 298 299 companion object { 300 @VisibleForTesting internal const val MIN_VALID_LEVEL = 0 301 } 302 } 303 304 const val TYPE_CARRIER_MERGED = "CarrierMerged" 305 const val TYPE_UNAVAILABLE = "Unavailable" 306 const val TYPE_INACTIVE = "Inactive" 307 const val TYPE_ACTIVE = "Active" 308 309 const val COL_NETWORK_TYPE = "type" 310 const val COL_NETWORK_ID = "networkId" 311 const val COL_SUB_ID = "subscriptionId" 312 const val COL_VALIDATED = "isValidated" 313 const val COL_LEVEL = "level" 314 const val COL_NUM_LEVELS = "maxLevel" 315 const val COL_SSID = "ssid" 316 const val COL_PASSPOINT_ACCESS_POINT = "isPasspointAccessPoint" 317 const val COL_ONLINE_SIGN_UP = "isOnlineSignUpForPasspointAccessPoint" 318 const val COL_PASSPOINT_NAME = "passpointProviderFriendlyName" 319 320 val LEVEL_DEFAULT: String? = null 321 val NUM_LEVELS_DEFAULT: String? = null 322 val NETWORK_ID_DEFAULT: String? = null 323 val SUB_ID_DEFAULT: String? = null 324