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