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