MobileSignalController.java revision 5c7daaf7763f4c1fc3eb44430d1b2e5bf11130e1
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 243 boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0 244 || mCurrentState.iconGroup == TelephonyIcons.ROAMING; 245 246 // Only send data sim callbacks to QS. 247 if (mCurrentState.dataSim) { 248 int qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0; 249 int length = mSignalsChangedCallbacks.size(); 250 for (int i = 0; i < length; i++) { 251 mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled 252 && !mCurrentState.isEmergency, 253 getQsCurrentIconId(), contentDescription, 254 qsTypeIcon, 255 mCurrentState.dataConnected && mCurrentState.activityIn, 256 mCurrentState.dataConnected && mCurrentState.activityOut, 257 dataContentDescription, 258 mCurrentState.isEmergency ? null : mCurrentState.networkName, 259 // Only wide if actually showing something. 260 icons.mIsWide && qsTypeIcon != 0); 261 } 262 } 263 int typeIcon = showDataIcon ? icons.mDataType : 0; 264 int signalClustersLength = mSignalClusters.size(); 265 for (int i = 0; i < signalClustersLength; i++) { 266 mSignalClusters.get(i).setMobileDataIndicators( 267 mCurrentState.enabled && !mCurrentState.airplaneMode, 268 getCurrentIconId(), 269 typeIcon, 270 contentDescription, 271 dataContentDescription, 272 // Only wide if actually showing something. 273 icons.mIsWide && typeIcon != 0, 274 mSubscriptionInfo.getSubscriptionId()); 275 } 276 } 277 278 @Override 279 protected MobileState cleanState() { 280 return new MobileState(); 281 } 282 283 private boolean hasService() { 284 if (mServiceState != null) { 285 // Consider the device to be in service if either voice or data 286 // service is available. Some SIM cards are marketed as data-only 287 // and do not support voice service, and on these SIM cards, we 288 // want to show signal bars for data service as well as the "no 289 // service" or "emergency calls only" text that indicates that voice 290 // is not available. 291 switch (mServiceState.getVoiceRegState()) { 292 case ServiceState.STATE_POWER_OFF: 293 return false; 294 case ServiceState.STATE_OUT_OF_SERVICE: 295 case ServiceState.STATE_EMERGENCY_ONLY: 296 return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE; 297 default: 298 return true; 299 } 300 } else { 301 return false; 302 } 303 } 304 305 private boolean isCdma() { 306 return (mSignalStrength != null) && !mSignalStrength.isGsm(); 307 } 308 309 public boolean isEmergencyOnly() { 310 return (mServiceState != null && mServiceState.isEmergencyOnly()); 311 } 312 313 private boolean isRoaming() { 314 if (isCdma()) { 315 final int iconMode = mServiceState.getCdmaEriIconMode(); 316 return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF 317 && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL 318 || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH); 319 } else { 320 return mServiceState != null && mServiceState.getRoaming(); 321 } 322 } 323 324 public void handleBroadcast(Intent intent) { 325 String action = intent.getAction(); 326 if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { 327 updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), 328 intent.getStringExtra(TelephonyIntents.EXTRA_SPN), 329 intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), 330 intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); 331 notifyListenersIfNecessary(); 332 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { 333 updateDataSim(); 334 } 335 } 336 337 private void updateDataSim() { 338 int defaultDataSub = SubscriptionManager.getDefaultDataSubId(); 339 if (SubscriptionManager.isValidSubscriptionId(defaultDataSub)) { 340 mCurrentState.dataSim = defaultDataSub == mSubscriptionInfo.getSubscriptionId(); 341 } else { 342 // There doesn't seem to be a data sim selected, however if 343 // there isn't a MobileSignalController with dataSim set, then 344 // QS won't get any callbacks and will be blank. Instead 345 // lets just assume we are the data sim (which will basically 346 // show one at random) in QS until one is selected. The user 347 // should pick one soon after, so we shouldn't be in this state 348 // for long. 349 mCurrentState.dataSim = true; 350 } 351 notifyListenersIfNecessary(); 352 } 353 354 /** 355 * Updates the network's name based on incoming spn and plmn. 356 */ 357 void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { 358 if (CHATTY) { 359 Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn 360 + " showPlmn=" + showPlmn + " plmn=" + plmn); 361 } 362 StringBuilder str = new StringBuilder(); 363 if (showPlmn && plmn != null) { 364 str.append(plmn); 365 } 366 if (showSpn && spn != null) { 367 if (str.length() != 0) { 368 str.append(mNetworkNameSeparator); 369 } 370 str.append(spn); 371 } 372 if (str.length() != 0) { 373 mCurrentState.networkName = str.toString(); 374 } else { 375 mCurrentState.networkName = mNetworkNameDefault; 376 } 377 } 378 379 /** 380 * Updates the current state based on mServiceState, mSignalStrength, mDataNetType, 381 * mDataState, and mSimState. It should be called any time one of these is updated. 382 * This will call listeners if necessary. 383 */ 384 private final void updateTelephony() { 385 if (DEBUG) { 386 Log.d(mTag, "updateTelephonySignalStrength: hasService=" + hasService() 387 + " ss=" + mSignalStrength); 388 } 389 mCurrentState.connected = hasService() && mSignalStrength != null; 390 if (mCurrentState.connected) { 391 if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) { 392 mCurrentState.level = mSignalStrength.getCdmaLevel(); 393 } else { 394 mCurrentState.level = mSignalStrength.getLevel(); 395 } 396 } 397 if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) { 398 mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType); 399 } else { 400 mCurrentState.iconGroup = mDefaultIcons; 401 } 402 mCurrentState.dataConnected = mCurrentState.connected 403 && mDataState == TelephonyManager.DATA_CONNECTED; 404 405 if (isRoaming()) { 406 mCurrentState.iconGroup = TelephonyIcons.ROAMING; 407 } 408 if (isEmergencyOnly() != mCurrentState.isEmergency) { 409 mCurrentState.isEmergency = isEmergencyOnly(); 410 mNetworkController.recalculateEmergency(); 411 } 412 // Fill in the network name if we think we have it. 413 if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null 414 && mServiceState.getOperatorAlphaShort() != null) { 415 mCurrentState.networkName = mServiceState.getOperatorAlphaShort(); 416 } 417 notifyListenersIfNecessary(); 418 } 419 420 @VisibleForTesting 421 void setActivity(int activity) { 422 mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT 423 || activity == TelephonyManager.DATA_ACTIVITY_IN; 424 mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT 425 || activity == TelephonyManager.DATA_ACTIVITY_OUT; 426 notifyListenersIfNecessary(); 427 } 428 429 @Override 430 public void dump(PrintWriter pw) { 431 super.dump(pw); 432 pw.println(" mSubscription=" + mSubscriptionInfo + ","); 433 pw.println(" mServiceState=" + mServiceState + ","); 434 pw.println(" mSignalStrength=" + mSignalStrength + ","); 435 pw.println(" mDataState=" + mDataState + ","); 436 pw.println(" mDataNetType=" + mDataNetType + ","); 437 } 438 439 class MobilePhoneStateListener extends PhoneStateListener { 440 public MobilePhoneStateListener(int subId) { 441 super(subId); 442 } 443 444 @Override 445 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 446 if (DEBUG) { 447 Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength + 448 ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); 449 } 450 mSignalStrength = signalStrength; 451 updateTelephony(); 452 } 453 454 @Override 455 public void onServiceStateChanged(ServiceState state) { 456 if (DEBUG) { 457 Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState() 458 + " dataState=" + state.getDataRegState()); 459 } 460 mServiceState = state; 461 updateTelephony(); 462 } 463 464 @Override 465 public void onDataConnectionStateChanged(int state, int networkType) { 466 if (DEBUG) { 467 Log.d(mTag, "onDataConnectionStateChanged: state=" + state 468 + " type=" + networkType); 469 } 470 mDataState = state; 471 mDataNetType = networkType; 472 updateTelephony(); 473 } 474 475 @Override 476 public void onDataActivity(int direction) { 477 if (DEBUG) { 478 Log.d(mTag, "onDataActivity: direction=" + direction); 479 } 480 setActivity(direction); 481 } 482 }; 483 484 static class MobileIconGroup extends SignalController.IconGroup { 485 final int mDataContentDescription; // mContentDescriptionDataType 486 final int mDataType; 487 final boolean mIsWide; 488 final int[] mQsDataType; 489 490 public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, 491 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, 492 int discContentDesc, int dataContentDesc, int dataType, boolean isWide, 493 int[] qsDataType) { 494 super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState, 495 qsDiscState, discContentDesc); 496 mDataContentDescription = dataContentDesc; 497 mDataType = dataType; 498 mIsWide = isWide; 499 mQsDataType = qsDataType; 500 } 501 } 502 503 static class MobileState extends SignalController.State { 504 String networkName; 505 boolean dataSim; 506 boolean dataConnected; 507 boolean isEmergency; 508 boolean airplaneMode; 509 int inetForNetwork; 510 511 @Override 512 public void copyFrom(State s) { 513 super.copyFrom(s); 514 MobileState state = (MobileState) s; 515 dataSim = state.dataSim; 516 networkName = state.networkName; 517 dataConnected = state.dataConnected; 518 inetForNetwork = state.inetForNetwork; 519 isEmergency = state.isEmergency; 520 airplaneMode = state.airplaneMode; 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("dataConnected=").append(dataConnected).append(','); 530 builder.append("inetForNetwork=").append(inetForNetwork).append(','); 531 builder.append("isEmergency=").append(isEmergency).append(','); 532 builder.append("airplaneMode=").append(airplaneMode); 533 } 534 535 @Override 536 public boolean equals(Object o) { 537 return super.equals(o) 538 && Objects.equals(((MobileState) o).networkName, networkName) 539 && ((MobileState) o).dataSim == dataSim 540 && ((MobileState) o).dataConnected == dataConnected 541 && ((MobileState) o).isEmergency == isEmergency 542 && ((MobileState) o).airplaneMode == airplaneMode 543 && ((MobileState) o).inetForNetwork == inetForNetwork; 544 } 545 } 546} 547