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