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.connectivity; 17 18 import static com.android.settingslib.mobile.MobileMappings.getDefaultIcons; 19 import static com.android.settingslib.mobile.MobileMappings.getIconKey; 20 import static com.android.settingslib.mobile.MobileMappings.mapIconSets; 21 22 import android.content.Context; 23 import android.content.Intent; 24 import android.database.ContentObserver; 25 import android.net.NetworkCapabilities; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.provider.Settings.Global; 29 import android.telephony.AccessNetworkConstants; 30 import android.telephony.CellSignalStrength; 31 import android.telephony.CellSignalStrengthCdma; 32 import android.telephony.ServiceState; 33 import android.telephony.SignalStrength; 34 import android.telephony.SubscriptionInfo; 35 import android.telephony.SubscriptionManager; 36 import android.telephony.TelephonyManager; 37 import android.telephony.ims.ImsException; 38 import android.telephony.ims.ImsMmTelManager; 39 import android.telephony.ims.ImsReasonInfo; 40 import android.telephony.ims.ImsRegistrationAttributes; 41 import android.telephony.ims.RegistrationManager.RegistrationCallback; 42 import android.text.Html; 43 import android.text.TextUtils; 44 import android.util.Log; 45 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.settingslib.AccessibilityContentDescriptions; 48 import com.android.settingslib.SignalIcon.MobileIconGroup; 49 import com.android.settingslib.graph.SignalDrawable; 50 import com.android.settingslib.mobile.MobileMappings.Config; 51 import com.android.settingslib.mobile.MobileStatusTracker; 52 import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus; 53 import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults; 54 import com.android.settingslib.mobile.TelephonyIcons; 55 import com.android.settingslib.net.SignalStrengthUtil; 56 import com.android.systemui.R; 57 import com.android.systemui.flags.FeatureFlags; 58 import com.android.systemui.util.CarrierConfigTracker; 59 60 import java.io.PrintWriter; 61 import java.text.SimpleDateFormat; 62 import java.util.BitSet; 63 import java.util.List; 64 import java.util.Map; 65 66 /** 67 * Monitors the mobile signal changes and update the SysUI icons. 68 */ 69 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> { 70 private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 71 private static final int STATUS_HISTORY_SIZE = 64; 72 private static final int IMS_TYPE_WWAN = 1; 73 private static final int IMS_TYPE_WLAN = 2; 74 private static final int IMS_TYPE_WLAN_CROSS_SIM = 3; 75 private final TelephonyManager mPhone; 76 private final CarrierConfigTracker mCarrierConfigTracker; 77 private final ImsMmTelManager mImsMmTelManager; 78 private final SubscriptionDefaults mDefaults; 79 private final String mNetworkNameDefault; 80 private final String mNetworkNameSeparator; 81 private final ContentObserver mObserver; 82 private final boolean mProviderModelBehavior; 83 private final boolean mProviderModelSetting; 84 private final Handler mReceiverHandler; 85 private int mImsType = IMS_TYPE_WWAN; 86 // Save entire info for logging, we only use the id. 87 final SubscriptionInfo mSubscriptionInfo; 88 private Map<String, MobileIconGroup> mNetworkToIconLookup; 89 90 private int mLastLevel; 91 private MobileIconGroup mDefaultIcons; 92 private Config mConfig; 93 @VisibleForTesting 94 boolean mInflateSignalStrengths = false; 95 private int mLastWwanLevel; 96 private int mLastWlanLevel; 97 private int mLastWlanCrossSimLevel; 98 @VisibleForTesting 99 MobileStatusTracker mMobileStatusTracker; 100 101 // Save the previous STATUS_HISTORY_SIZE states for logging. 102 private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE]; 103 // Where to copy the next state into. 104 private int mMobileStatusHistoryIndex; 105 106 private final MobileStatusTracker.Callback mMobileCallback = 107 new MobileStatusTracker.Callback() { 108 private String mLastStatus; 109 110 @Override 111 public void onMobileStatusChanged(boolean updateTelephony, 112 MobileStatus mobileStatus) { 113 if (Log.isLoggable(mTag, Log.DEBUG)) { 114 Log.d(mTag, "onMobileStatusChanged=" 115 + " updateTelephony=" + updateTelephony 116 + " mobileStatus=" + mobileStatus.toString()); 117 } 118 String currentStatus = mobileStatus.toString(); 119 if (!currentStatus.equals(mLastStatus)) { 120 mLastStatus = currentStatus; 121 String status = new StringBuilder() 122 .append(SSDF.format(System.currentTimeMillis())).append(",") 123 .append(currentStatus) 124 .toString(); 125 recordLastMobileStatus(status); 126 } 127 updateMobileStatus(mobileStatus); 128 if (updateTelephony) { 129 updateTelephony(); 130 } else { 131 notifyListenersIfNecessary(); 132 } 133 } 134 }; 135 136 private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() { 137 @Override 138 public void onRegistered(ImsRegistrationAttributes attributes) { 139 Log.d(mTag, "onRegistered: " + "attributes=" + attributes); 140 int imsTransportType = attributes.getTransportType(); 141 int registrationAttributes = attributes.getAttributeFlags(); 142 if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { 143 mImsType = IMS_TYPE_WWAN; 144 IconState statusIcon = new IconState( 145 true, 146 getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false), 147 getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false)); 148 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 149 } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { 150 if (registrationAttributes == 0) { 151 mImsType = IMS_TYPE_WLAN; 152 IconState statusIcon = new IconState( 153 true, 154 getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true), 155 getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true)); 156 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 157 } else if (registrationAttributes 158 == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) { 159 mImsType = IMS_TYPE_WLAN_CROSS_SIM; 160 IconState statusIcon = new IconState( 161 true, 162 getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false), 163 getCallStrengthDescription( 164 mLastWlanCrossSimLevel, /* isWifi= */false)); 165 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 166 } 167 } 168 } 169 170 @Override 171 public void onUnregistered(ImsReasonInfo info) { 172 Log.d(mTag, "onDeregistered: " + "info=" + info); 173 mImsType = IMS_TYPE_WWAN; 174 IconState statusIcon = new IconState( 175 true, 176 getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false), 177 getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false)); 178 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 179 } 180 }; 181 182 // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't 183 // need listener lists anymore. MobileSignalController( Context context, Config config, boolean hasMobileData, TelephonyManager phone, CallbackHandler callbackHandler, NetworkControllerImpl networkController, SubscriptionInfo info, SubscriptionDefaults defaults, Looper receiverLooper, CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags )184 public MobileSignalController( 185 Context context, 186 Config config, 187 boolean hasMobileData, 188 TelephonyManager phone, 189 CallbackHandler callbackHandler, 190 NetworkControllerImpl networkController, 191 SubscriptionInfo info, 192 SubscriptionDefaults defaults, 193 Looper receiverLooper, 194 CarrierConfigTracker carrierConfigTracker, 195 FeatureFlags featureFlags 196 ) { 197 super("MobileSignalController(" + info.getSubscriptionId() + ")", context, 198 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler, 199 networkController); 200 mCarrierConfigTracker = carrierConfigTracker; 201 mConfig = config; 202 mPhone = phone; 203 mDefaults = defaults; 204 mSubscriptionInfo = info; 205 mNetworkNameSeparator = getTextIfExists( 206 R.string.status_bar_network_name_separator).toString(); 207 mNetworkNameDefault = getTextIfExists( 208 com.android.internal.R.string.lockscreen_carrier_default).toString(); 209 mReceiverHandler = new Handler(receiverLooper); 210 211 mNetworkToIconLookup = mapIconSets(mConfig); 212 mDefaultIcons = getDefaultIcons(mConfig); 213 214 String networkName = info.getCarrierName() != null ? info.getCarrierName().toString() 215 : mNetworkNameDefault; 216 mLastState.networkName = mCurrentState.networkName = networkName; 217 mLastState.networkNameData = mCurrentState.networkNameData = networkName; 218 mLastState.enabled = mCurrentState.enabled = hasMobileData; 219 mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons; 220 mObserver = new ContentObserver(new Handler(receiverLooper)) { 221 @Override 222 public void onChange(boolean selfChange) { 223 updateTelephony(); 224 } 225 }; 226 mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId()); 227 mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper, 228 info, mDefaults, mMobileCallback); 229 mProviderModelBehavior = featureFlags.isCombinedStatusBarSignalIconsEnabled(); 230 mProviderModelSetting = featureFlags.isProviderModelSettingEnabled(); 231 } 232 setConfiguration(Config config)233 void setConfiguration(Config config) { 234 mConfig = config; 235 updateInflateSignalStrength(); 236 mNetworkToIconLookup = mapIconSets(mConfig); 237 mDefaultIcons = getDefaultIcons(mConfig); 238 updateTelephony(); 239 } 240 setAirplaneMode(boolean airplaneMode)241 void setAirplaneMode(boolean airplaneMode) { 242 mCurrentState.airplaneMode = airplaneMode; 243 notifyListenersIfNecessary(); 244 } 245 setUserSetupComplete(boolean userSetup)246 void setUserSetupComplete(boolean userSetup) { 247 mCurrentState.userSetup = userSetup; 248 notifyListenersIfNecessary(); 249 } 250 251 @Override updateConnectivity(BitSet connectedTransports, BitSet validatedTransports)252 public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { 253 boolean isValidated = validatedTransports.get(mTransportType); 254 mCurrentState.isDefault = connectedTransports.get(mTransportType); 255 // Only show this as not having connectivity if we are default. 256 mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0; 257 notifyListenersIfNecessary(); 258 } 259 setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode)260 void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) { 261 mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode; 262 updateTelephony(); 263 } 264 265 /** 266 * Start listening for phone state changes. 267 */ registerListener()268 public void registerListener() { 269 mMobileStatusTracker.setListening(true); 270 mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA), 271 true, mObserver); 272 mContext.getContentResolver().registerContentObserver(Global.getUriFor( 273 Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()), 274 true, mObserver); 275 if (mProviderModelBehavior) { 276 mReceiverHandler.post(mTryRegisterIms); 277 } 278 } 279 280 // There is no listener to monitor whether the IMS service is ready, so we have to retry the 281 // IMS registration. 282 private final Runnable mTryRegisterIms = new Runnable() { 283 private static final int MAX_RETRY = 12; 284 private int mRetryCount; 285 286 @Override 287 public void run() { 288 try { 289 mRetryCount++; 290 mImsMmTelManager.registerImsRegistrationCallback( 291 mReceiverHandler::post, mRegistrationCallback); 292 Log.d(mTag, "registerImsRegistrationCallback succeeded"); 293 } catch (RuntimeException | ImsException e) { 294 if (mRetryCount < MAX_RETRY) { 295 Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e); 296 // Wait for 5 seconds to retry 297 mReceiverHandler.postDelayed(mTryRegisterIms, 5000); 298 } 299 } 300 } 301 }; 302 303 /** 304 * Stop listening for phone state changes. 305 */ unregisterListener()306 public void unregisterListener() { 307 mMobileStatusTracker.setListening(false); 308 mContext.getContentResolver().unregisterContentObserver(mObserver); 309 mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback); 310 } 311 updateInflateSignalStrength()312 private void updateInflateSignalStrength() { 313 mInflateSignalStrengths = SignalStrengthUtil.shouldInflateSignalStrength(mContext, 314 mSubscriptionInfo.getSubscriptionId()); 315 } 316 getNumLevels()317 private int getNumLevels() { 318 if (mInflateSignalStrengths) { 319 return CellSignalStrength.getNumSignalStrengthLevels() + 1; 320 } 321 return CellSignalStrength.getNumSignalStrengthLevels(); 322 } 323 324 @Override getCurrentIconId()325 public int getCurrentIconId() { 326 if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) { 327 return SignalDrawable.getCarrierChangeState(getNumLevels()); 328 } else if (mCurrentState.connected) { 329 int level = mCurrentState.level; 330 if (mInflateSignalStrengths) { 331 level++; 332 } 333 boolean dataDisabled = mCurrentState.userSetup 334 && (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED 335 || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA 336 && mCurrentState.defaultDataOff)); 337 boolean noInternet = mCurrentState.inetCondition == 0; 338 boolean cutOut = dataDisabled || noInternet; 339 return SignalDrawable.getState(level, getNumLevels(), cutOut); 340 } else if (mCurrentState.enabled) { 341 return SignalDrawable.getEmptyState(getNumLevels()); 342 } else { 343 return 0; 344 } 345 } 346 347 @Override getQsCurrentIconId()348 public int getQsCurrentIconId() { 349 return getCurrentIconId(); 350 } 351 352 @Override notifyListeners(SignalCallback callback)353 public void notifyListeners(SignalCallback callback) { 354 // If the device is on carrier merged WiFi, we should let WifiSignalController to control 355 // the SysUI states. 356 if (mNetworkController.isCarrierMergedWifi(mSubscriptionInfo.getSubscriptionId())) { 357 return; 358 } 359 MobileIconGroup icons = getIcons(); 360 361 String contentDescription = getTextIfExists(getContentDescription()).toString(); 362 CharSequence dataContentDescriptionHtml = getTextIfExists(icons.dataContentDescription); 363 364 //TODO: Hacky 365 // The data content description can sometimes be shown in a text view and might come to us 366 // as HTML. Strip any styling here so that listeners don't have to care 367 CharSequence dataContentDescription = Html.fromHtml( 368 dataContentDescriptionHtml.toString(), 0).toString(); 369 if (mCurrentState.inetCondition == 0) { 370 dataContentDescription = mContext.getString(R.string.data_connection_no_internet); 371 } 372 373 final QsInfo qsInfo = getQsInfo(contentDescription, icons.dataType); 374 final SbInfo sbInfo = getSbInfo(contentDescription, icons.dataType); 375 376 MobileDataIndicators mobileDataIndicators = new MobileDataIndicators( 377 sbInfo.icon, 378 qsInfo.icon, 379 sbInfo.ratTypeIcon, 380 qsInfo.ratTypeIcon, 381 mCurrentState.hasActivityIn(), 382 mCurrentState.hasActivityOut(), 383 dataContentDescription, 384 dataContentDescriptionHtml, 385 qsInfo.description, 386 mSubscriptionInfo.getSubscriptionId(), 387 mCurrentState.roaming, 388 sbInfo.showTriangle); 389 callback.setMobileDataIndicators(mobileDataIndicators); 390 } 391 getQsInfo(String contentDescription, int dataTypeIcon)392 private QsInfo getQsInfo(String contentDescription, int dataTypeIcon) { 393 int qsTypeIcon = 0; 394 IconState qsIcon = null; 395 CharSequence qsDescription = null; 396 397 boolean pm = mProviderModelSetting || mProviderModelBehavior; 398 if (mCurrentState.dataSim) { 399 // If using provider model behavior, only show QS icons if the state is also default 400 if (pm && !mCurrentState.isDefault) { 401 return new QsInfo(qsTypeIcon, qsIcon, qsDescription); 402 } 403 404 if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) { 405 qsTypeIcon = dataTypeIcon; 406 } 407 408 boolean qsIconVisible = mCurrentState.enabled && !mCurrentState.isEmergency; 409 qsIcon = new IconState(qsIconVisible, getQsCurrentIconId(), contentDescription); 410 411 if (!mCurrentState.isEmergency) { 412 qsDescription = mCurrentState.networkName; 413 } 414 } 415 416 return new QsInfo(qsTypeIcon, qsIcon, qsDescription); 417 } 418 getSbInfo(String contentDescription, int dataTypeIcon)419 private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) { 420 final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault(); 421 boolean showTriangle = false; 422 int typeIcon = 0; 423 IconState statusIcon = null; 424 425 if (mProviderModelBehavior) { 426 boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled) 427 && (mCurrentState.dataSim && mCurrentState.isDefault); 428 typeIcon = 429 (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0; 430 showDataIconStatusBar |= mCurrentState.roaming; 431 statusIcon = new IconState( 432 showDataIconStatusBar && !mCurrentState.airplaneMode, 433 getCurrentIconId(), contentDescription); 434 435 showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode; 436 } else { 437 statusIcon = new IconState( 438 mCurrentState.enabled && !mCurrentState.airplaneMode, 439 getCurrentIconId(), contentDescription); 440 441 boolean showDataIconInStatusBar = 442 (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled; 443 typeIcon = 444 (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0; 445 showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode; 446 } 447 448 return new SbInfo(showTriangle, typeIcon, statusIcon); 449 } 450 451 @Override cleanState()452 protected MobileState cleanState() { 453 return new MobileState(); 454 } 455 isInService()456 public boolean isInService() { 457 return mCurrentState.isInService(); 458 } 459 getNetworkNameForCarrierWiFi()460 String getNetworkNameForCarrierWiFi() { 461 return mPhone.getSimOperatorName(); 462 } 463 isRoaming()464 private boolean isRoaming() { 465 // During a carrier change, roaming indications need to be suppressed. 466 if (isCarrierNetworkChangeActive()) { 467 return false; 468 } 469 if (mCurrentState.isCdma()) { 470 return mPhone.getCdmaEnhancedRoamingIndicatorDisplayNumber() 471 != TelephonyManager.ERI_OFF; 472 } else { 473 return mCurrentState.isRoaming(); 474 } 475 } 476 isCarrierNetworkChangeActive()477 private boolean isCarrierNetworkChangeActive() { 478 return mCurrentState.carrierNetworkChangeMode; 479 } 480 handleBroadcast(Intent intent)481 void handleBroadcast(Intent intent) { 482 String action = intent.getAction(); 483 if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) { 484 updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false), 485 intent.getStringExtra(TelephonyManager.EXTRA_SPN), 486 intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN), 487 intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false), 488 intent.getStringExtra(TelephonyManager.EXTRA_PLMN)); 489 notifyListenersIfNecessary(); 490 } else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { 491 updateDataSim(); 492 notifyListenersIfNecessary(); 493 } 494 } 495 updateDataSim()496 private void updateDataSim() { 497 int activeDataSubId = mDefaults.getActiveDataSubId(); 498 if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) { 499 mCurrentState.dataSim = activeDataSubId == mSubscriptionInfo.getSubscriptionId(); 500 } else { 501 // There doesn't seem to be a data sim selected, however if 502 // there isn't a MobileSignalController with dataSim set, then 503 // QS won't get any callbacks and will be blank. Instead 504 // lets just assume we are the data sim (which will basically 505 // show one at random) in QS until one is selected. The user 506 // should pick one soon after, so we shouldn't be in this state 507 // for long. 508 mCurrentState.dataSim = true; 509 } 510 } 511 512 /** 513 * Updates the network's name based on incoming spn and plmn. 514 */ updateNetworkName(boolean showSpn, String spn, String dataSpn, boolean showPlmn, String plmn)515 void updateNetworkName(boolean showSpn, String spn, String dataSpn, 516 boolean showPlmn, String plmn) { 517 if (CHATTY) { 518 Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn 519 + " spn=" + spn + " dataSpn=" + dataSpn 520 + " showPlmn=" + showPlmn + " plmn=" + plmn); 521 } 522 StringBuilder str = new StringBuilder(); 523 StringBuilder strData = new StringBuilder(); 524 if (showPlmn && plmn != null) { 525 str.append(plmn); 526 strData.append(plmn); 527 } 528 if (showSpn && spn != null) { 529 if (str.length() != 0) { 530 str.append(mNetworkNameSeparator); 531 } 532 str.append(spn); 533 } 534 if (str.length() != 0) { 535 mCurrentState.networkName = str.toString(); 536 } else { 537 mCurrentState.networkName = mNetworkNameDefault; 538 } 539 if (showSpn && dataSpn != null) { 540 if (strData.length() != 0) { 541 strData.append(mNetworkNameSeparator); 542 } 543 strData.append(dataSpn); 544 } 545 if (strData.length() != 0) { 546 mCurrentState.networkNameData = strData.toString(); 547 } else { 548 mCurrentState.networkNameData = mNetworkNameDefault; 549 } 550 } 551 552 /** 553 * Extracts the CellSignalStrengthCdma from SignalStrength then returns the level 554 */ getCdmaLevel(SignalStrength signalStrength)555 private int getCdmaLevel(SignalStrength signalStrength) { 556 List<CellSignalStrengthCdma> signalStrengthCdma = 557 signalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class); 558 if (!signalStrengthCdma.isEmpty()) { 559 return signalStrengthCdma.get(0).getLevel(); 560 } 561 return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 562 } 563 updateMobileStatus(MobileStatus mobileStatus)564 private void updateMobileStatus(MobileStatus mobileStatus) { 565 int lastVoiceState = mCurrentState.getVoiceServiceState(); 566 mCurrentState.setFromMobileStatus(mobileStatus); 567 568 notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength); 569 if (mProviderModelBehavior) { 570 maybeNotifyCallStateChanged(lastVoiceState); 571 } 572 } 573 574 /** Call state changed is only applicable when provider model behavior is true */ maybeNotifyCallStateChanged(int lastVoiceState)575 private void maybeNotifyCallStateChanged(int lastVoiceState) { 576 int currentVoiceState = mCurrentState.getVoiceServiceState(); 577 if (lastVoiceState == currentVoiceState) { 578 return; 579 } 580 // Only update the no calling Status in the below scenarios 581 // 1. The first valid voice state has been received 582 // 2. The voice state has been changed and either the last or current state is 583 // ServiceState.STATE_IN_SERVICE 584 if (lastVoiceState == -1 585 || (lastVoiceState == ServiceState.STATE_IN_SERVICE 586 || currentVoiceState == ServiceState.STATE_IN_SERVICE)) { 587 boolean isNoCalling = mCurrentState.isNoCalling(); 588 isNoCalling &= !hideNoCalling(); 589 IconState statusIcon = new IconState(isNoCalling, 590 R.drawable.ic_qs_no_calling_sms, 591 getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString()); 592 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 593 } 594 } 595 updateNoCallingState()596 void updateNoCallingState() { 597 int currentVoiceState = mCurrentState.getVoiceServiceState(); 598 boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE; 599 isNoCalling &= !hideNoCalling(); 600 IconState statusIcon = new IconState(isNoCalling, 601 R.drawable.ic_qs_no_calling_sms, 602 getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString()); 603 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 604 } 605 hideNoCalling()606 private boolean hideNoCalling() { 607 return mNetworkController.hasDefaultNetwork() 608 && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId()); 609 } 610 getCallStrengthIcon(int level, boolean isWifi)611 private int getCallStrengthIcon(int level, boolean isWifi) { 612 return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level] 613 : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level]; 614 } 615 getCallStrengthDescription(int level, boolean isWifi)616 private String getCallStrengthDescription(int level, boolean isWifi) { 617 return isWifi 618 ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level]) 619 .toString() 620 : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level]) 621 .toString(); 622 } 623 refreshCallIndicator(SignalCallback callback)624 void refreshCallIndicator(SignalCallback callback) { 625 boolean isNoCalling = mCurrentState.isNoCalling(); 626 isNoCalling &= !hideNoCalling(); 627 IconState statusIcon = new IconState(isNoCalling, 628 R.drawable.ic_qs_no_calling_sms, 629 getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString()); 630 callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId()); 631 632 switch (mImsType) { 633 case IMS_TYPE_WWAN: 634 statusIcon = new IconState( 635 true, 636 getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false), 637 getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false)); 638 break; 639 case IMS_TYPE_WLAN: 640 statusIcon = new IconState( 641 true, 642 getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true), 643 getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true)); 644 break; 645 case IMS_TYPE_WLAN_CROSS_SIM: 646 statusIcon = new IconState( 647 true, 648 getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false), 649 getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false)); 650 } 651 callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId()); 652 } 653 notifyWifiLevelChange(int level)654 void notifyWifiLevelChange(int level) { 655 if (!mProviderModelBehavior) { 656 return; 657 } 658 mLastWlanLevel = level; 659 if (mImsType != IMS_TYPE_WLAN) { 660 return; 661 } 662 IconState statusIcon = new IconState( 663 true, 664 getCallStrengthIcon(level, /* isWifi= */true), 665 getCallStrengthDescription(level, /* isWifi= */true)); 666 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 667 } 668 notifyDefaultMobileLevelChange(int level)669 void notifyDefaultMobileLevelChange(int level) { 670 if (!mProviderModelBehavior) { 671 return; 672 } 673 mLastWlanCrossSimLevel = level; 674 if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) { 675 return; 676 } 677 IconState statusIcon = new IconState( 678 true, 679 getCallStrengthIcon(level, /* isWifi= */false), 680 getCallStrengthDescription(level, /* isWifi= */false)); 681 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 682 } 683 notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength)684 void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) { 685 if (!mProviderModelBehavior) { 686 return; 687 } 688 int newLevel = getSignalLevel(signalStrength); 689 if (newLevel != mLastLevel) { 690 mLastLevel = newLevel; 691 mLastWwanLevel = newLevel; 692 if (mImsType == IMS_TYPE_WWAN) { 693 IconState statusIcon = new IconState( 694 true, 695 getCallStrengthIcon(newLevel, /* isWifi= */false), 696 getCallStrengthDescription(newLevel, /* isWifi= */false)); 697 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); 698 } 699 if (mCurrentState.dataSim) { 700 mNetworkController.notifyDefaultMobileLevelChange(newLevel); 701 } 702 } 703 } 704 getSignalLevel(SignalStrength signalStrength)705 int getSignalLevel(SignalStrength signalStrength) { 706 if (signalStrength == null) { 707 return 0; 708 } 709 if (!signalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) { 710 return getCdmaLevel(signalStrength); 711 } else { 712 return signalStrength.getLevel(); 713 } 714 } 715 716 /** 717 * Updates the current state based on ServiceState, SignalStrength, DataState, 718 * TelephonyDisplayInfo, and sim state. It should be called any time one of these is updated. 719 * This will call listeners if necessary. 720 */ updateTelephony()721 private void updateTelephony() { 722 if (Log.isLoggable(mTag, Log.DEBUG)) { 723 Log.d(mTag, "updateTelephonySignalStrength: hasService=" 724 + mCurrentState.isInService() 725 + " ss=" + mCurrentState.signalStrength 726 + " displayInfo=" + mCurrentState.telephonyDisplayInfo); 727 } 728 checkDefaultData(); 729 mCurrentState.connected = mCurrentState.isInService(); 730 if (mCurrentState.connected) { 731 mCurrentState.level = getSignalLevel(mCurrentState.signalStrength); 732 } 733 734 String iconKey = getIconKey(mCurrentState.telephonyDisplayInfo); 735 if (mNetworkToIconLookup.get(iconKey) != null) { 736 mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey); 737 } else { 738 mCurrentState.iconGroup = mDefaultIcons; 739 } 740 mCurrentState.dataConnected = mCurrentState.isDataConnected(); 741 742 mCurrentState.roaming = isRoaming(); 743 if (isCarrierNetworkChangeActive()) { 744 mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE; 745 } else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) { 746 if (mSubscriptionInfo.getSubscriptionId() != mDefaults.getDefaultDataSubId()) { 747 mCurrentState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA; 748 } else { 749 mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED; 750 } 751 } 752 if (mCurrentState.isEmergencyOnly() != mCurrentState.isEmergency) { 753 mCurrentState.isEmergency = mCurrentState.isEmergencyOnly(); 754 mNetworkController.recalculateEmergency(); 755 } 756 // Fill in the network name if we think we have it. 757 if (mCurrentState.networkName.equals(mNetworkNameDefault) 758 && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) { 759 mCurrentState.networkName = mCurrentState.getOperatorAlphaShort(); 760 } 761 // If this is the data subscription, update the currentState data name 762 if (mCurrentState.networkNameData.equals(mNetworkNameDefault) 763 && mCurrentState.dataSim 764 && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) { 765 mCurrentState.networkNameData = mCurrentState.getOperatorAlphaShort(); 766 } 767 768 notifyListenersIfNecessary(); 769 } 770 771 /** 772 * If we are controlling the NOT_DEFAULT_DATA icon, check the status of the other one 773 */ checkDefaultData()774 private void checkDefaultData() { 775 if (mCurrentState.iconGroup != TelephonyIcons.NOT_DEFAULT_DATA) { 776 mCurrentState.defaultDataOff = false; 777 return; 778 } 779 780 mCurrentState.defaultDataOff = mNetworkController.isDataControllerDisabled(); 781 } 782 onMobileDataChanged()783 void onMobileDataChanged() { 784 checkDefaultData(); 785 notifyListenersIfNecessary(); 786 } 787 isDataDisabled()788 boolean isDataDisabled() { 789 return !mPhone.isDataConnectionAllowed(); 790 } 791 792 @VisibleForTesting setActivity(int activity)793 void setActivity(int activity) { 794 mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT 795 || activity == TelephonyManager.DATA_ACTIVITY_IN; 796 mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT 797 || activity == TelephonyManager.DATA_ACTIVITY_OUT; 798 notifyListenersIfNecessary(); 799 } 800 recordLastMobileStatus(String mobileStatus)801 private void recordLastMobileStatus(String mobileStatus) { 802 mMobileStatusHistory[mMobileStatusHistoryIndex] = mobileStatus; 803 mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE; 804 } 805 806 @VisibleForTesting setImsType(int imsType)807 void setImsType(int imsType) { 808 mImsType = imsType; 809 } 810 811 @Override dump(PrintWriter pw)812 public void dump(PrintWriter pw) { 813 super.dump(pw); 814 pw.println(" mSubscription=" + mSubscriptionInfo + ","); 815 pw.println(" mProviderModelSetting=" + mProviderModelSetting + ","); 816 pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ","); 817 pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ","); 818 pw.println(" isDataDisabled=" + isDataDisabled() + ","); 819 pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ","); 820 pw.println(" MobileStatusHistory"); 821 int size = 0; 822 for (int i = 0; i < STATUS_HISTORY_SIZE; i++) { 823 if (mMobileStatusHistory[i] != null) { 824 size++; 825 } 826 } 827 // Print out the previous states in ordered number. 828 for (int i = mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - 1; 829 i >= mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - size; i--) { 830 pw.println(" Previous MobileStatus(" 831 + (mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - i) + "): " 832 + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]); 833 } 834 } 835 836 /** Box for QS icon info */ 837 private static final class QsInfo { 838 final int ratTypeIcon; 839 final IconState icon; 840 final CharSequence description; 841 QsInfo(int typeIcon, IconState iconState, CharSequence desc)842 QsInfo(int typeIcon, IconState iconState, CharSequence desc) { 843 ratTypeIcon = typeIcon; 844 icon = iconState; 845 description = desc; 846 } 847 } 848 849 /** Box for StatusBar icon info */ 850 private static final class SbInfo { 851 final boolean showTriangle; 852 final int ratTypeIcon; 853 final IconState icon; 854 SbInfo(boolean show, int typeIcon, IconState iconState)855 SbInfo(boolean show, int typeIcon, IconState iconState) { 856 showTriangle = show; 857 ratTypeIcon = typeIcon; 858 icon = iconState; 859 } 860 } 861 } 862