1 /* 2 * Copyright (C) 2010 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 17 package com.android.systemui.statusbar.policy; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 20 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; 21 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 22 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; 23 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.res.Resources; 29 import android.net.ConnectivityManager; 30 import android.net.NetworkCapabilities; 31 import android.net.NetworkInfo; 32 import android.net.wifi.WifiConfiguration; 33 import android.net.wifi.WifiInfo; 34 import android.net.wifi.WifiManager; 35 import android.os.AsyncTask; 36 import android.os.Bundle; 37 import android.os.Handler; 38 import android.os.Message; 39 import android.os.Messenger; 40 import android.provider.Settings; 41 import android.telephony.PhoneStateListener; 42 import android.telephony.ServiceState; 43 import android.telephony.SignalStrength; 44 import android.telephony.SubscriptionInfo; 45 import android.telephony.SubscriptionManager; 46 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 47 import android.telephony.TelephonyManager; 48 import android.text.TextUtils; 49 import android.text.format.DateFormat; 50 import android.util.Log; 51 import android.util.SparseArray; 52 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.internal.telephony.IccCardConstants; 55 import com.android.internal.telephony.PhoneConstants; 56 import com.android.internal.telephony.TelephonyIntents; 57 import com.android.internal.telephony.cdma.EriInfo; 58 import com.android.internal.util.AsyncChannel; 59 import com.android.systemui.DemoMode; 60 import com.android.systemui.R; 61 62 import java.io.FileDescriptor; 63 import java.io.PrintWriter; 64 import java.util.ArrayList; 65 import java.util.BitSet; 66 import java.util.Collections; 67 import java.util.Comparator; 68 import java.util.HashMap; 69 import java.util.List; 70 import java.util.Locale; 71 import java.util.Map; 72 import java.util.Objects; 73 74 /** Platform implementation of the network controller. **/ 75 public class NetworkControllerImpl extends BroadcastReceiver 76 implements NetworkController, DemoMode { 77 // debug 78 static final String TAG = "NetworkController"; 79 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 80 // additional diagnostics, but not logspew 81 static final boolean CHATTY = Log.isLoggable(TAG + ".Chat", Log.DEBUG); 82 // Save the previous SignalController.States of all SignalControllers for dumps. 83 static final boolean RECORD_HISTORY = true; 84 // If RECORD_HISTORY how many to save, must be a power of 2. 85 static final int HISTORY_SIZE = 16; 86 87 private static final int INET_CONDITION_THRESHOLD = 50; 88 89 private final Context mContext; 90 private final TelephonyManager mPhone; 91 private final WifiManager mWifiManager; 92 private final ConnectivityManager mConnectivityManager; 93 private final SubscriptionManager mSubscriptionManager; 94 private final boolean mHasMobileDataFeature; 95 private Config mConfig; 96 97 // Subcontrollers. 98 @VisibleForTesting 99 final WifiSignalController mWifiSignalController; 100 @VisibleForTesting 101 final Map<Integer, MobileSignalController> mMobileSignalControllers = 102 new HashMap<Integer, MobileSignalController>(); 103 // When no SIMs are around at setup, and one is added later, it seems to default to the first 104 // SIM for most actions. This may be null if there aren't any SIMs around. 105 private MobileSignalController mDefaultSignalController; 106 private final AccessPointControllerImpl mAccessPoints; 107 private final MobileDataControllerImpl mMobileDataController; 108 109 // Network types that replace the carrier label if the device does not support mobile data. 110 private boolean mBluetoothTethered = false; 111 private boolean mEthernetConnected = false; 112 113 // state of inet connection 114 private boolean mConnected = false; 115 private boolean mInetCondition; // Used for Logging and demo. 116 117 // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are 118 // connected and validated, respectively. 119 private final BitSet mConnectedTransports = new BitSet(); 120 private final BitSet mValidatedTransports = new BitSet(); 121 122 // States that don't belong to a subcontroller. 123 private boolean mAirplaneMode = false; 124 private boolean mHasNoSims; 125 private Locale mLocale = null; 126 // This list holds our ordering. 127 private List<SubscriptionInfo> mCurrentSubscriptions 128 = new ArrayList<SubscriptionInfo>(); 129 130 // All the callbacks. 131 private ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<EmergencyListener>(); 132 private ArrayList<CarrierLabelListener> mCarrierListeners = 133 new ArrayList<CarrierLabelListener>(); 134 private ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>(); 135 private ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks = 136 new ArrayList<NetworkSignalChangedCallback>(); 137 @VisibleForTesting 138 boolean mListening; 139 140 // The current user ID. 141 private int mCurrentUserId; 142 143 /** 144 * Construct this controller object and register for updates. 145 */ 146 public NetworkControllerImpl(Context context) { 147 this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), 148 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), 149 (WifiManager) context.getSystemService(Context.WIFI_SERVICE), 150 SubscriptionManager.from(context), Config.readConfig(context), 151 new AccessPointControllerImpl(context), new MobileDataControllerImpl(context)); 152 registerListeners(); 153 } 154 155 @VisibleForTesting 156 NetworkControllerImpl(Context context, ConnectivityManager connectivityManager, 157 TelephonyManager telephonyManager, WifiManager wifiManager, 158 SubscriptionManager subManager, Config config, 159 AccessPointControllerImpl accessPointController, 160 MobileDataControllerImpl mobileDataController) { 161 mContext = context; 162 mConfig = config; 163 164 mSubscriptionManager = subManager; 165 mConnectivityManager = connectivityManager; 166 mHasMobileDataFeature = 167 mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); 168 169 // telephony 170 mPhone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 171 172 // wifi 173 mWifiManager = wifiManager; 174 175 mLocale = mContext.getResources().getConfiguration().locale; 176 mAccessPoints = accessPointController; 177 mMobileDataController = mobileDataController; 178 mMobileDataController.setNetworkController(this); 179 // TODO: Find a way to move this into MobileDataController. 180 mMobileDataController.setCallback(new MobileDataControllerImpl.Callback() { 181 @Override 182 public void onMobileDataEnabled(boolean enabled) { 183 notifyMobileDataEnabled(enabled); 184 } 185 }); 186 mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature, 187 mSignalsChangedCallbacks, mSignalClusters, this); 188 189 // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it 190 updateAirplaneMode(true /* force callback */); 191 mAccessPoints.setNetworkController(this); 192 } 193 194 private void registerListeners() { 195 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 196 mobileSignalController.registerListener(); 197 } 198 mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener); 199 200 // broadcasts 201 IntentFilter filter = new IntentFilter(); 202 filter.addAction(WifiManager.RSSI_CHANGED_ACTION); 203 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 204 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 205 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 206 filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 207 filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); 208 filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION); 209 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE); 210 filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); 211 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 212 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); 213 mContext.registerReceiver(this, filter); 214 mListening = true; 215 216 updateMobileControllers(); 217 } 218 219 private void unregisterListeners() { 220 mListening = false; 221 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 222 mobileSignalController.unregisterListener(); 223 } 224 mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener); 225 mContext.unregisterReceiver(this); 226 } 227 228 public int getConnectedWifiLevel() { 229 return mWifiSignalController.getState().level; 230 } 231 232 @Override 233 public AccessPointController getAccessPointController() { 234 return mAccessPoints; 235 } 236 237 @Override 238 public MobileDataController getMobileDataController() { 239 return mMobileDataController; 240 } 241 242 public void addEmergencyListener(EmergencyListener listener) { 243 mEmergencyListeners.add(listener); 244 listener.setEmergencyCallsOnly(isEmergencyOnly()); 245 } 246 247 public void addCarrierLabel(CarrierLabelListener listener) { 248 mCarrierListeners.add(listener); 249 refreshCarrierLabel(); 250 } 251 252 private void notifyMobileDataEnabled(boolean enabled) { 253 final int length = mSignalsChangedCallbacks.size(); 254 for (int i = 0; i < length; i++) { 255 mSignalsChangedCallbacks.get(i).onMobileDataEnabled(enabled); 256 } 257 } 258 259 public boolean hasMobileDataFeature() { 260 return mHasMobileDataFeature; 261 } 262 263 public boolean hasVoiceCallingFeature() { 264 return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; 265 } 266 267 private MobileSignalController getDataController() { 268 int dataSubId = SubscriptionManager.getDefaultDataSubId(); 269 if (!SubscriptionManager.isValidSubscriptionId(dataSubId)) { 270 if (DEBUG) Log.e(TAG, "No data sim selected"); 271 return mDefaultSignalController; 272 } 273 if (mMobileSignalControllers.containsKey(dataSubId)) { 274 return mMobileSignalControllers.get(dataSubId); 275 } 276 if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId); 277 return mDefaultSignalController; 278 } 279 280 public String getMobileNetworkName() { 281 MobileSignalController controller = getDataController(); 282 return controller != null ? controller.getState().networkName : ""; 283 } 284 285 public boolean isEmergencyOnly() { 286 int voiceSubId = SubscriptionManager.getDefaultVoiceSubId(); 287 if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) { 288 for (MobileSignalController mobileSignalController : 289 mMobileSignalControllers.values()) { 290 if (!mobileSignalController.isEmergencyOnly()) { 291 return false; 292 } 293 } 294 } 295 if (mMobileSignalControllers.containsKey(voiceSubId)) { 296 return mMobileSignalControllers.get(voiceSubId).isEmergencyOnly(); 297 } 298 if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId); 299 // Something is wrong, better assume we can't make calls... 300 return true; 301 } 302 303 /** 304 * Emergency status may have changed (triggered by MobileSignalController), 305 * so we should recheck and send out the state to listeners. 306 */ 307 void recalculateEmergency() { 308 final boolean emergencyOnly = isEmergencyOnly(); 309 final int length = mEmergencyListeners.size(); 310 for (int i = 0; i < length; i++) { 311 mEmergencyListeners.get(i).setEmergencyCallsOnly(emergencyOnly); 312 } 313 // If the emergency has a chance to change, then so does the carrier 314 // label. 315 refreshCarrierLabel(); 316 } 317 318 public void addSignalCluster(SignalCluster cluster) { 319 mSignalClusters.add(cluster); 320 cluster.setSubs(mCurrentSubscriptions); 321 cluster.setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, 322 R.string.accessibility_airplane_mode); 323 cluster.setNoSims(mHasNoSims); 324 mWifiSignalController.notifyListeners(); 325 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 326 mobileSignalController.notifyListeners(); 327 } 328 } 329 330 public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { 331 mSignalsChangedCallbacks.add(cb); 332 cb.onAirplaneModeChanged(mAirplaneMode); 333 cb.onNoSimVisibleChanged(mHasNoSims); 334 mWifiSignalController.notifyListeners(); 335 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 336 mobileSignalController.notifyListeners(); 337 } 338 } 339 340 public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { 341 mSignalsChangedCallbacks.remove(cb); 342 } 343 344 @Override 345 public void setWifiEnabled(final boolean enabled) { 346 new AsyncTask<Void, Void, Void>() { 347 @Override 348 protected Void doInBackground(Void... args) { 349 // Disable tethering if enabling Wifi 350 final int wifiApState = mWifiManager.getWifiApState(); 351 if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || 352 (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { 353 mWifiManager.setWifiApEnabled(null, false); 354 } 355 356 mWifiManager.setWifiEnabled(enabled); 357 return null; 358 } 359 }.execute(); 360 } 361 362 @Override 363 public void onUserSwitched(int newUserId) { 364 mCurrentUserId = newUserId; 365 mAccessPoints.onUserSwitched(newUserId); 366 updateConnectivity(); 367 refreshCarrierLabel(); 368 } 369 370 @Override 371 public void onReceive(Context context, Intent intent) { 372 if (CHATTY) { 373 Log.d(TAG, "onReceive: intent=" + intent); 374 } 375 final String action = intent.getAction(); 376 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) || 377 action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { 378 updateConnectivity(); 379 refreshCarrierLabel(); 380 } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { 381 mConfig = Config.readConfig(mContext); 382 handleConfigurationChanged(); 383 } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { 384 refreshLocale(); 385 updateAirplaneMode(false); 386 refreshCarrierLabel(); 387 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) { 388 // We are using different subs now, we might be able to make calls. 389 recalculateEmergency(); 390 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { 391 // Notify every MobileSignalController so they can know whether they are the 392 // data sim or not. 393 for (MobileSignalController controller : mMobileSignalControllers.values()) { 394 controller.handleBroadcast(intent); 395 } 396 } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { 397 // Might have different subscriptions now. 398 updateMobileControllers(); 399 } else { 400 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, 401 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 402 if (SubscriptionManager.isValidSubscriptionId(subId)) { 403 if (mMobileSignalControllers.containsKey(subId)) { 404 mMobileSignalControllers.get(subId).handleBroadcast(intent); 405 } else { 406 // Can't find this subscription... We must be out of date. 407 updateMobileControllers(); 408 } 409 } else { 410 // No sub id, must be for the wifi. 411 mWifiSignalController.handleBroadcast(intent); 412 } 413 } 414 } 415 416 @VisibleForTesting 417 void handleConfigurationChanged() { 418 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 419 mobileSignalController.setConfiguration(mConfig); 420 } 421 refreshLocale(); 422 refreshCarrierLabel(); 423 } 424 425 private void updateMobileControllers() { 426 if (!mListening) { 427 return; 428 } 429 List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList(); 430 if (subscriptions == null) { 431 subscriptions = Collections.emptyList(); 432 } 433 // If there have been no relevant changes to any of the subscriptions, we can leave as is. 434 if (hasCorrectMobileControllers(subscriptions)) { 435 // Even if the controllers are correct, make sure we have the right no sims state. 436 // Such as on boot, don't need any controllers, because there are no sims, 437 // but we still need to update the no sim state. 438 updateNoSims(); 439 return; 440 } 441 setCurrentSubscriptions(subscriptions); 442 updateNoSims(); 443 } 444 445 @VisibleForTesting 446 protected void updateNoSims() { 447 boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0; 448 if (hasNoSims != mHasNoSims) { 449 mHasNoSims = hasNoSims; 450 notifyListeners(); 451 } 452 } 453 454 @VisibleForTesting 455 void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) { 456 Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() { 457 @Override 458 public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) { 459 return lhs.getSimSlotIndex() == rhs.getSimSlotIndex() 460 ? lhs.getSubscriptionId() - rhs.getSubscriptionId() 461 : lhs.getSimSlotIndex() - rhs.getSimSlotIndex(); 462 } 463 }); 464 final int length = mSignalClusters.size(); 465 for (int i = 0; i < length; i++) { 466 mSignalClusters.get(i).setSubs(subscriptions); 467 } 468 mCurrentSubscriptions = subscriptions; 469 470 HashMap<Integer, MobileSignalController> cachedControllers = 471 new HashMap<Integer, MobileSignalController>(mMobileSignalControllers); 472 mMobileSignalControllers.clear(); 473 final int num = subscriptions.size(); 474 for (int i = 0; i < num; i++) { 475 int subId = subscriptions.get(i).getSubscriptionId(); 476 // If we have a copy of this controller already reuse it, otherwise make a new one. 477 if (cachedControllers.containsKey(subId)) { 478 mMobileSignalControllers.put(subId, cachedControllers.remove(subId)); 479 } else { 480 MobileSignalController controller = new MobileSignalController(mContext, mConfig, 481 mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, mSignalClusters, 482 this, subscriptions.get(i)); 483 mMobileSignalControllers.put(subId, controller); 484 if (subscriptions.get(i).getSimSlotIndex() == 0) { 485 mDefaultSignalController = controller; 486 } 487 if (mListening) { 488 controller.registerListener(); 489 } 490 } 491 } 492 if (mListening) { 493 for (Integer key : cachedControllers.keySet()) { 494 if (cachedControllers.get(key) == mDefaultSignalController) { 495 mDefaultSignalController = null; 496 } 497 cachedControllers.get(key).unregisterListener(); 498 } 499 } 500 // There may be new MobileSignalControllers around, make sure they get the current 501 // inet condition and airplane mode. 502 pushConnectivityToSignals(); 503 updateAirplaneMode(true /* force */); 504 } 505 506 @VisibleForTesting 507 boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) { 508 if (allSubscriptions.size() != mMobileSignalControllers.size()) { 509 return false; 510 } 511 for (SubscriptionInfo info : allSubscriptions) { 512 if (!mMobileSignalControllers.containsKey(info.getSubscriptionId())) { 513 return false; 514 } 515 } 516 return true; 517 } 518 519 private void updateAirplaneMode(boolean force) { 520 boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(), 521 Settings.Global.AIRPLANE_MODE_ON, 0) == 1); 522 if (airplaneMode != mAirplaneMode || force) { 523 mAirplaneMode = airplaneMode; 524 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 525 mobileSignalController.setAirplaneMode(mAirplaneMode); 526 } 527 notifyListeners(); 528 refreshCarrierLabel(); 529 } 530 } 531 532 private void refreshLocale() { 533 Locale current = mContext.getResources().getConfiguration().locale; 534 if (!current.equals(mLocale)) { 535 mLocale = current; 536 notifyAllListeners(); 537 } 538 } 539 540 /** 541 * Forces update of all callbacks on both SignalClusters and 542 * NetworkSignalChangedCallbacks. 543 */ 544 private void notifyAllListeners() { 545 notifyListeners(); 546 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 547 mobileSignalController.notifyListeners(); 548 } 549 mWifiSignalController.notifyListeners(); 550 } 551 552 /** 553 * Notifies listeners of changes in state of to the NetworkController, but 554 * does not notify for any info on SignalControllers, for that call 555 * notifyAllListeners. 556 */ 557 private void notifyListeners() { 558 int length = mSignalClusters.size(); 559 for (int i = 0; i < length; i++) { 560 mSignalClusters.get(i).setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, 561 R.string.accessibility_airplane_mode); 562 mSignalClusters.get(i).setNoSims(mHasNoSims); 563 } 564 int signalsChangedLength = mSignalsChangedCallbacks.size(); 565 for (int i = 0; i < signalsChangedLength; i++) { 566 mSignalsChangedCallbacks.get(i).onAirplaneModeChanged(mAirplaneMode); 567 mSignalsChangedCallbacks.get(i).onNoSimVisibleChanged(mHasNoSims); 568 } 569 } 570 571 /** 572 * Update the Inet conditions and what network we are connected to. 573 */ 574 private void updateConnectivity() { 575 mConnectedTransports.clear(); 576 mValidatedTransports.clear(); 577 for (NetworkCapabilities nc : 578 mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) { 579 for (int transportType : nc.getTransportTypes()) { 580 mConnectedTransports.set(transportType); 581 if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) { 582 mValidatedTransports.set(transportType); 583 } 584 } 585 } 586 587 if (CHATTY) { 588 Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports); 589 Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports); 590 } 591 592 mConnected = !mConnectedTransports.isEmpty(); 593 mInetCondition = !mValidatedTransports.isEmpty(); 594 mBluetoothTethered = mConnectedTransports.get(TRANSPORT_BLUETOOTH); 595 mEthernetConnected = mConnectedTransports.get(TRANSPORT_ETHERNET); 596 597 pushConnectivityToSignals(); 598 } 599 600 /** 601 * Pushes the current connectivity state to all SignalControllers. 602 */ 603 private void pushConnectivityToSignals() { 604 // We want to update all the icons, all at once, for any condition change 605 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 606 mobileSignalController.setInetCondition( 607 mInetCondition ? 1 : 0, 608 mValidatedTransports.get(mobileSignalController.getTransportType()) ? 1 : 0); 609 } 610 mWifiSignalController.setInetCondition( 611 mValidatedTransports.get(mWifiSignalController.getTransportType()) ? 1 : 0); 612 } 613 614 /** 615 * Recalculate and update the carrier label. 616 */ 617 void refreshCarrierLabel() { 618 Context context = mContext; 619 620 WifiSignalController.WifiState wifiState = mWifiSignalController.getState(); 621 String label = ""; 622 for (MobileSignalController controller : mMobileSignalControllers.values()) { 623 label = controller.getLabel(label, mConnected, mHasMobileDataFeature); 624 } 625 626 // TODO Simplify this ugliness, some of the flows below shouldn't be possible anymore 627 // but stay for the sake of history. 628 if (mBluetoothTethered && !mHasMobileDataFeature) { 629 label = mContext.getString(R.string.bluetooth_tethered); 630 } 631 632 if (mEthernetConnected && !mHasMobileDataFeature) { 633 label = context.getString(R.string.ethernet_label); 634 } 635 636 if (mAirplaneMode && !isEmergencyOnly()) { 637 // combined values from connected wifi take precedence over airplane mode 638 if (wifiState.connected && mHasMobileDataFeature) { 639 // Suppress "No internet connection." from mobile if wifi connected. 640 label = ""; 641 } else { 642 if (!mHasMobileDataFeature) { 643 label = context.getString( 644 R.string.status_bar_settings_signal_meter_disconnected); 645 } 646 } 647 } else if (!isMobileDataConnected() && !wifiState.connected && !mBluetoothTethered && 648 !mEthernetConnected && !mHasMobileDataFeature) { 649 // Pretty much no connection. 650 label = context.getString(R.string.status_bar_settings_signal_meter_disconnected); 651 } 652 653 // for mobile devices, we always show mobile connection info here (SPN/PLMN) 654 // for other devices, we show whatever network is connected 655 // This is determined above by references to mHasMobileDataFeature. 656 int length = mCarrierListeners.size(); 657 for (int i = 0; i < length; i++) { 658 mCarrierListeners.get(i).setCarrierLabel(label); 659 } 660 } 661 662 private boolean isMobileDataConnected() { 663 MobileSignalController controller = getDataController(); 664 return controller != null ? controller.getState().dataConnected : false; 665 } 666 667 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 668 pw.println("NetworkController state:"); 669 670 pw.println(" - telephony ------"); 671 pw.print(" hasVoiceCallingFeature()="); 672 pw.println(hasVoiceCallingFeature()); 673 674 pw.println(" - Bluetooth ----"); 675 pw.print(" mBtReverseTethered="); 676 pw.println(mBluetoothTethered); 677 678 pw.println(" - connectivity ------"); 679 pw.print(" mConnectedTransports="); 680 pw.println(mConnectedTransports); 681 pw.print(" mValidatedTransports="); 682 pw.println(mValidatedTransports); 683 pw.print(" mInetCondition="); 684 pw.println(mInetCondition); 685 pw.print(" mAirplaneMode="); 686 pw.println(mAirplaneMode); 687 pw.print(" mLocale="); 688 pw.println(mLocale); 689 690 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 691 mobileSignalController.dump(pw); 692 } 693 mWifiSignalController.dump(pw); 694 } 695 696 private boolean mDemoMode; 697 private int mDemoInetCondition; 698 private WifiSignalController.WifiState mDemoWifiState; 699 700 @Override 701 public void dispatchDemoCommand(String command, Bundle args) { 702 if (!mDemoMode && command.equals(COMMAND_ENTER)) { 703 if (DEBUG) Log.d(TAG, "Entering demo mode"); 704 unregisterListeners(); 705 mDemoMode = true; 706 mDemoInetCondition = mInetCondition ? 1 : 0; 707 mDemoWifiState = mWifiSignalController.getState(); 708 } else if (mDemoMode && command.equals(COMMAND_EXIT)) { 709 if (DEBUG) Log.d(TAG, "Exiting demo mode"); 710 mDemoMode = false; 711 // Update what MobileSignalControllers, because they may change 712 // to set the number of sim slots. 713 updateMobileControllers(); 714 for (MobileSignalController controller : mMobileSignalControllers.values()) { 715 controller.resetLastState(); 716 } 717 mWifiSignalController.resetLastState(); 718 registerListeners(); 719 notifyAllListeners(); 720 refreshCarrierLabel(); 721 } else if (mDemoMode && command.equals(COMMAND_NETWORK)) { 722 String airplane = args.getString("airplane"); 723 if (airplane != null) { 724 boolean show = airplane.equals("show"); 725 int length = mSignalClusters.size(); 726 for (int i = 0; i < length; i++) { 727 mSignalClusters.get(i).setIsAirplaneMode(show, TelephonyIcons.FLIGHT_MODE_ICON, 728 R.string.accessibility_airplane_mode); 729 } 730 } 731 String fully = args.getString("fully"); 732 if (fully != null) { 733 mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0; 734 mWifiSignalController.setInetCondition(mDemoInetCondition); 735 for (MobileSignalController controller : mMobileSignalControllers.values()) { 736 controller.setInetCondition(mDemoInetCondition, mDemoInetCondition); 737 } 738 } 739 String wifi = args.getString("wifi"); 740 if (wifi != null) { 741 boolean show = wifi.equals("show"); 742 String level = args.getString("level"); 743 if (level != null) { 744 mDemoWifiState.level = level.equals("null") ? -1 745 : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); 746 mDemoWifiState.connected = mDemoWifiState.level >= 0; 747 } 748 mDemoWifiState.enabled = show; 749 mWifiSignalController.notifyListeners(); 750 } 751 String sims = args.getString("sims"); 752 if (sims != null) { 753 int num = Integer.parseInt(sims); 754 List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>(); 755 if (num != mMobileSignalControllers.size()) { 756 mMobileSignalControllers.clear(); 757 int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax(); 758 for (int i = start /* get out of normal index range */; i < start + num; i++) { 759 SubscriptionInfo info = new SubscriptionInfo(i, "", i, "", "", 0, 0, "", 0, 760 null, 0, 0, ""); 761 subs.add(info); 762 mMobileSignalControllers.put(i, new MobileSignalController(mContext, 763 mConfig, mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, 764 mSignalClusters, this, info)); 765 } 766 } 767 final int n = mSignalClusters.size(); 768 for (int i = 0; i < n; i++) { 769 mSignalClusters.get(i).setSubs(subs); 770 } 771 } 772 String nosim = args.getString("nosim"); 773 if (nosim != null) { 774 boolean show = nosim.equals("show"); 775 final int n = mSignalClusters.size(); 776 for (int i = 0; i < n; i++) { 777 mSignalClusters.get(i).setNoSims(show); 778 } 779 } 780 String mobile = args.getString("mobile"); 781 if (mobile != null) { 782 boolean show = mobile.equals("show"); 783 String datatype = args.getString("datatype"); 784 String slotString = args.getString("slot"); 785 int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString); 786 // Hack to index linearly for easy use. 787 MobileSignalController controller = mMobileSignalControllers 788 .values().toArray(new MobileSignalController[0])[slot]; 789 controller.getState().dataSim = datatype != null; 790 if (datatype != null) { 791 controller.getState().iconGroup = 792 datatype.equals("1x") ? TelephonyIcons.ONE_X : 793 datatype.equals("3g") ? TelephonyIcons.THREE_G : 794 datatype.equals("4g") ? TelephonyIcons.FOUR_G : 795 datatype.equals("e") ? TelephonyIcons.E : 796 datatype.equals("g") ? TelephonyIcons.G : 797 datatype.equals("h") ? TelephonyIcons.H : 798 datatype.equals("lte") ? TelephonyIcons.LTE : 799 datatype.equals("roam") ? TelephonyIcons.ROAMING : 800 TelephonyIcons.UNKNOWN; 801 } 802 int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH; 803 String level = args.getString("level"); 804 if (level != null) { 805 controller.getState().level = level.equals("null") ? -1 806 : Math.min(Integer.parseInt(level), icons[0].length - 1); 807 controller.getState().connected = controller.getState().level >= 0; 808 } 809 controller.getState().enabled = show; 810 controller.notifyListeners(); 811 } 812 refreshCarrierLabel(); 813 } 814 } 815 816 private final OnSubscriptionsChangedListener mSubscriptionListener = 817 new OnSubscriptionsChangedListener() { 818 @Override 819 public void onSubscriptionsChanged() { 820 updateMobileControllers(); 821 }; 822 }; 823 824 // TODO: Move to its own file. 825 static class WifiSignalController extends 826 SignalController<WifiSignalController.WifiState, SignalController.IconGroup> { 827 private final WifiManager mWifiManager; 828 private final AsyncChannel mWifiChannel; 829 private final boolean mHasMobileData; 830 831 public WifiSignalController(Context context, boolean hasMobileData, 832 List<NetworkSignalChangedCallback> signalCallbacks, 833 List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { 834 super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI, 835 signalCallbacks, signalClusters, networkController); 836 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 837 mHasMobileData = hasMobileData; 838 Handler handler = new WifiHandler(); 839 mWifiChannel = new AsyncChannel(); 840 Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); 841 if (wifiMessenger != null) { 842 mWifiChannel.connect(context, handler, wifiMessenger); 843 } 844 // WiFi only has one state. 845 mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup( 846 "Wi-Fi Icons", 847 WifiIcons.WIFI_SIGNAL_STRENGTH, 848 WifiIcons.QS_WIFI_SIGNAL_STRENGTH, 849 AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH, 850 WifiIcons.WIFI_NO_NETWORK, 851 WifiIcons.QS_WIFI_NO_NETWORK, 852 WifiIcons.WIFI_NO_NETWORK, 853 WifiIcons.QS_WIFI_NO_NETWORK, 854 AccessibilityContentDescriptions.WIFI_NO_CONNECTION 855 ); 856 } 857 858 @Override 859 protected WifiState cleanState() { 860 return new WifiState(); 861 } 862 863 @Override 864 public void notifyListeners() { 865 // only show wifi in the cluster if connected or if wifi-only 866 boolean wifiVisible = mCurrentState.enabled 867 && (mCurrentState.connected || !mHasMobileData); 868 String wifiDesc = wifiVisible ? mCurrentState.ssid : null; 869 boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; 870 String contentDescription = getStringIfExists(getContentDescription()); 871 int length = mSignalsChangedCallbacks.size(); 872 for (int i = 0; i < length; i++) { 873 mSignalsChangedCallbacks.get(i).onWifiSignalChanged(mCurrentState.enabled, 874 mCurrentState.connected, getQsCurrentIconId(), 875 ssidPresent && mCurrentState.activityIn, 876 ssidPresent && mCurrentState.activityOut, contentDescription, wifiDesc); 877 } 878 879 int signalClustersLength = mSignalClusters.size(); 880 for (int i = 0; i < signalClustersLength; i++) { 881 mSignalClusters.get(i).setWifiIndicators(wifiVisible, getCurrentIconId(), 882 contentDescription); 883 } 884 } 885 886 /** 887 * Extract wifi state directly from broadcasts about changes in wifi state. 888 */ 889 public void handleBroadcast(Intent intent) { 890 String action = intent.getAction(); 891 if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 892 mCurrentState.enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 893 WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; 894 } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 895 final NetworkInfo networkInfo = (NetworkInfo) 896 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); 897 mCurrentState.connected = networkInfo != null && networkInfo.isConnected(); 898 // If Connected grab the signal strength and ssid. 899 if (mCurrentState.connected) { 900 // try getting it out of the intent first 901 WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null 902 ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) 903 : mWifiManager.getConnectionInfo(); 904 if (info != null) { 905 mCurrentState.ssid = getSsid(info); 906 } else { 907 mCurrentState.ssid = null; 908 } 909 } else if (!mCurrentState.connected) { 910 mCurrentState.ssid = null; 911 } 912 } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { 913 // Default to -200 as its below WifiManager.MIN_RSSI. 914 mCurrentState.rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); 915 mCurrentState.level = WifiManager.calculateSignalLevel( 916 mCurrentState.rssi, WifiIcons.WIFI_LEVEL_COUNT); 917 } 918 919 notifyListenersIfNecessary(); 920 } 921 922 private String getSsid(WifiInfo info) { 923 String ssid = info.getSSID(); 924 if (ssid != null) { 925 return ssid; 926 } 927 // OK, it's not in the connectionInfo; we have to go hunting for it 928 List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks(); 929 int length = networks.size(); 930 for (int i = 0; i < length; i++) { 931 if (networks.get(i).networkId == info.getNetworkId()) { 932 return networks.get(i).SSID; 933 } 934 } 935 return null; 936 } 937 938 @VisibleForTesting 939 void setActivity(int wifiActivity) { 940 mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT 941 || wifiActivity == WifiManager.DATA_ACTIVITY_IN; 942 mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT 943 || wifiActivity == WifiManager.DATA_ACTIVITY_OUT; 944 notifyListenersIfNecessary(); 945 } 946 947 /** 948 * Handler to receive the data activity on wifi. 949 */ 950 class WifiHandler extends Handler { 951 @Override 952 public void handleMessage(Message msg) { 953 switch (msg.what) { 954 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 955 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 956 mWifiChannel.sendMessage(Message.obtain(this, 957 AsyncChannel.CMD_CHANNEL_FULL_CONNECTION)); 958 } else { 959 Log.e(mTag, "Failed to connect to wifi"); 960 } 961 break; 962 case WifiManager.DATA_ACTIVITY_NOTIFICATION: 963 setActivity(msg.arg1); 964 break; 965 default: 966 // Ignore 967 break; 968 } 969 } 970 } 971 972 static class WifiState extends SignalController.State { 973 String ssid; 974 975 @Override 976 public void copyFrom(State s) { 977 super.copyFrom(s); 978 WifiState state = (WifiState) s; 979 ssid = state.ssid; 980 } 981 982 @Override 983 protected void toString(StringBuilder builder) { 984 super.toString(builder); 985 builder.append(',').append("ssid=").append(ssid); 986 } 987 988 @Override 989 public boolean equals(Object o) { 990 return super.equals(o) 991 && Objects.equals(((WifiState) o).ssid, ssid); 992 } 993 } 994 } 995 996 // TODO: Move to its own file. 997 public static class MobileSignalController extends SignalController< 998 MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> { 999 private final TelephonyManager mPhone; 1000 private final String mNetworkNameDefault; 1001 private final String mNetworkNameSeparator; 1002 @VisibleForTesting 1003 final PhoneStateListener mPhoneStateListener; 1004 // Save entire info for logging, we only use the id. 1005 private final SubscriptionInfo mSubscriptionInfo; 1006 1007 // @VisibleForDemoMode 1008 final SparseArray<MobileIconGroup> mNetworkToIconLookup; 1009 1010 // Since some pieces of the phone state are interdependent we store it locally, 1011 // this could potentially become part of MobileState for simplification/complication 1012 // of code. 1013 private IccCardConstants.State mSimState = IccCardConstants.State.READY; 1014 private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 1015 private int mDataState = TelephonyManager.DATA_DISCONNECTED; 1016 private ServiceState mServiceState; 1017 private SignalStrength mSignalStrength; 1018 private MobileIconGroup mDefaultIcons; 1019 private Config mConfig; 1020 1021 // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't 1022 // need listener lists anymore. 1023 public MobileSignalController(Context context, Config config, boolean hasMobileData, 1024 TelephonyManager phone, List<NetworkSignalChangedCallback> signalCallbacks, 1025 List<SignalCluster> signalClusters, NetworkControllerImpl networkController, 1026 SubscriptionInfo info) { 1027 super("MobileSignalController(" + info.getSubscriptionId() + ")", context, 1028 NetworkCapabilities.TRANSPORT_CELLULAR, signalCallbacks, signalClusters, 1029 networkController); 1030 mNetworkToIconLookup = new SparseArray<>(); 1031 mConfig = config; 1032 mPhone = phone; 1033 mSubscriptionInfo = info; 1034 mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId()); 1035 mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator); 1036 mNetworkNameDefault = getStringIfExists( 1037 com.android.internal.R.string.lockscreen_carrier_default); 1038 1039 mapIconSets(); 1040 1041 mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault; 1042 mLastState.enabled = mCurrentState.enabled = hasMobileData; 1043 mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons; 1044 // Get initial data sim state. 1045 updateDataSim(); 1046 } 1047 1048 public void setConfiguration(Config config) { 1049 mConfig = config; 1050 mapIconSets(); 1051 updateTelephony(); 1052 } 1053 1054 /** 1055 * Get (the mobile parts of) the carrier string. 1056 * 1057 * @param currentLabel can be used for concatenation, currently just empty 1058 * @param connected whether the device has connection to the internet at all 1059 * @param isMobileLabel whether to always return the network or just when data is connected 1060 */ 1061 public String getLabel(String currentLabel, boolean connected, boolean isMobileLabel) { 1062 if (!mCurrentState.enabled) { 1063 return ""; 1064 } else { 1065 String mobileLabel = ""; 1066 // We want to show the carrier name if in service and either: 1067 // - We are connected to mobile data, or 1068 // - We are not connected to mobile data, as long as the *reason* packets are not 1069 // being routed over that link is that we have better connectivity via wifi. 1070 // If data is disconnected for some other reason but wifi (or ethernet/bluetooth) 1071 // is connected, we show nothing. 1072 // Otherwise (nothing connected) we show "No internet connection". 1073 if (mCurrentState.dataConnected) { 1074 mobileLabel = mCurrentState.networkName; 1075 } else if (connected || mCurrentState.isEmergency) { 1076 if (mCurrentState.connected || mCurrentState.isEmergency) { 1077 // The isEmergencyOnly test covers the case of a phone with no SIM 1078 mobileLabel = mCurrentState.networkName; 1079 } 1080 } else { 1081 mobileLabel = mContext.getString( 1082 R.string.status_bar_settings_signal_meter_disconnected); 1083 } 1084 1085 if (currentLabel.length() != 0) { 1086 currentLabel = currentLabel + mNetworkNameSeparator; 1087 } 1088 // Now for things that should only be shown when actually using mobile data. 1089 if (isMobileLabel) { 1090 return currentLabel + mobileLabel; 1091 } else { 1092 return currentLabel 1093 + (mCurrentState.dataConnected ? mobileLabel : currentLabel); 1094 } 1095 } 1096 } 1097 1098 public int getDataContentDescription() { 1099 return getIcons().mDataContentDescription; 1100 } 1101 1102 public void setAirplaneMode(boolean airplaneMode) { 1103 mCurrentState.airplaneMode = airplaneMode; 1104 notifyListenersIfNecessary(); 1105 } 1106 1107 public void setInetCondition(int inetCondition, int inetConditionForNetwork) { 1108 // For mobile data, use general inet condition for phone signal indexing, 1109 // and network specific for data indexing (I think this might be a bug, but 1110 // keeping for now). 1111 // TODO: Update with explanation of why. 1112 mCurrentState.inetForNetwork = inetConditionForNetwork; 1113 setInetCondition(inetCondition); 1114 } 1115 1116 /** 1117 * Start listening for phone state changes. 1118 */ 1119 public void registerListener() { 1120 mPhone.listen(mPhoneStateListener, 1121 PhoneStateListener.LISTEN_SERVICE_STATE 1122 | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS 1123 | PhoneStateListener.LISTEN_CALL_STATE 1124 | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE 1125 | PhoneStateListener.LISTEN_DATA_ACTIVITY); 1126 } 1127 1128 /** 1129 * Stop listening for phone state changes. 1130 */ 1131 public void unregisterListener() { 1132 mPhone.listen(mPhoneStateListener, 0); 1133 } 1134 1135 /** 1136 * Produce a mapping of data network types to icon groups for simple and quick use in 1137 * updateTelephony. 1138 */ 1139 private void mapIconSets() { 1140 mNetworkToIconLookup.clear(); 1141 1142 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G); 1143 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G); 1144 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G); 1145 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G); 1146 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G); 1147 1148 if (!mConfig.showAtLeast3G) { 1149 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN, 1150 TelephonyIcons.UNKNOWN); 1151 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E); 1152 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X); 1153 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X); 1154 1155 mDefaultIcons = TelephonyIcons.G; 1156 } else { 1157 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN, 1158 TelephonyIcons.THREE_G); 1159 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, 1160 TelephonyIcons.THREE_G); 1161 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, 1162 TelephonyIcons.THREE_G); 1163 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, 1164 TelephonyIcons.THREE_G); 1165 mDefaultIcons = TelephonyIcons.THREE_G; 1166 } 1167 1168 MobileIconGroup hGroup = TelephonyIcons.THREE_G; 1169 if (mConfig.hspaDataDistinguishable) { 1170 hGroup = TelephonyIcons.H; 1171 } 1172 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup); 1173 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup); 1174 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup); 1175 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup); 1176 1177 if (mConfig.show4gForLte) { 1178 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G); 1179 } else { 1180 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE); 1181 } 1182 } 1183 1184 @Override 1185 public void notifyListeners() { 1186 MobileIconGroup icons = getIcons(); 1187 1188 String contentDescription = getStringIfExists(getContentDescription()); 1189 String dataContentDescription = getStringIfExists(icons.mDataContentDescription); 1190 1191 boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0 1192 || mCurrentState.iconGroup == TelephonyIcons.ROAMING; 1193 1194 // Only send data sim callbacks to QS. 1195 if (mCurrentState.dataSim) { 1196 int qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0; 1197 int length = mSignalsChangedCallbacks.size(); 1198 for (int i = 0; i < length; i++) { 1199 mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled 1200 && !mCurrentState.isEmergency, 1201 getQsCurrentIconId(), contentDescription, 1202 qsTypeIcon, 1203 mCurrentState.dataConnected && mCurrentState.activityIn, 1204 mCurrentState.dataConnected && mCurrentState.activityOut, 1205 dataContentDescription, 1206 mCurrentState.isEmergency ? null : mCurrentState.networkName, 1207 // Only wide if actually showing something. 1208 icons.mIsWide && qsTypeIcon != 0); 1209 } 1210 } 1211 int typeIcon = showDataIcon ? icons.mDataType : 0; 1212 int signalClustersLength = mSignalClusters.size(); 1213 for (int i = 0; i < signalClustersLength; i++) { 1214 mSignalClusters.get(i).setMobileDataIndicators( 1215 mCurrentState.enabled && !mCurrentState.airplaneMode, 1216 getCurrentIconId(), 1217 typeIcon, 1218 contentDescription, 1219 dataContentDescription, 1220 // Only wide if actually showing something. 1221 icons.mIsWide && typeIcon != 0, 1222 mSubscriptionInfo.getSubscriptionId()); 1223 } 1224 } 1225 1226 @Override 1227 protected MobileState cleanState() { 1228 return new MobileState(); 1229 } 1230 1231 private boolean hasService() { 1232 if (mServiceState != null) { 1233 // Consider the device to be in service if either voice or data 1234 // service is available. Some SIM cards are marketed as data-only 1235 // and do not support voice service, and on these SIM cards, we 1236 // want to show signal bars for data service as well as the "no 1237 // service" or "emergency calls only" text that indicates that voice 1238 // is not available. 1239 switch (mServiceState.getVoiceRegState()) { 1240 case ServiceState.STATE_POWER_OFF: 1241 return false; 1242 case ServiceState.STATE_OUT_OF_SERVICE: 1243 case ServiceState.STATE_EMERGENCY_ONLY: 1244 return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE; 1245 default: 1246 return true; 1247 } 1248 } else { 1249 return false; 1250 } 1251 } 1252 1253 private boolean isCdma() { 1254 return (mSignalStrength != null) && !mSignalStrength.isGsm(); 1255 } 1256 1257 public boolean isEmergencyOnly() { 1258 return (mServiceState != null && mServiceState.isEmergencyOnly()); 1259 } 1260 1261 private boolean isRoaming() { 1262 if (isCdma()) { 1263 final int iconMode = mServiceState.getCdmaEriIconMode(); 1264 return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF 1265 && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL 1266 || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH); 1267 } else { 1268 return mServiceState != null && mServiceState.getRoaming(); 1269 } 1270 } 1271 1272 public void handleBroadcast(Intent intent) { 1273 String action = intent.getAction(); 1274 if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { 1275 updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), 1276 intent.getStringExtra(TelephonyIntents.EXTRA_SPN), 1277 intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), 1278 intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); 1279 notifyListenersIfNecessary(); 1280 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { 1281 updateDataSim(); 1282 } 1283 } 1284 1285 private void updateDataSim() { 1286 int defaultDataSub = SubscriptionManager.getDefaultDataSubId(); 1287 if (SubscriptionManager.isValidSubscriptionId(defaultDataSub)) { 1288 mCurrentState.dataSim = defaultDataSub == mSubscriptionInfo.getSubscriptionId(); 1289 } else { 1290 // There doesn't seem to be a data sim selected, however if 1291 // there isn't a MobileSignalController with dataSim set, then 1292 // QS won't get any callbacks and will be blank. Instead 1293 // lets just assume we are the data sim (which will basically 1294 // show one at random) in QS until one is selected. The user 1295 // should pick one soon after, so we shouldn't be in this state 1296 // for long. 1297 mCurrentState.dataSim = true; 1298 } 1299 notifyListenersIfNecessary(); 1300 } 1301 1302 /** 1303 * Updates the network's name based on incoming spn and plmn. 1304 */ 1305 void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { 1306 if (CHATTY) { 1307 Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn 1308 + " showPlmn=" + showPlmn + " plmn=" + plmn); 1309 } 1310 StringBuilder str = new StringBuilder(); 1311 if (showPlmn && plmn != null) { 1312 str.append(plmn); 1313 } 1314 if (showSpn && spn != null) { 1315 if (str.length() != 0) { 1316 str.append(mNetworkNameSeparator); 1317 } 1318 str.append(spn); 1319 } 1320 if (str.length() != 0) { 1321 mCurrentState.networkName = str.toString(); 1322 } else { 1323 mCurrentState.networkName = mNetworkNameDefault; 1324 } 1325 } 1326 1327 /** 1328 * Updates the current state based on mServiceState, mSignalStrength, mDataNetType, 1329 * mDataState, and mSimState. It should be called any time one of these is updated. 1330 * This will call listeners if necessary. 1331 */ 1332 private final void updateTelephony() { 1333 if (DEBUG) { 1334 Log.d(TAG, "updateTelephonySignalStrength: hasService=" + hasService() 1335 + " ss=" + mSignalStrength); 1336 } 1337 mCurrentState.connected = hasService() && mSignalStrength != null; 1338 if (mCurrentState.connected) { 1339 if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) { 1340 mCurrentState.level = mSignalStrength.getCdmaLevel(); 1341 } else { 1342 mCurrentState.level = mSignalStrength.getLevel(); 1343 } 1344 } 1345 if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) { 1346 mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType); 1347 } else { 1348 mCurrentState.iconGroup = mDefaultIcons; 1349 } 1350 mCurrentState.dataConnected = mCurrentState.connected 1351 && mDataState == TelephonyManager.DATA_CONNECTED; 1352 1353 if (isRoaming()) { 1354 mCurrentState.iconGroup = TelephonyIcons.ROAMING; 1355 } 1356 if (isEmergencyOnly() != mCurrentState.isEmergency) { 1357 mCurrentState.isEmergency = isEmergencyOnly(); 1358 mNetworkController.recalculateEmergency(); 1359 } 1360 // Fill in the network name if we think we have it. 1361 if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null 1362 && mServiceState.getOperatorAlphaShort() != null) { 1363 mCurrentState.networkName = mServiceState.getOperatorAlphaShort(); 1364 } 1365 notifyListenersIfNecessary(); 1366 } 1367 1368 @VisibleForTesting 1369 void setActivity(int activity) { 1370 mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT 1371 || activity == TelephonyManager.DATA_ACTIVITY_IN; 1372 mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT 1373 || activity == TelephonyManager.DATA_ACTIVITY_OUT; 1374 notifyListenersIfNecessary(); 1375 } 1376 1377 @Override 1378 public void dump(PrintWriter pw) { 1379 super.dump(pw); 1380 pw.println(" mSubscription=" + mSubscriptionInfo + ","); 1381 pw.println(" mServiceState=" + mServiceState + ","); 1382 pw.println(" mSignalStrength=" + mSignalStrength + ","); 1383 pw.println(" mDataState=" + mDataState + ","); 1384 pw.println(" mDataNetType=" + mDataNetType + ","); 1385 } 1386 1387 class MobilePhoneStateListener extends PhoneStateListener { 1388 public MobilePhoneStateListener(int subId) { 1389 super(subId); 1390 } 1391 1392 @Override 1393 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 1394 if (DEBUG) { 1395 Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength + 1396 ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); 1397 } 1398 mSignalStrength = signalStrength; 1399 updateTelephony(); 1400 } 1401 1402 @Override 1403 public void onServiceStateChanged(ServiceState state) { 1404 if (DEBUG) { 1405 Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState() 1406 + " dataState=" + state.getDataRegState()); 1407 } 1408 mServiceState = state; 1409 updateTelephony(); 1410 } 1411 1412 @Override 1413 public void onDataConnectionStateChanged(int state, int networkType) { 1414 if (DEBUG) { 1415 Log.d(mTag, "onDataConnectionStateChanged: state=" + state 1416 + " type=" + networkType); 1417 } 1418 mDataState = state; 1419 mDataNetType = networkType; 1420 updateTelephony(); 1421 } 1422 1423 @Override 1424 public void onDataActivity(int direction) { 1425 if (DEBUG) { 1426 Log.d(mTag, "onDataActivity: direction=" + direction); 1427 } 1428 setActivity(direction); 1429 } 1430 }; 1431 1432 static class MobileIconGroup extends SignalController.IconGroup { 1433 final int mDataContentDescription; // mContentDescriptionDataType 1434 final int mDataType; 1435 final boolean mIsWide; 1436 final int[] mQsDataType; 1437 1438 public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, 1439 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, 1440 int discContentDesc, int dataContentDesc, int dataType, boolean isWide, 1441 int[] qsDataType) { 1442 super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState, 1443 qsDiscState, discContentDesc); 1444 mDataContentDescription = dataContentDesc; 1445 mDataType = dataType; 1446 mIsWide = isWide; 1447 mQsDataType = qsDataType; 1448 } 1449 } 1450 1451 static class MobileState extends SignalController.State { 1452 String networkName; 1453 boolean dataSim; 1454 boolean dataConnected; 1455 boolean isEmergency; 1456 boolean airplaneMode; 1457 int inetForNetwork; 1458 1459 @Override 1460 public void copyFrom(State s) { 1461 super.copyFrom(s); 1462 MobileState state = (MobileState) s; 1463 dataSim = state.dataSim; 1464 networkName = state.networkName; 1465 dataConnected = state.dataConnected; 1466 inetForNetwork = state.inetForNetwork; 1467 isEmergency = state.isEmergency; 1468 airplaneMode = state.airplaneMode; 1469 } 1470 1471 @Override 1472 protected void toString(StringBuilder builder) { 1473 super.toString(builder); 1474 builder.append(','); 1475 builder.append("dataSim=").append(dataSim).append(','); 1476 builder.append("networkName=").append(networkName).append(','); 1477 builder.append("dataConnected=").append(dataConnected).append(','); 1478 builder.append("inetForNetwork=").append(inetForNetwork).append(','); 1479 builder.append("isEmergency=").append(isEmergency).append(','); 1480 builder.append("airplaneMode=").append(airplaneMode); 1481 } 1482 1483 @Override 1484 public boolean equals(Object o) { 1485 return super.equals(o) 1486 && Objects.equals(((MobileState) o).networkName, networkName) 1487 && ((MobileState) o).dataSim == dataSim 1488 && ((MobileState) o).dataConnected == dataConnected 1489 && ((MobileState) o).isEmergency == isEmergency 1490 && ((MobileState) o).airplaneMode == airplaneMode 1491 && ((MobileState) o).inetForNetwork == inetForNetwork; 1492 } 1493 } 1494 } 1495 1496 /** 1497 * Common base class for handling signal for both wifi and mobile data. 1498 */ 1499 static abstract class SignalController<T extends SignalController.State, 1500 I extends SignalController.IconGroup> { 1501 protected final String mTag; 1502 protected final T mCurrentState; 1503 protected final T mLastState; 1504 protected final int mTransportType; 1505 protected final Context mContext; 1506 // The owner of the SignalController (i.e. NetworkController will maintain the following 1507 // lists and call notifyListeners whenever the list has changed to ensure everyone 1508 // is aware of current state. 1509 protected final List<NetworkSignalChangedCallback> mSignalsChangedCallbacks; 1510 protected final List<SignalCluster> mSignalClusters; 1511 protected final NetworkControllerImpl mNetworkController; 1512 1513 // Save the previous HISTORY_SIZE states for logging. 1514 private final State[] mHistory; 1515 // Where to copy the next state into. 1516 private int mHistoryIndex; 1517 1518 public SignalController(String tag, Context context, int type, 1519 List<NetworkSignalChangedCallback> signalCallbacks, 1520 List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { 1521 mTag = TAG + "." + tag; 1522 mNetworkController = networkController; 1523 mTransportType = type; 1524 mContext = context; 1525 mSignalsChangedCallbacks = signalCallbacks; 1526 mSignalClusters = signalClusters; 1527 mCurrentState = cleanState(); 1528 mLastState = cleanState(); 1529 if (RECORD_HISTORY) { 1530 mHistory = new State[HISTORY_SIZE]; 1531 for (int i = 0; i < HISTORY_SIZE; i++) { 1532 mHistory[i] = cleanState(); 1533 } 1534 } 1535 } 1536 1537 public T getState() { 1538 return mCurrentState; 1539 } 1540 1541 public int getTransportType() { 1542 return mTransportType; 1543 } 1544 1545 public void setInetCondition(int inetCondition) { 1546 mCurrentState.inetCondition = inetCondition; 1547 notifyListenersIfNecessary(); 1548 } 1549 1550 /** 1551 * Used at the end of demo mode to clear out any ugly state that it has created. 1552 * Since we haven't had any callbacks, then isDirty will not have been triggered, 1553 * so we can just take the last good state directly from there. 1554 * 1555 * Used for demo mode. 1556 */ 1557 void resetLastState() { 1558 mCurrentState.copyFrom(mLastState); 1559 } 1560 1561 /** 1562 * Determines if the state of this signal controller has changed and 1563 * needs to trigger callbacks related to it. 1564 */ 1565 public boolean isDirty() { 1566 if (!mLastState.equals(mCurrentState)) { 1567 if (DEBUG) { 1568 Log.d(mTag, "Change in state from: " + mLastState + "\n" 1569 + "\tto: " + mCurrentState); 1570 } 1571 return true; 1572 } 1573 return false; 1574 } 1575 1576 public void saveLastState() { 1577 if (RECORD_HISTORY) { 1578 recordLastState(); 1579 } 1580 // Updates the current time. 1581 mCurrentState.time = System.currentTimeMillis(); 1582 mLastState.copyFrom(mCurrentState); 1583 } 1584 1585 /** 1586 * Gets the signal icon for QS based on current state of connected, enabled, and level. 1587 */ 1588 public int getQsCurrentIconId() { 1589 if (mCurrentState.connected) { 1590 return getIcons().mQsIcons[mCurrentState.inetCondition][mCurrentState.level]; 1591 } else if (mCurrentState.enabled) { 1592 return getIcons().mQsDiscState; 1593 } else { 1594 return getIcons().mQsNullState; 1595 } 1596 } 1597 1598 /** 1599 * Gets the signal icon for SB based on current state of connected, enabled, and level. 1600 */ 1601 public int getCurrentIconId() { 1602 if (mCurrentState.connected) { 1603 return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level]; 1604 } else if (mCurrentState.enabled) { 1605 return getIcons().mSbDiscState; 1606 } else { 1607 return getIcons().mSbNullState; 1608 } 1609 } 1610 1611 /** 1612 * Gets the content description id for the signal based on current state of connected and 1613 * level. 1614 */ 1615 public int getContentDescription() { 1616 if (mCurrentState.connected) { 1617 return getIcons().mContentDesc[mCurrentState.level]; 1618 } else { 1619 return getIcons().mDiscContentDesc; 1620 } 1621 } 1622 1623 public void notifyListenersIfNecessary() { 1624 if (isDirty()) { 1625 saveLastState(); 1626 notifyListeners(); 1627 mNetworkController.refreshCarrierLabel(); 1628 } 1629 } 1630 1631 /** 1632 * Returns the resource if resId is not 0, and an empty string otherwise. 1633 */ 1634 protected String getStringIfExists(int resId) { 1635 return resId != 0 ? mContext.getString(resId) : ""; 1636 } 1637 1638 protected I getIcons() { 1639 return (I) mCurrentState.iconGroup; 1640 } 1641 1642 /** 1643 * Saves the last state of any changes, so we can log the current 1644 * and last value of any state data. 1645 */ 1646 protected void recordLastState() { 1647 mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState); 1648 } 1649 1650 public void dump(PrintWriter pw) { 1651 pw.println(" - " + mTag + " -----"); 1652 pw.println(" Current State: " + mCurrentState); 1653 if (RECORD_HISTORY) { 1654 // Count up the states that actually contain time stamps, and only display those. 1655 int size = 0; 1656 for (int i = 0; i < HISTORY_SIZE; i++) { 1657 if (mHistory[i].time != 0) size++; 1658 } 1659 // Print out the previous states in ordered number. 1660 for (int i = mHistoryIndex + HISTORY_SIZE - 1; 1661 i >= mHistoryIndex + HISTORY_SIZE - size; i--) { 1662 pw.println(" Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + ": " 1663 + mHistory[i & (HISTORY_SIZE - 1)]); 1664 } 1665 } 1666 } 1667 1668 /** 1669 * Trigger callbacks based on current state. The callbacks should be completely 1670 * based on current state, and only need to be called in the scenario where 1671 * mCurrentState != mLastState. 1672 */ 1673 public abstract void notifyListeners(); 1674 1675 /** 1676 * Generate a blank T. 1677 */ 1678 protected abstract T cleanState(); 1679 1680 /* 1681 * Holds icons for a given state. Arrays are generally indexed as inet 1682 * state (full connectivity or not) first, and second dimension as 1683 * signal strength. 1684 */ 1685 static class IconGroup { 1686 final int[][] mSbIcons; 1687 final int[][] mQsIcons; 1688 final int[] mContentDesc; 1689 final int mSbNullState; 1690 final int mQsNullState; 1691 final int mSbDiscState; 1692 final int mQsDiscState; 1693 final int mDiscContentDesc; 1694 // For logging. 1695 final String mName; 1696 1697 public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, 1698 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, 1699 int discContentDesc) { 1700 mName = name; 1701 mSbIcons = sbIcons; 1702 mQsIcons = qsIcons; 1703 mContentDesc = contentDesc; 1704 mSbNullState = sbNullState; 1705 mQsNullState = qsNullState; 1706 mSbDiscState = sbDiscState; 1707 mQsDiscState = qsDiscState; 1708 mDiscContentDesc = discContentDesc; 1709 } 1710 1711 @Override 1712 public String toString() { 1713 return "IconGroup(" + mName + ")"; 1714 } 1715 } 1716 1717 static class State { 1718 boolean connected; 1719 boolean enabled; 1720 boolean activityIn; 1721 boolean activityOut; 1722 int level; 1723 IconGroup iconGroup; 1724 int inetCondition; 1725 int rssi; // Only for logging. 1726 1727 // Not used for comparison, just used for logging. 1728 long time; 1729 1730 public void copyFrom(State state) { 1731 connected = state.connected; 1732 enabled = state.enabled; 1733 level = state.level; 1734 iconGroup = state.iconGroup; 1735 inetCondition = state.inetCondition; 1736 activityIn = state.activityIn; 1737 activityOut = state.activityOut; 1738 rssi = state.rssi; 1739 time = state.time; 1740 } 1741 1742 @Override 1743 public String toString() { 1744 if (time != 0) { 1745 StringBuilder builder = new StringBuilder(); 1746 toString(builder); 1747 return builder.toString(); 1748 } else { 1749 return "Empty " + getClass().getSimpleName(); 1750 } 1751 } 1752 1753 protected void toString(StringBuilder builder) { 1754 builder.append("connected=").append(connected).append(',') 1755 .append("enabled=").append(enabled).append(',') 1756 .append("level=").append(level).append(',') 1757 .append("inetCondition=").append(inetCondition).append(',') 1758 .append("iconGroup=").append(iconGroup).append(',') 1759 .append("activityIn=").append(activityIn).append(',') 1760 .append("activityOut=").append(activityOut).append(',') 1761 .append("rssi=").append(rssi).append(',') 1762 .append("lastModified=").append(DateFormat.format("MM-dd hh:mm:ss", time)); 1763 } 1764 1765 @Override 1766 public boolean equals(Object o) { 1767 if (!o.getClass().equals(getClass())) { 1768 return false; 1769 } 1770 State other = (State) o; 1771 return other.connected == connected 1772 && other.enabled == enabled 1773 && other.level == level 1774 && other.inetCondition == inetCondition 1775 && other.iconGroup == iconGroup 1776 && other.activityIn == activityIn 1777 && other.activityOut == activityOut 1778 && other.rssi == rssi; 1779 } 1780 } 1781 } 1782 1783 public interface SignalCluster { 1784 void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription); 1785 1786 void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon, 1787 String contentDescription, String typeContentDescription, boolean isTypeIconWide, 1788 int subId); 1789 void setSubs(List<SubscriptionInfo> subs); 1790 void setNoSims(boolean show); 1791 1792 void setIsAirplaneMode(boolean is, int airplaneIcon, int contentDescription); 1793 } 1794 1795 public interface EmergencyListener { 1796 void setEmergencyCallsOnly(boolean emergencyOnly); 1797 } 1798 1799 public interface CarrierLabelListener { 1800 void setCarrierLabel(String label); 1801 } 1802 1803 @VisibleForTesting 1804 static class Config { 1805 boolean showAtLeast3G = false; 1806 boolean alwaysShowCdmaRssi = false; 1807 boolean show4gForLte = false; 1808 boolean hspaDataDistinguishable; 1809 1810 static Config readConfig(Context context) { 1811 Config config = new Config(); 1812 Resources res = context.getResources(); 1813 1814 config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G); 1815 config.alwaysShowCdmaRssi = 1816 res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi); 1817 config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE); 1818 config.hspaDataDistinguishable = 1819 res.getBoolean(R.bool.config_hspa_data_distinguishable); 1820 return config; 1821 } 1822 } 1823 } 1824