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