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