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