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