MobileSignalController.java revision 1a95c86281bc9c2c187b3f4df19d2805595b6f26
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 */ 16package com.android.systemui.statusbar.policy; 17 18import android.content.Context; 19import android.content.Intent; 20import android.net.NetworkCapabilities; 21import android.os.Looper; 22import android.telephony.PhoneStateListener; 23import android.telephony.ServiceState; 24import android.telephony.SignalStrength; 25import android.telephony.SubscriptionInfo; 26import android.telephony.SubscriptionManager; 27import android.telephony.TelephonyManager; 28import android.text.TextUtils; 29import android.util.Log; 30import android.util.SparseArray; 31 32import com.android.internal.annotations.VisibleForTesting; 33import com.android.internal.telephony.TelephonyIntents; 34import com.android.internal.telephony.cdma.EriInfo; 35import com.android.systemui.R; 36import com.android.systemui.statusbar.policy.NetworkController.IconState; 37import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; 38import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults; 39 40import java.io.PrintWriter; 41import java.util.BitSet; 42import java.util.Objects; 43 44 45public 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. 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 101 public void setConfiguration(Config config) { 102 mConfig = config; 103 mapIconSets(); 104 updateTelephony(); 105 } 106 107 public int getDataContentDescription() { 108 return getIcons().mDataContentDescription; 109 } 110 111 public void setAirplaneMode(boolean airplaneMode) { 112 mCurrentState.airplaneMode = airplaneMode; 113 notifyListenersIfNecessary(); 114 } 115 116 @Override 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 125 public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) { 126 mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode; 127 updateTelephony(); 128 } 129 130 /** 131 * Start listening for phone state changes. 132 */ 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 */ 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 */ 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 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 || mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED; 211 IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode, 212 getCurrentIconId(), contentDescription); 213 214 int qsTypeIcon = 0; 215 IconState qsIcon = null; 216 String description = null; 217 // Only send data sim callbacks to QS. 218 if (mCurrentState.dataSim) { 219 qsTypeIcon = showDataIcon ? icons.mQsDataType : 0; 220 qsIcon = new IconState(mCurrentState.enabled 221 && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription); 222 description = mCurrentState.isEmergency ? null : mCurrentState.networkName; 223 } 224 boolean activityIn = mCurrentState.dataConnected 225 && !mCurrentState.carrierNetworkChangeMode 226 && mCurrentState.activityIn; 227 boolean activityOut = mCurrentState.dataConnected 228 && !mCurrentState.carrierNetworkChangeMode 229 && mCurrentState.activityOut; 230 showDataIcon &= mCurrentState.isDefault 231 || mCurrentState.iconGroup == TelephonyIcons.ROAMING 232 || mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED; 233 int typeIcon = showDataIcon ? icons.mDataType : 0; 234 mCallbackHandler.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, 235 activityIn, activityOut, dataContentDescription, description, icons.mIsWide, 236 mSubscriptionInfo.getSubscriptionId()); 237 } 238 239 @Override 240 protected MobileState cleanState() { 241 return new MobileState(); 242 } 243 244 private boolean hasService() { 245 if (mServiceState != null) { 246 // Consider the device to be in service if either voice or data 247 // service is available. Some SIM cards are marketed as data-only 248 // and do not support voice service, and on these SIM cards, we 249 // want to show signal bars for data service as well as the "no 250 // service" or "emergency calls only" text that indicates that voice 251 // is not available. 252 switch (mServiceState.getVoiceRegState()) { 253 case ServiceState.STATE_POWER_OFF: 254 return false; 255 case ServiceState.STATE_OUT_OF_SERVICE: 256 case ServiceState.STATE_EMERGENCY_ONLY: 257 return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE; 258 default: 259 return true; 260 } 261 } else { 262 return false; 263 } 264 } 265 266 private boolean isCdma() { 267 return (mSignalStrength != null) && !mSignalStrength.isGsm(); 268 } 269 270 public boolean isEmergencyOnly() { 271 return (mServiceState != null && mServiceState.isEmergencyOnly()); 272 } 273 274 private boolean isRoaming() { 275 if (isCdma()) { 276 final int iconMode = mServiceState.getCdmaEriIconMode(); 277 return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF 278 && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL 279 || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH); 280 } else { 281 return mServiceState != null && mServiceState.getRoaming(); 282 } 283 } 284 285 private boolean isCarrierNetworkChangeActive() { 286 return mCurrentState.carrierNetworkChangeMode; 287 } 288 289 public void handleBroadcast(Intent intent) { 290 String action = intent.getAction(); 291 if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { 292 updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), 293 intent.getStringExtra(TelephonyIntents.EXTRA_SPN), 294 intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN), 295 intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), 296 intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); 297 notifyListenersIfNecessary(); 298 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { 299 updateDataSim(); 300 notifyListenersIfNecessary(); 301 } 302 } 303 304 private void updateDataSim() { 305 int defaultDataSub = mDefaults.getDefaultDataSubId(); 306 if (SubscriptionManager.isValidSubscriptionId(defaultDataSub)) { 307 mCurrentState.dataSim = defaultDataSub == mSubscriptionInfo.getSubscriptionId(); 308 } else { 309 // There doesn't seem to be a data sim selected, however if 310 // there isn't a MobileSignalController with dataSim set, then 311 // QS won't get any callbacks and will be blank. Instead 312 // lets just assume we are the data sim (which will basically 313 // show one at random) in QS until one is selected. The user 314 // should pick one soon after, so we shouldn't be in this state 315 // for long. 316 mCurrentState.dataSim = true; 317 } 318 } 319 320 /** 321 * Updates the network's name based on incoming spn and plmn. 322 */ 323 void updateNetworkName(boolean showSpn, String spn, String dataSpn, 324 boolean showPlmn, String plmn) { 325 if (CHATTY) { 326 Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn 327 + " spn=" + spn + " dataSpn=" + dataSpn 328 + " showPlmn=" + showPlmn + " plmn=" + plmn); 329 } 330 StringBuilder str = new StringBuilder(); 331 StringBuilder strData = new StringBuilder(); 332 if (showPlmn && plmn != null) { 333 str.append(plmn); 334 strData.append(plmn); 335 } 336 if (showSpn && spn != null) { 337 if (str.length() != 0) { 338 str.append(mNetworkNameSeparator); 339 } 340 str.append(spn); 341 } 342 if (str.length() != 0) { 343 mCurrentState.networkName = str.toString(); 344 } else { 345 mCurrentState.networkName = mNetworkNameDefault; 346 } 347 if (showSpn && dataSpn != null) { 348 if (strData.length() != 0) { 349 strData.append(mNetworkNameSeparator); 350 } 351 strData.append(dataSpn); 352 } 353 if (strData.length() != 0) { 354 mCurrentState.networkNameData = strData.toString(); 355 } else { 356 mCurrentState.networkNameData = mNetworkNameDefault; 357 } 358 } 359 360 /** 361 * Updates the current state based on mServiceState, mSignalStrength, mDataNetType, 362 * mDataState, and mSimState. It should be called any time one of these is updated. 363 * This will call listeners if necessary. 364 */ 365 private final void updateTelephony() { 366 if (DEBUG) { 367 Log.d(mTag, "updateTelephonySignalStrength: hasService=" + hasService() 368 + " ss=" + mSignalStrength); 369 } 370 mCurrentState.connected = hasService() && mSignalStrength != null; 371 if (mCurrentState.connected) { 372 if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) { 373 mCurrentState.level = mSignalStrength.getCdmaLevel(); 374 } else { 375 mCurrentState.level = mSignalStrength.getLevel(); 376 } 377 } 378 if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) { 379 mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType); 380 } else { 381 mCurrentState.iconGroup = mDefaultIcons; 382 } 383 mCurrentState.dataConnected = mCurrentState.connected 384 && mDataState == TelephonyManager.DATA_CONNECTED; 385 386 if (isCarrierNetworkChangeActive()) { 387 mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE; 388 } else if (isRoaming()) { 389 mCurrentState.iconGroup = TelephonyIcons.ROAMING; 390 } else if (isDataDisabled()) { 391 mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED; 392 } 393 if (isEmergencyOnly() != mCurrentState.isEmergency) { 394 mCurrentState.isEmergency = isEmergencyOnly(); 395 mNetworkController.recalculateEmergency(); 396 } 397 // Fill in the network name if we think we have it. 398 if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null 399 && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) { 400 mCurrentState.networkName = mServiceState.getOperatorAlphaShort(); 401 } 402 403 notifyListenersIfNecessary(); 404 } 405 406 private boolean isDataDisabled() { 407 return !mPhone.getDataEnabled(mSubscriptionInfo.getSubscriptionId()); 408 } 409 410 @VisibleForTesting 411 void setActivity(int activity) { 412 mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT 413 || activity == TelephonyManager.DATA_ACTIVITY_IN; 414 mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT 415 || activity == TelephonyManager.DATA_ACTIVITY_OUT; 416 notifyListenersIfNecessary(); 417 } 418 419 @Override 420 public void dump(PrintWriter pw) { 421 super.dump(pw); 422 pw.println(" mSubscription=" + mSubscriptionInfo + ","); 423 pw.println(" mServiceState=" + mServiceState + ","); 424 pw.println(" mSignalStrength=" + mSignalStrength + ","); 425 pw.println(" mDataState=" + mDataState + ","); 426 pw.println(" mDataNetType=" + mDataNetType + ","); 427 } 428 429 class MobilePhoneStateListener extends PhoneStateListener { 430 public MobilePhoneStateListener(int subId, Looper looper) { 431 super(subId, looper); 432 } 433 434 @Override 435 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 436 if (DEBUG) { 437 Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength + 438 ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); 439 } 440 mSignalStrength = signalStrength; 441 updateTelephony(); 442 } 443 444 @Override 445 public void onServiceStateChanged(ServiceState state) { 446 if (DEBUG) { 447 Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState() 448 + " dataState=" + state.getDataRegState()); 449 } 450 mServiceState = state; 451 mDataNetType = state.getDataNetworkType(); 452 updateTelephony(); 453 } 454 455 @Override 456 public void onDataConnectionStateChanged(int state, int networkType) { 457 if (DEBUG) { 458 Log.d(mTag, "onDataConnectionStateChanged: state=" + state 459 + " type=" + networkType); 460 } 461 mDataState = state; 462 mDataNetType = networkType; 463 updateTelephony(); 464 } 465 466 @Override 467 public void onDataActivity(int direction) { 468 if (DEBUG) { 469 Log.d(mTag, "onDataActivity: direction=" + direction); 470 } 471 setActivity(direction); 472 } 473 474 @Override 475 public void onCarrierNetworkChange(boolean active) { 476 if (DEBUG) { 477 Log.d(mTag, "onCarrierNetworkChange: active=" + active); 478 } 479 mCurrentState.carrierNetworkChangeMode = active; 480 481 updateTelephony(); 482 } 483 }; 484 485 static class MobileIconGroup extends SignalController.IconGroup { 486 final int mDataContentDescription; // mContentDescriptionDataType 487 final int mDataType; 488 final boolean mIsWide; 489 final int mQsDataType; 490 491 public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, 492 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, 493 int discContentDesc, int dataContentDesc, int dataType, boolean isWide, 494 int qsDataType) { 495 super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState, 496 qsDiscState, discContentDesc); 497 mDataContentDescription = dataContentDesc; 498 mDataType = dataType; 499 mIsWide = isWide; 500 mQsDataType = qsDataType; 501 } 502 } 503 504 static class MobileState extends SignalController.State { 505 String networkName; 506 String networkNameData; 507 boolean dataSim; 508 boolean dataConnected; 509 boolean isEmergency; 510 boolean airplaneMode; 511 boolean carrierNetworkChangeMode; 512 boolean isDefault; 513 514 @Override 515 public void copyFrom(State s) { 516 super.copyFrom(s); 517 MobileState state = (MobileState) s; 518 dataSim = state.dataSim; 519 networkName = state.networkName; 520 networkNameData = state.networkNameData; 521 dataConnected = state.dataConnected; 522 isDefault = state.isDefault; 523 isEmergency = state.isEmergency; 524 airplaneMode = state.airplaneMode; 525 carrierNetworkChangeMode = state.carrierNetworkChangeMode; 526 } 527 528 @Override 529 protected void toString(StringBuilder builder) { 530 super.toString(builder); 531 builder.append(','); 532 builder.append("dataSim=").append(dataSim).append(','); 533 builder.append("networkName=").append(networkName).append(','); 534 builder.append("networkNameData=").append(networkNameData).append(','); 535 builder.append("dataConnected=").append(dataConnected).append(','); 536 builder.append("isDefault=").append(isDefault).append(','); 537 builder.append("isEmergency=").append(isEmergency).append(','); 538 builder.append("airplaneMode=").append(airplaneMode).append(','); 539 builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode); 540 } 541 542 @Override 543 public boolean equals(Object o) { 544 return super.equals(o) 545 && Objects.equals(((MobileState) o).networkName, networkName) 546 && Objects.equals(((MobileState) o).networkNameData, networkNameData) 547 && ((MobileState) o).dataSim == dataSim 548 && ((MobileState) o).dataConnected == dataConnected 549 && ((MobileState) o).isEmergency == isEmergency 550 && ((MobileState) o).airplaneMode == airplaneMode 551 && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode 552 && ((MobileState) o).isDefault == isDefault; 553 } 554 } 555} 556