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