1 /* 2 * Copyright (C) 2015 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 package com.android.systemui.statusbar.policy; 17 18 import android.content.Context; 19 import android.content.Intent; 20 import android.net.NetworkCapabilities; 21 import android.os.Looper; 22 import android.telephony.PhoneStateListener; 23 import android.telephony.ServiceState; 24 import android.telephony.SignalStrength; 25 import android.telephony.SubscriptionInfo; 26 import android.telephony.SubscriptionManager; 27 import android.telephony.TelephonyManager; 28 import android.text.TextUtils; 29 import android.util.Log; 30 import android.util.SparseArray; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.internal.telephony.TelephonyIntents; 34 import com.android.internal.telephony.cdma.EriInfo; 35 import com.android.systemui.R; 36 import com.android.systemui.statusbar.policy.NetworkController.IconState; 37 import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; 38 import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults; 39 40 import java.io.PrintWriter; 41 import java.util.BitSet; 42 import java.util.Objects; 43 44 45 public class MobileSignalController extends SignalController< 46 MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> { 47 private final TelephonyManager mPhone; 48 private final SubscriptionDefaults mDefaults; 49 private final String mNetworkNameDefault; 50 private final String mNetworkNameSeparator; 51 @VisibleForTesting 52 final PhoneStateListener mPhoneStateListener; 53 // Save entire info for logging, we only use the id. 54 final SubscriptionInfo mSubscriptionInfo; 55 56 // @VisibleForDemoMode 57 final SparseArray<MobileIconGroup> mNetworkToIconLookup; 58 59 // Since some pieces of the phone state are interdependent we store it locally, 60 // this could potentially become part of MobileState for simplification/complication 61 // of code. 62 private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 63 private int mDataState = TelephonyManager.DATA_DISCONNECTED; 64 private ServiceState mServiceState; 65 private SignalStrength mSignalStrength; 66 private MobileIconGroup mDefaultIcons; 67 private Config mConfig; 68 69 // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't 70 // need listener lists anymore. MobileSignalController(Context context, Config config, boolean hasMobileData, TelephonyManager phone, CallbackHandler callbackHandler, NetworkControllerImpl networkController, SubscriptionInfo info, SubscriptionDefaults defaults, Looper receiverLooper)71 public MobileSignalController(Context context, Config config, boolean hasMobileData, 72 TelephonyManager phone, CallbackHandler callbackHandler, 73 NetworkControllerImpl networkController, SubscriptionInfo info, 74 SubscriptionDefaults defaults, Looper receiverLooper) { 75 super("MobileSignalController(" + info.getSubscriptionId() + ")", context, 76 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler, 77 networkController); 78 mNetworkToIconLookup = new SparseArray<>(); 79 mConfig = config; 80 mPhone = phone; 81 mDefaults = defaults; 82 mSubscriptionInfo = info; 83 mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId(), 84 receiverLooper); 85 mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator); 86 mNetworkNameDefault = getStringIfExists( 87 com.android.internal.R.string.lockscreen_carrier_default); 88 89 mapIconSets(); 90 91 String networkName = info.getCarrierName() != null ? info.getCarrierName().toString() 92 : mNetworkNameDefault; 93 mLastState.networkName = mCurrentState.networkName = networkName; 94 mLastState.networkNameData = mCurrentState.networkNameData = networkName; 95 mLastState.enabled = mCurrentState.enabled = hasMobileData; 96 mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons; 97 // Get initial data sim state. 98 updateDataSim(); 99 } 100 setConfiguration(Config config)101 public void setConfiguration(Config config) { 102 mConfig = config; 103 mapIconSets(); 104 updateTelephony(); 105 } 106 getDataContentDescription()107 public int getDataContentDescription() { 108 return getIcons().mDataContentDescription; 109 } 110 setAirplaneMode(boolean airplaneMode)111 public void setAirplaneMode(boolean airplaneMode) { 112 mCurrentState.airplaneMode = airplaneMode; 113 notifyListenersIfNecessary(); 114 } 115 116 @Override updateConnectivity(BitSet connectedTransports, BitSet validatedTransports)117 public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { 118 boolean isValidated = validatedTransports.get(mTransportType); 119 mCurrentState.isDefault = connectedTransports.get(mTransportType); 120 // Only show this as not having connectivity if we are default. 121 mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0; 122 notifyListenersIfNecessary(); 123 } 124 setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode)125 public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) { 126 mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode; 127 updateTelephony(); 128 } 129 130 /** 131 * Start listening for phone state changes. 132 */ registerListener()133 public void registerListener() { 134 mPhone.listen(mPhoneStateListener, 135 PhoneStateListener.LISTEN_SERVICE_STATE 136 | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS 137 | PhoneStateListener.LISTEN_CALL_STATE 138 | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE 139 | PhoneStateListener.LISTEN_DATA_ACTIVITY 140 | PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE); 141 } 142 143 /** 144 * Stop listening for phone state changes. 145 */ unregisterListener()146 public void unregisterListener() { 147 mPhone.listen(mPhoneStateListener, 0); 148 } 149 150 /** 151 * Produce a mapping of data network types to icon groups for simple and quick use in 152 * updateTelephony. 153 */ mapIconSets()154 private void mapIconSets() { 155 mNetworkToIconLookup.clear(); 156 157 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G); 158 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G); 159 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G); 160 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G); 161 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G); 162 163 if (!mConfig.showAtLeast3G) { 164 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN, 165 TelephonyIcons.UNKNOWN); 166 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E); 167 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X); 168 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X); 169 170 mDefaultIcons = TelephonyIcons.G; 171 } else { 172 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN, 173 TelephonyIcons.THREE_G); 174 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, 175 TelephonyIcons.THREE_G); 176 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, 177 TelephonyIcons.THREE_G); 178 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, 179 TelephonyIcons.THREE_G); 180 mDefaultIcons = TelephonyIcons.THREE_G; 181 } 182 183 MobileIconGroup hGroup = TelephonyIcons.THREE_G; 184 if (mConfig.hspaDataDistinguishable) { 185 hGroup = TelephonyIcons.H; 186 } 187 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup); 188 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup); 189 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup); 190 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup); 191 192 if (mConfig.show4gForLte) { 193 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G); 194 } else { 195 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE); 196 } 197 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC); 198 } 199 200 @Override notifyListeners()201 public void notifyListeners() { 202 MobileIconGroup icons = getIcons(); 203 204 String contentDescription = getStringIfExists(getContentDescription()); 205 String dataContentDescription = getStringIfExists(icons.mDataContentDescription); 206 207 // Show icon in QS when we are connected or need to show roaming. 208 boolean showDataIcon = mCurrentState.dataConnected 209 || mCurrentState.iconGroup == TelephonyIcons.ROAMING; 210 IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode, 211 getCurrentIconId(), contentDescription); 212 213 int qsTypeIcon = 0; 214 IconState qsIcon = null; 215 String description = null; 216 // Only send data sim callbacks to QS. 217 if (mCurrentState.dataSim) { 218 qsTypeIcon = showDataIcon ? icons.mQsDataType : 0; 219 qsIcon = new IconState(mCurrentState.enabled 220 && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription); 221 description = mCurrentState.isEmergency ? null : mCurrentState.networkName; 222 } 223 boolean activityIn = mCurrentState.dataConnected 224 && !mCurrentState.carrierNetworkChangeMode 225 && mCurrentState.activityIn; 226 boolean activityOut = mCurrentState.dataConnected 227 && !mCurrentState.carrierNetworkChangeMode 228 && mCurrentState.activityOut; 229 showDataIcon &= mCurrentState.isDefault 230 || mCurrentState.iconGroup == TelephonyIcons.ROAMING; 231 int typeIcon = showDataIcon ? icons.mDataType : 0; 232 mCallbackHandler.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, 233 activityIn, activityOut, dataContentDescription, description, icons.mIsWide, 234 mSubscriptionInfo.getSubscriptionId()); 235 } 236 237 @Override cleanState()238 protected MobileState cleanState() { 239 return new MobileState(); 240 } 241 hasService()242 private boolean hasService() { 243 if (mServiceState != null) { 244 // Consider the device to be in service if either voice or data 245 // service is available. Some SIM cards are marketed as data-only 246 // and do not support voice service, and on these SIM cards, we 247 // want to show signal bars for data service as well as the "no 248 // service" or "emergency calls only" text that indicates that voice 249 // is not available. 250 switch (mServiceState.getVoiceRegState()) { 251 case ServiceState.STATE_POWER_OFF: 252 return false; 253 case ServiceState.STATE_OUT_OF_SERVICE: 254 case ServiceState.STATE_EMERGENCY_ONLY: 255 return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE; 256 default: 257 return true; 258 } 259 } else { 260 return false; 261 } 262 } 263 isCdma()264 private boolean isCdma() { 265 return (mSignalStrength != null) && !mSignalStrength.isGsm(); 266 } 267 isEmergencyOnly()268 public boolean isEmergencyOnly() { 269 return (mServiceState != null && mServiceState.isEmergencyOnly()); 270 } 271 isRoaming()272 private boolean isRoaming() { 273 if (isCdma()) { 274 final int iconMode = mServiceState.getCdmaEriIconMode(); 275 return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF 276 && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL 277 || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH); 278 } else { 279 return mServiceState != null && mServiceState.getRoaming(); 280 } 281 } 282 isCarrierNetworkChangeActive()283 private boolean isCarrierNetworkChangeActive() { 284 return mCurrentState.carrierNetworkChangeMode; 285 } 286 handleBroadcast(Intent intent)287 public void handleBroadcast(Intent intent) { 288 String action = intent.getAction(); 289 if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { 290 updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), 291 intent.getStringExtra(TelephonyIntents.EXTRA_SPN), 292 intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN), 293 intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), 294 intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); 295 notifyListenersIfNecessary(); 296 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { 297 updateDataSim(); 298 notifyListenersIfNecessary(); 299 } 300 } 301 updateDataSim()302 private void updateDataSim() { 303 int defaultDataSub = mDefaults.getDefaultDataSubId(); 304 if (SubscriptionManager.isValidSubscriptionId(defaultDataSub)) { 305 mCurrentState.dataSim = defaultDataSub == mSubscriptionInfo.getSubscriptionId(); 306 } else { 307 // There doesn't seem to be a data sim selected, however if 308 // there isn't a MobileSignalController with dataSim set, then 309 // QS won't get any callbacks and will be blank. Instead 310 // lets just assume we are the data sim (which will basically 311 // show one at random) in QS until one is selected. The user 312 // should pick one soon after, so we shouldn't be in this state 313 // for long. 314 mCurrentState.dataSim = true; 315 } 316 } 317 318 /** 319 * Updates the network's name based on incoming spn and plmn. 320 */ updateNetworkName(boolean showSpn, String spn, String dataSpn, boolean showPlmn, String plmn)321 void updateNetworkName(boolean showSpn, String spn, String dataSpn, 322 boolean showPlmn, String plmn) { 323 if (CHATTY) { 324 Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn 325 + " spn=" + spn + " dataSpn=" + dataSpn 326 + " showPlmn=" + showPlmn + " plmn=" + plmn); 327 } 328 StringBuilder str = new StringBuilder(); 329 StringBuilder strData = new StringBuilder(); 330 if (showPlmn && plmn != null) { 331 str.append(plmn); 332 strData.append(plmn); 333 } 334 if (showSpn && spn != null) { 335 if (str.length() != 0) { 336 str.append(mNetworkNameSeparator); 337 } 338 str.append(spn); 339 } 340 if (str.length() != 0) { 341 mCurrentState.networkName = str.toString(); 342 } else { 343 mCurrentState.networkName = mNetworkNameDefault; 344 } 345 if (showSpn && dataSpn != null) { 346 if (strData.length() != 0) { 347 strData.append(mNetworkNameSeparator); 348 } 349 strData.append(dataSpn); 350 } 351 if (strData.length() != 0) { 352 mCurrentState.networkNameData = strData.toString(); 353 } else { 354 mCurrentState.networkNameData = mNetworkNameDefault; 355 } 356 } 357 358 /** 359 * Updates the current state based on mServiceState, mSignalStrength, mDataNetType, 360 * mDataState, and mSimState. It should be called any time one of these is updated. 361 * This will call listeners if necessary. 362 */ updateTelephony()363 private final void updateTelephony() { 364 if (DEBUG) { 365 Log.d(mTag, "updateTelephonySignalStrength: hasService=" + hasService() 366 + " ss=" + mSignalStrength); 367 } 368 mCurrentState.connected = hasService() && mSignalStrength != null; 369 if (mCurrentState.connected) { 370 if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) { 371 mCurrentState.level = mSignalStrength.getCdmaLevel(); 372 } else { 373 mCurrentState.level = mSignalStrength.getLevel(); 374 } 375 } 376 if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) { 377 mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType); 378 } else { 379 mCurrentState.iconGroup = mDefaultIcons; 380 } 381 mCurrentState.dataConnected = mCurrentState.connected 382 && mDataState == TelephonyManager.DATA_CONNECTED; 383 384 if (isCarrierNetworkChangeActive()) { 385 mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE; 386 } else if (isRoaming()) { 387 mCurrentState.iconGroup = TelephonyIcons.ROAMING; 388 } 389 if (isEmergencyOnly() != mCurrentState.isEmergency) { 390 mCurrentState.isEmergency = isEmergencyOnly(); 391 mNetworkController.recalculateEmergency(); 392 } 393 // Fill in the network name if we think we have it. 394 if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null 395 && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) { 396 mCurrentState.networkName = mServiceState.getOperatorAlphaShort(); 397 } 398 399 notifyListenersIfNecessary(); 400 } 401 402 @VisibleForTesting setActivity(int activity)403 void setActivity(int activity) { 404 mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT 405 || activity == TelephonyManager.DATA_ACTIVITY_IN; 406 mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT 407 || activity == TelephonyManager.DATA_ACTIVITY_OUT; 408 notifyListenersIfNecessary(); 409 } 410 411 @Override dump(PrintWriter pw)412 public void dump(PrintWriter pw) { 413 super.dump(pw); 414 pw.println(" mSubscription=" + mSubscriptionInfo + ","); 415 pw.println(" mServiceState=" + mServiceState + ","); 416 pw.println(" mSignalStrength=" + mSignalStrength + ","); 417 pw.println(" mDataState=" + mDataState + ","); 418 pw.println(" mDataNetType=" + mDataNetType + ","); 419 } 420 421 class MobilePhoneStateListener extends PhoneStateListener { MobilePhoneStateListener(int subId, Looper looper)422 public MobilePhoneStateListener(int subId, Looper looper) { 423 super(subId, looper); 424 } 425 426 @Override onSignalStrengthsChanged(SignalStrength signalStrength)427 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 428 if (DEBUG) { 429 Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength + 430 ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); 431 } 432 mSignalStrength = signalStrength; 433 updateTelephony(); 434 } 435 436 @Override onServiceStateChanged(ServiceState state)437 public void onServiceStateChanged(ServiceState state) { 438 if (DEBUG) { 439 Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState() 440 + " dataState=" + state.getDataRegState()); 441 } 442 mServiceState = state; 443 updateTelephony(); 444 } 445 446 @Override onDataConnectionStateChanged(int state, int networkType)447 public void onDataConnectionStateChanged(int state, int networkType) { 448 if (DEBUG) { 449 Log.d(mTag, "onDataConnectionStateChanged: state=" + state 450 + " type=" + networkType); 451 } 452 mDataState = state; 453 mDataNetType = networkType; 454 updateTelephony(); 455 } 456 457 @Override onDataActivity(int direction)458 public void onDataActivity(int direction) { 459 if (DEBUG) { 460 Log.d(mTag, "onDataActivity: direction=" + direction); 461 } 462 setActivity(direction); 463 } 464 465 @Override onCarrierNetworkChange(boolean active)466 public void onCarrierNetworkChange(boolean active) { 467 if (DEBUG) { 468 Log.d(mTag, "onCarrierNetworkChange: active=" + active); 469 } 470 mCurrentState.carrierNetworkChangeMode = active; 471 472 updateTelephony(); 473 } 474 }; 475 476 static class MobileIconGroup extends SignalController.IconGroup { 477 final int mDataContentDescription; // mContentDescriptionDataType 478 final int mDataType; 479 final boolean mIsWide; 480 final int mQsDataType; 481 MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, int discContentDesc, int dataContentDesc, int dataType, boolean isWide, int qsDataType)482 public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, 483 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, 484 int discContentDesc, int dataContentDesc, int dataType, boolean isWide, 485 int qsDataType) { 486 super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState, 487 qsDiscState, discContentDesc); 488 mDataContentDescription = dataContentDesc; 489 mDataType = dataType; 490 mIsWide = isWide; 491 mQsDataType = qsDataType; 492 } 493 } 494 495 static class MobileState extends SignalController.State { 496 String networkName; 497 String networkNameData; 498 boolean dataSim; 499 boolean dataConnected; 500 boolean isEmergency; 501 boolean airplaneMode; 502 boolean carrierNetworkChangeMode; 503 boolean isDefault; 504 505 @Override copyFrom(State s)506 public void copyFrom(State s) { 507 super.copyFrom(s); 508 MobileState state = (MobileState) s; 509 dataSim = state.dataSim; 510 networkName = state.networkName; 511 networkNameData = state.networkNameData; 512 dataConnected = state.dataConnected; 513 isDefault = state.isDefault; 514 isEmergency = state.isEmergency; 515 airplaneMode = state.airplaneMode; 516 carrierNetworkChangeMode = state.carrierNetworkChangeMode; 517 } 518 519 @Override toString(StringBuilder builder)520 protected void toString(StringBuilder builder) { 521 super.toString(builder); 522 builder.append(','); 523 builder.append("dataSim=").append(dataSim).append(','); 524 builder.append("networkName=").append(networkName).append(','); 525 builder.append("networkNameData=").append(networkNameData).append(','); 526 builder.append("dataConnected=").append(dataConnected).append(','); 527 builder.append("isDefault=").append(isDefault).append(','); 528 builder.append("isEmergency=").append(isEmergency).append(','); 529 builder.append("airplaneMode=").append(airplaneMode).append(','); 530 builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode); 531 } 532 533 @Override equals(Object o)534 public boolean equals(Object o) { 535 return super.equals(o) 536 && Objects.equals(((MobileState) o).networkName, networkName) 537 && Objects.equals(((MobileState) o).networkNameData, networkNameData) 538 && ((MobileState) o).dataSim == dataSim 539 && ((MobileState) o).dataConnected == dataConnected 540 && ((MobileState) o).isEmergency == isEmergency 541 && ((MobileState) o).airplaneMode == airplaneMode 542 && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode 543 && ((MobileState) o).isDefault == isDefault; 544 } 545 } 546 } 547