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 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.res.Resources; 26 import android.net.ConnectivityManager; 27 import android.net.NetworkCapabilities; 28 import android.net.wifi.WifiManager; 29 import android.os.AsyncTask; 30 import android.os.Bundle; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.provider.Settings; 34 import android.telephony.ServiceState; 35 import android.telephony.SubscriptionInfo; 36 import android.telephony.SubscriptionManager; 37 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 38 import android.telephony.TelephonyManager; 39 import android.text.TextUtils; 40 import android.util.Log; 41 import android.util.MathUtils; 42 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.internal.telephony.PhoneConstants; 45 import com.android.internal.telephony.TelephonyIntents; 46 import com.android.systemui.DemoMode; 47 import com.android.systemui.R; 48 49 import java.io.FileDescriptor; 50 import java.io.PrintWriter; 51 import java.util.ArrayList; 52 import java.util.BitSet; 53 import java.util.Collections; 54 import java.util.Comparator; 55 import java.util.HashMap; 56 import java.util.List; 57 import java.util.Locale; 58 import java.util.Map; 59 60 /** Platform implementation of the network controller. **/ 61 public class NetworkControllerImpl extends BroadcastReceiver 62 implements NetworkController, DemoMode { 63 // debug 64 static final String TAG = "NetworkController"; 65 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 66 // additional diagnostics, but not logspew 67 static final boolean CHATTY = Log.isLoggable(TAG + "Chat", Log.DEBUG); 68 69 private static final int EMERGENCY_NO_CONTROLLERS = 0; 70 private static final int EMERGENCY_FIRST_CONTROLLER = 100; 71 private static final int EMERGENCY_VOICE_CONTROLLER = 200; 72 private static final int EMERGENCY_NO_SUB = 300; 73 74 private final Context mContext; 75 private final TelephonyManager mPhone; 76 private final WifiManager mWifiManager; 77 private final ConnectivityManager mConnectivityManager; 78 private final SubscriptionManager mSubscriptionManager; 79 private final boolean mHasMobileDataFeature; 80 private final SubscriptionDefaults mSubDefaults; 81 private Config mConfig; 82 83 // Subcontrollers. 84 @VisibleForTesting 85 final WifiSignalController mWifiSignalController; 86 87 @VisibleForTesting 88 final EthernetSignalController mEthernetSignalController; 89 90 @VisibleForTesting 91 final Map<Integer, MobileSignalController> mMobileSignalControllers = 92 new HashMap<Integer, MobileSignalController>(); 93 // When no SIMs are around at setup, and one is added later, it seems to default to the first 94 // SIM for most actions. This may be null if there aren't any SIMs around. 95 private MobileSignalController mDefaultSignalController; 96 private final AccessPointControllerImpl mAccessPoints; 97 private final MobileDataControllerImpl mMobileDataController; 98 99 private boolean mInetCondition; // Used for Logging and demo. 100 101 // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are 102 // connected and validated, respectively. 103 private final BitSet mConnectedTransports = new BitSet(); 104 private final BitSet mValidatedTransports = new BitSet(); 105 106 // States that don't belong to a subcontroller. 107 private boolean mAirplaneMode = false; 108 private boolean mHasNoSims; 109 private Locale mLocale = null; 110 // This list holds our ordering. 111 private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>(); 112 113 @VisibleForTesting 114 boolean mListening; 115 116 // The current user ID. 117 private int mCurrentUserId; 118 119 private OnSubscriptionsChangedListener mSubscriptionListener; 120 121 // Handler that all broadcasts are received on. 122 private final Handler mReceiverHandler; 123 // Handler that all callbacks are made on. 124 private final CallbackHandler mCallbackHandler; 125 126 private int mEmergencySource; 127 private boolean mIsEmergency; 128 129 @VisibleForTesting 130 ServiceState mLastServiceState; 131 132 /** 133 * Construct this controller object and register for updates. 134 */ 135 public NetworkControllerImpl(Context context, Looper bgLooper) { 136 this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), 137 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), 138 (WifiManager) context.getSystemService(Context.WIFI_SERVICE), 139 SubscriptionManager.from(context), Config.readConfig(context), bgLooper, 140 new CallbackHandler(), 141 new AccessPointControllerImpl(context, bgLooper), 142 new MobileDataControllerImpl(context), 143 new SubscriptionDefaults()); 144 mReceiverHandler.post(mRegisterListeners); 145 } 146 147 @VisibleForTesting 148 NetworkControllerImpl(Context context, ConnectivityManager connectivityManager, 149 TelephonyManager telephonyManager, WifiManager wifiManager, 150 SubscriptionManager subManager, Config config, Looper bgLooper, 151 CallbackHandler callbackHandler, 152 AccessPointControllerImpl accessPointController, 153 MobileDataControllerImpl mobileDataController, 154 SubscriptionDefaults defaultsHandler) { 155 mContext = context; 156 mConfig = config; 157 mReceiverHandler = new Handler(bgLooper); 158 mCallbackHandler = callbackHandler; 159 160 mSubscriptionManager = subManager; 161 mSubDefaults = defaultsHandler; 162 mConnectivityManager = connectivityManager; 163 mHasMobileDataFeature = 164 mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); 165 166 // telephony 167 mPhone = telephonyManager; 168 169 // wifi 170 mWifiManager = wifiManager; 171 172 mLocale = mContext.getResources().getConfiguration().locale; 173 mAccessPoints = accessPointController; 174 mMobileDataController = mobileDataController; 175 mMobileDataController.setNetworkController(this); 176 // TODO: Find a way to move this into MobileDataController. 177 mMobileDataController.setCallback(new MobileDataControllerImpl.Callback() { 178 @Override 179 public void onMobileDataEnabled(boolean enabled) { 180 mCallbackHandler.setMobileDataEnabled(enabled); 181 } 182 }); 183 mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature, 184 mCallbackHandler, this); 185 186 mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this); 187 188 // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it 189 updateAirplaneMode(true /* force callback */); 190 } 191 192 private void registerListeners() { 193 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 194 mobileSignalController.registerListener(); 195 } 196 if (mSubscriptionListener == null) { 197 mSubscriptionListener = new SubListener(); 198 } 199 mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener); 200 201 // broadcasts 202 IntentFilter filter = new IntentFilter(); 203 filter.addAction(WifiManager.RSSI_CHANGED_ACTION); 204 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 205 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 206 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 207 filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 208 filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); 209 filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); 210 filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION); 211 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 212 filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); 213 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); 214 mContext.registerReceiver(this, filter, null, mReceiverHandler); 215 mListening = true; 216 217 updateMobileControllers(); 218 } 219 220 private void unregisterListeners() { 221 mListening = false; 222 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 223 mobileSignalController.unregisterListener(); 224 } 225 mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener); 226 mContext.unregisterReceiver(this); 227 } 228 229 public int getConnectedWifiLevel() { 230 return mWifiSignalController.getState().level; 231 } 232 233 @Override 234 public AccessPointController getAccessPointController() { 235 return mAccessPoints; 236 } 237 238 @Override 239 public MobileDataController getMobileDataController() { 240 return mMobileDataController; 241 } 242 243 public void addEmergencyListener(EmergencyListener listener) { 244 mCallbackHandler.setListening(listener, true); 245 mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly()); 246 } 247 248 public boolean hasMobileDataFeature() { 249 return mHasMobileDataFeature; 250 } 251 252 public boolean hasVoiceCallingFeature() { 253 return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; 254 } 255 256 private MobileSignalController getDataController() { 257 int dataSubId = mSubDefaults.getDefaultDataSubId(); 258 if (!SubscriptionManager.isValidSubscriptionId(dataSubId)) { 259 if (DEBUG) Log.e(TAG, "No data sim selected"); 260 return mDefaultSignalController; 261 } 262 if (mMobileSignalControllers.containsKey(dataSubId)) { 263 return mMobileSignalControllers.get(dataSubId); 264 } 265 if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId); 266 return mDefaultSignalController; 267 } 268 269 public String getMobileDataNetworkName() { 270 MobileSignalController controller = getDataController(); 271 return controller != null ? controller.getState().networkNameData : ""; 272 } 273 274 public boolean isEmergencyOnly() { 275 if (mMobileSignalControllers.size() == 0) { 276 // When there are no active subscriptions, determine emengency state from last 277 // broadcast. 278 mEmergencySource = EMERGENCY_NO_CONTROLLERS; 279 return mLastServiceState != null && mLastServiceState.isEmergencyOnly(); 280 } 281 int voiceSubId = mSubDefaults.getDefaultVoiceSubId(); 282 if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) { 283 for (MobileSignalController mobileSignalController : 284 mMobileSignalControllers.values()) { 285 if (!mobileSignalController.getState().isEmergency) { 286 mEmergencySource = EMERGENCY_FIRST_CONTROLLER 287 + mobileSignalController.mSubscriptionInfo.getSubscriptionId(); 288 if (DEBUG) Log.d(TAG, "Found emergency " + mobileSignalController.mTag); 289 return false; 290 } 291 } 292 } 293 if (mMobileSignalControllers.containsKey(voiceSubId)) { 294 mEmergencySource = EMERGENCY_VOICE_CONTROLLER + voiceSubId; 295 if (DEBUG) Log.d(TAG, "Getting emergency from " + voiceSubId); 296 return mMobileSignalControllers.get(voiceSubId).getState().isEmergency; 297 } 298 if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId); 299 mEmergencySource = EMERGENCY_NO_SUB + voiceSubId; 300 // Something is wrong, better assume we can't make calls... 301 return true; 302 } 303 304 /** 305 * Emergency status may have changed (triggered by MobileSignalController), 306 * so we should recheck and send out the state to listeners. 307 */ 308 void recalculateEmergency() { 309 mIsEmergency = isEmergencyOnly(); 310 mCallbackHandler.setEmergencyCallsOnly(mIsEmergency); 311 } 312 313 public void addSignalCallback(SignalCallback cb) { 314 mCallbackHandler.setListening(cb, true); 315 mCallbackHandler.setSubs(mCurrentSubscriptions); 316 mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode, 317 TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); 318 mCallbackHandler.setNoSims(mHasNoSims); 319 mWifiSignalController.notifyListeners(); 320 mEthernetSignalController.notifyListeners(); 321 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 322 mobileSignalController.notifyListeners(); 323 } 324 } 325 326 @Override 327 public void removeSignalCallback(SignalCallback cb) { 328 mCallbackHandler.setListening(cb, false); 329 } 330 331 @Override 332 public void setWifiEnabled(final boolean enabled) { 333 new AsyncTask<Void, Void, Void>() { 334 @Override 335 protected Void doInBackground(Void... args) { 336 // Disable tethering if enabling Wifi 337 final int wifiApState = mWifiManager.getWifiApState(); 338 if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || 339 (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { 340 mWifiManager.setWifiApEnabled(null, false); 341 } 342 343 mWifiManager.setWifiEnabled(enabled); 344 return null; 345 } 346 }.execute(); 347 } 348 349 @Override 350 public void onUserSwitched(int newUserId) { 351 mCurrentUserId = newUserId; 352 mAccessPoints.onUserSwitched(newUserId); 353 updateConnectivity(); 354 } 355 356 @Override 357 public void onReceive(Context context, Intent intent) { 358 if (CHATTY) { 359 Log.d(TAG, "onReceive: intent=" + intent); 360 } 361 final String action = intent.getAction(); 362 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || 363 action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { 364 updateConnectivity(); 365 } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { 366 refreshLocale(); 367 updateAirplaneMode(false); 368 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) { 369 // We are using different subs now, we might be able to make calls. 370 recalculateEmergency(); 371 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { 372 // Notify every MobileSignalController so they can know whether they are the 373 // data sim or not. 374 for (MobileSignalController controller : mMobileSignalControllers.values()) { 375 controller.handleBroadcast(intent); 376 } 377 } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { 378 // Might have different subscriptions now. 379 updateMobileControllers(); 380 } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) { 381 mLastServiceState = ServiceState.newFromBundle(intent.getExtras()); 382 if (mMobileSignalControllers.size() == 0) { 383 // If none of the subscriptions are active, we might need to recalculate 384 // emergency state. 385 recalculateEmergency(); 386 } 387 } else { 388 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, 389 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 390 if (SubscriptionManager.isValidSubscriptionId(subId)) { 391 if (mMobileSignalControllers.containsKey(subId)) { 392 mMobileSignalControllers.get(subId).handleBroadcast(intent); 393 } else { 394 // Can't find this subscription... We must be out of date. 395 updateMobileControllers(); 396 } 397 } else { 398 // No sub id, must be for the wifi. 399 mWifiSignalController.handleBroadcast(intent); 400 } 401 } 402 } 403 404 public void onConfigurationChanged() { 405 mConfig = Config.readConfig(mContext); 406 mReceiverHandler.post(new Runnable() { 407 @Override 408 public void run() { 409 handleConfigurationChanged(); 410 } 411 }); 412 } 413 414 @VisibleForTesting 415 void handleConfigurationChanged() { 416 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 417 mobileSignalController.setConfiguration(mConfig); 418 } 419 refreshLocale(); 420 } 421 422 private void updateMobileControllers() { 423 if (!mListening) { 424 return; 425 } 426 doUpdateMobileControllers(); 427 } 428 429 @VisibleForTesting 430 void doUpdateMobileControllers() { 431 List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList(); 432 if (subscriptions == null) { 433 subscriptions = Collections.emptyList(); 434 } 435 // If there have been no relevant changes to any of the subscriptions, we can leave as is. 436 if (hasCorrectMobileControllers(subscriptions)) { 437 // Even if the controllers are correct, make sure we have the right no sims state. 438 // Such as on boot, don't need any controllers, because there are no sims, 439 // but we still need to update the no sim state. 440 updateNoSims(); 441 return; 442 } 443 setCurrentSubscriptions(subscriptions); 444 updateNoSims(); 445 recalculateEmergency(); 446 } 447 448 @VisibleForTesting 449 protected void updateNoSims() { 450 boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0; 451 if (hasNoSims != mHasNoSims) { 452 mHasNoSims = hasNoSims; 453 mCallbackHandler.setNoSims(mHasNoSims); 454 } 455 } 456 457 @VisibleForTesting 458 void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) { 459 Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() { 460 @Override 461 public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) { 462 return lhs.getSimSlotIndex() == rhs.getSimSlotIndex() 463 ? lhs.getSubscriptionId() - rhs.getSubscriptionId() 464 : lhs.getSimSlotIndex() - rhs.getSimSlotIndex(); 465 } 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.remove(subId)); 478 } else { 479 MobileSignalController controller = new MobileSignalController(mContext, mConfig, 480 mHasMobileDataFeature, mPhone, mCallbackHandler, 481 this, subscriptions.get(i), mSubDefaults, mReceiverHandler.getLooper()); 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 mCallbackHandler.setSubs(subscriptions); 500 notifyAllListeners(); 501 502 // There may be new MobileSignalControllers around, make sure they get the current 503 // inet condition and airplane mode. 504 pushConnectivityToSignals(); 505 updateAirplaneMode(true /* force */); 506 } 507 508 @VisibleForTesting 509 boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) { 510 if (allSubscriptions.size() != mMobileSignalControllers.size()) { 511 return false; 512 } 513 for (SubscriptionInfo info : allSubscriptions) { 514 if (!mMobileSignalControllers.containsKey(info.getSubscriptionId())) { 515 return false; 516 } 517 } 518 return true; 519 } 520 521 private void updateAirplaneMode(boolean force) { 522 boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(), 523 Settings.Global.AIRPLANE_MODE_ON, 0) == 1); 524 if (airplaneMode != mAirplaneMode || force) { 525 mAirplaneMode = airplaneMode; 526 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 527 mobileSignalController.setAirplaneMode(mAirplaneMode); 528 } 529 notifyListeners(); 530 } 531 } 532 533 private void refreshLocale() { 534 Locale current = mContext.getResources().getConfiguration().locale; 535 if (!current.equals(mLocale)) { 536 mLocale = current; 537 notifyAllListeners(); 538 } 539 } 540 541 /** 542 * Forces update of all callbacks on both SignalClusters and 543 * NetworkSignalChangedCallbacks. 544 */ 545 private void notifyAllListeners() { 546 notifyListeners(); 547 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 548 mobileSignalController.notifyListeners(); 549 } 550 mWifiSignalController.notifyListeners(); 551 mEthernetSignalController.notifyListeners(); 552 } 553 554 /** 555 * Notifies listeners of changes in state of to the NetworkController, but 556 * does not notify for any info on SignalControllers, for that call 557 * notifyAllListeners. 558 */ 559 private void notifyListeners() { 560 mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode, 561 TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); 562 mCallbackHandler.setNoSims(mHasNoSims); 563 } 564 565 /** 566 * Update the Inet conditions and what network we are connected to. 567 */ 568 private void updateConnectivity() { 569 mConnectedTransports.clear(); 570 mValidatedTransports.clear(); 571 for (NetworkCapabilities nc : 572 mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) { 573 for (int transportType : nc.getTransportTypes()) { 574 mConnectedTransports.set(transportType); 575 if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) { 576 mValidatedTransports.set(transportType); 577 } 578 } 579 } 580 581 if (CHATTY) { 582 Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports); 583 Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports); 584 } 585 586 mInetCondition = !mValidatedTransports.isEmpty(); 587 588 pushConnectivityToSignals(); 589 } 590 591 /** 592 * Pushes the current connectivity state to all SignalControllers. 593 */ 594 private void pushConnectivityToSignals() { 595 // We want to update all the icons, all at once, for any condition change 596 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 597 mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); 598 } 599 mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); 600 mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); 601 } 602 603 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 604 pw.println("NetworkController state:"); 605 606 pw.println(" - telephony ------"); 607 pw.print(" hasVoiceCallingFeature()="); 608 pw.println(hasVoiceCallingFeature()); 609 610 pw.println(" - connectivity ------"); 611 pw.print(" mConnectedTransports="); 612 pw.println(mConnectedTransports); 613 pw.print(" mValidatedTransports="); 614 pw.println(mValidatedTransports); 615 pw.print(" mInetCondition="); 616 pw.println(mInetCondition); 617 pw.print(" mAirplaneMode="); 618 pw.println(mAirplaneMode); 619 pw.print(" mLocale="); 620 pw.println(mLocale); 621 pw.print(" mLastServiceState="); 622 pw.println(mLastServiceState); 623 pw.print(" mIsEmergency="); 624 pw.println(mIsEmergency); 625 pw.print(" mEmergencySource="); 626 pw.println(emergencyToString(mEmergencySource)); 627 628 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 629 mobileSignalController.dump(pw); 630 } 631 mWifiSignalController.dump(pw); 632 633 mEthernetSignalController.dump(pw); 634 635 mAccessPoints.dump(pw); 636 } 637 638 private static final String emergencyToString(int emergencySource) { 639 if (emergencySource > EMERGENCY_NO_SUB) { 640 return "NO_SUB(" + (emergencySource - EMERGENCY_NO_SUB) + ")"; 641 } else if (emergencySource > EMERGENCY_VOICE_CONTROLLER) { 642 return "VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")"; 643 } else if (emergencySource > EMERGENCY_FIRST_CONTROLLER) { 644 return "FIRST_CONTROLLER(" + (emergencySource - EMERGENCY_FIRST_CONTROLLER) + ")"; 645 } else if (emergencySource == EMERGENCY_NO_CONTROLLERS) { 646 return "NO_CONTROLLERS"; 647 } 648 return "UNKNOWN_SOURCE"; 649 } 650 651 private boolean mDemoMode; 652 private boolean mDemoInetCondition; 653 private WifiSignalController.WifiState mDemoWifiState; 654 655 @Override 656 public void dispatchDemoCommand(String command, Bundle args) { 657 if (!mDemoMode && command.equals(COMMAND_ENTER)) { 658 if (DEBUG) Log.d(TAG, "Entering demo mode"); 659 unregisterListeners(); 660 mDemoMode = true; 661 mDemoInetCondition = mInetCondition; 662 mDemoWifiState = mWifiSignalController.getState(); 663 } else if (mDemoMode && command.equals(COMMAND_EXIT)) { 664 if (DEBUG) Log.d(TAG, "Exiting demo mode"); 665 mDemoMode = false; 666 // Update what MobileSignalControllers, because they may change 667 // to set the number of sim slots. 668 updateMobileControllers(); 669 for (MobileSignalController controller : mMobileSignalControllers.values()) { 670 controller.resetLastState(); 671 } 672 mWifiSignalController.resetLastState(); 673 mReceiverHandler.post(mRegisterListeners); 674 notifyAllListeners(); 675 } else if (mDemoMode && command.equals(COMMAND_NETWORK)) { 676 String airplane = args.getString("airplane"); 677 if (airplane != null) { 678 boolean show = airplane.equals("show"); 679 mCallbackHandler.setIsAirplaneMode(new IconState(show, 680 TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, 681 mContext)); 682 } 683 String fully = args.getString("fully"); 684 if (fully != null) { 685 mDemoInetCondition = Boolean.parseBoolean(fully); 686 BitSet connected = new BitSet(); 687 688 if (mDemoInetCondition) { 689 connected.set(mWifiSignalController.mTransportType); 690 } 691 mWifiSignalController.updateConnectivity(connected, connected); 692 for (MobileSignalController controller : mMobileSignalControllers.values()) { 693 if (mDemoInetCondition) { 694 connected.set(controller.mTransportType); 695 } 696 controller.updateConnectivity(connected, connected); 697 } 698 } 699 String wifi = args.getString("wifi"); 700 if (wifi != null) { 701 boolean show = wifi.equals("show"); 702 String level = args.getString("level"); 703 if (level != null) { 704 mDemoWifiState.level = level.equals("null") ? -1 705 : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); 706 mDemoWifiState.connected = mDemoWifiState.level >= 0; 707 } 708 mDemoWifiState.enabled = show; 709 mWifiSignalController.notifyListeners(); 710 } 711 String sims = args.getString("sims"); 712 if (sims != null) { 713 int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); 714 List<SubscriptionInfo> subs = new ArrayList<>(); 715 if (num != mMobileSignalControllers.size()) { 716 mMobileSignalControllers.clear(); 717 int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax(); 718 for (int i = start /* get out of normal index range */; i < start + num; i++) { 719 subs.add(addSignalController(i, i)); 720 } 721 mCallbackHandler.setSubs(subs); 722 } 723 } 724 String nosim = args.getString("nosim"); 725 if (nosim != null) { 726 mHasNoSims = nosim.equals("show"); 727 mCallbackHandler.setNoSims(mHasNoSims); 728 } 729 String mobile = args.getString("mobile"); 730 if (mobile != null) { 731 boolean show = mobile.equals("show"); 732 String datatype = args.getString("datatype"); 733 String slotString = args.getString("slot"); 734 int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString); 735 slot = MathUtils.constrain(slot, 0, 8); 736 // Ensure we have enough sim slots 737 List<SubscriptionInfo> subs = new ArrayList<>(); 738 while (mMobileSignalControllers.size() <= slot) { 739 int nextSlot = mMobileSignalControllers.size(); 740 subs.add(addSignalController(nextSlot, nextSlot)); 741 } 742 if (!subs.isEmpty()) { 743 mCallbackHandler.setSubs(subs); 744 } 745 // Hack to index linearly for easy use. 746 MobileSignalController controller = mMobileSignalControllers 747 .values().toArray(new MobileSignalController[0])[slot]; 748 controller.getState().dataSim = datatype != null; 749 if (datatype != null) { 750 controller.getState().iconGroup = 751 datatype.equals("1x") ? TelephonyIcons.ONE_X : 752 datatype.equals("3g") ? TelephonyIcons.THREE_G : 753 datatype.equals("4g") ? TelephonyIcons.FOUR_G : 754 datatype.equals("e") ? TelephonyIcons.E : 755 datatype.equals("g") ? TelephonyIcons.G : 756 datatype.equals("h") ? TelephonyIcons.H : 757 datatype.equals("lte") ? TelephonyIcons.LTE : 758 datatype.equals("roam") ? TelephonyIcons.ROAMING : 759 TelephonyIcons.UNKNOWN; 760 } 761 int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH; 762 String level = args.getString("level"); 763 if (level != null) { 764 controller.getState().level = level.equals("null") ? -1 765 : Math.min(Integer.parseInt(level), icons[0].length - 1); 766 controller.getState().connected = controller.getState().level >= 0; 767 } 768 controller.getState().enabled = show; 769 controller.notifyListeners(); 770 } 771 String carrierNetworkChange = args.getString("carriernetworkchange"); 772 if (carrierNetworkChange != null) { 773 boolean show = carrierNetworkChange.equals("show"); 774 for (MobileSignalController controller : mMobileSignalControllers.values()) { 775 controller.setCarrierNetworkChangeMode(show); 776 } 777 } 778 } 779 } 780 781 private SubscriptionInfo addSignalController(int id, int simSlotIndex) { 782 SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0, 783 null, 0, 0, ""); 784 mMobileSignalControllers.put(id, new MobileSignalController(mContext, 785 mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info, 786 mSubDefaults, mReceiverHandler.getLooper())); 787 return info; 788 } 789 790 private class SubListener extends OnSubscriptionsChangedListener { 791 @Override 792 public void onSubscriptionsChanged() { 793 updateMobileControllers(); 794 } 795 } 796 797 /** 798 * Used to register listeners from the BG Looper, this way the PhoneStateListeners that 799 * get created will also run on the BG Looper. 800 */ 801 private final Runnable mRegisterListeners = new Runnable() { 802 @Override 803 public void run() { 804 registerListeners(); 805 } 806 }; 807 808 public interface EmergencyListener { 809 void setEmergencyCallsOnly(boolean emergencyOnly); 810 } 811 812 public static class SubscriptionDefaults { 813 public int getDefaultVoiceSubId() { 814 return SubscriptionManager.getDefaultVoiceSubId(); 815 } 816 817 public int getDefaultDataSubId() { 818 return SubscriptionManager.getDefaultDataSubId(); 819 } 820 } 821 822 @VisibleForTesting 823 static class Config { 824 boolean showAtLeast3G = false; 825 boolean alwaysShowCdmaRssi = false; 826 boolean show4gForLte = false; 827 boolean hspaDataDistinguishable; 828 829 static Config readConfig(Context context) { 830 Config config = new Config(); 831 Resources res = context.getResources(); 832 833 config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G); 834 config.alwaysShowCdmaRssi = 835 res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi); 836 config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE); 837 config.hspaDataDistinguishable = 838 res.getBoolean(R.bool.config_hspa_data_distinguishable); 839 return config; 840 } 841 } 842 } 843