1 /* 2 * Copyright 2018 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.internal.telephony.data; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.ServiceConnection; 26 import android.os.AsyncResult; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.PersistableBundle; 32 import android.os.Registrant; 33 import android.os.RegistrantList; 34 import android.os.RemoteException; 35 import android.telephony.AccessNetworkConstants; 36 import android.telephony.AccessNetworkConstants.AccessNetworkType; 37 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; 38 import android.telephony.AccessNetworkConstants.TransportType; 39 import android.telephony.Annotation.ApnType; 40 import android.telephony.Annotation.NetCapability; 41 import android.telephony.AnomalyReporter; 42 import android.telephony.CarrierConfigManager; 43 import android.telephony.data.ApnSetting; 44 import android.telephony.data.IQualifiedNetworksService; 45 import android.telephony.data.IQualifiedNetworksServiceCallback; 46 import android.telephony.data.QualifiedNetworksService; 47 import android.telephony.data.ThrottleStatus; 48 import android.text.TextUtils; 49 import android.util.ArraySet; 50 import android.util.IndentingPrintWriter; 51 import android.util.LocalLog; 52 import android.util.SparseArray; 53 54 import com.android.internal.telephony.Phone; 55 import com.android.internal.telephony.SlidingWindowEventCounter; 56 import com.android.telephony.Rlog; 57 58 import java.io.FileDescriptor; 59 import java.io.PrintWriter; 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.List; 63 import java.util.Map; 64 import java.util.Set; 65 import java.util.UUID; 66 import java.util.concurrent.ConcurrentHashMap; 67 import java.util.concurrent.Executor; 68 import java.util.stream.Collectors; 69 70 /** 71 * Access network manager manages the qualified/available networks for mobile data connection. 72 * It binds to the vendor's qualified networks service and actively monitors the qualified 73 * networks changes. 74 */ 75 public class AccessNetworksManager extends Handler { 76 private static final boolean DBG = false; 77 78 /** Event to guide a transport type for initial data connection of emergency data network. */ 79 private static final int EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY = 1; 80 81 /** 82 * The counters to detect frequent QNS attempt to change preferred network transport by ApnType. 83 */ 84 private final @NonNull SparseArray<SlidingWindowEventCounter> mApnTypeToQnsChangeNetworkCounter; 85 86 private final String mLogTag; 87 private final LocalLog mLocalLog = new LocalLog(64); 88 private final UUID mAnomalyUUID = UUID.fromString("c2d1a639-00e2-4561-9619-6acf37d90590"); 89 private String mLastBoundPackageName; 90 91 public static final int[] SUPPORTED_APN_TYPES = { 92 ApnSetting.TYPE_DEFAULT, 93 ApnSetting.TYPE_MMS, 94 ApnSetting.TYPE_FOTA, 95 ApnSetting.TYPE_IMS, 96 ApnSetting.TYPE_CBS, 97 ApnSetting.TYPE_SUPL, 98 ApnSetting.TYPE_EMERGENCY, 99 ApnSetting.TYPE_XCAP, 100 ApnSetting.TYPE_DUN 101 }; 102 103 private final Phone mPhone; 104 105 private final CarrierConfigManager mCarrierConfigManager; 106 107 private @Nullable DataConfigManager mDataConfigManager; 108 109 private IQualifiedNetworksService mIQualifiedNetworksService; 110 111 private AccessNetworksManagerDeathRecipient mDeathRecipient; 112 113 private String mTargetBindingPackageName; 114 115 private QualifiedNetworksServiceConnection mServiceConnection; 116 117 // Available networks. Key is the APN type. 118 private final SparseArray<int[]> mAvailableNetworks = new SparseArray<>(); 119 120 private final @TransportType int[] mAvailableTransports; 121 122 private final RegistrantList mQualifiedNetworksChangedRegistrants = new RegistrantList(); 123 124 /** 125 * The preferred transport of the APN type. The key is the APN type, and the value is the 126 * transport. The preferred transports are updated as soon as QNS changes the preference. 127 */ 128 private final Map<Integer, Integer> mPreferredTransports = new ConcurrentHashMap<>(); 129 130 /** 131 * Callbacks for passing information to interested clients. 132 */ 133 private final @NonNull Set<AccessNetworksManagerCallback> mAccessNetworksManagerCallbacks = 134 new ArraySet<>(); 135 136 /** 137 * Represents qualified network types list on a specific APN type. 138 */ 139 public static class QualifiedNetworks { 140 public final @ApnType int apnType; 141 // The qualified networks in preferred order. Each network is a AccessNetworkType. 142 public final @NonNull @RadioAccessNetworkType int[] qualifiedNetworks; QualifiedNetworks(@pnType int apnType, @NonNull int[] qualifiedNetworks)143 public QualifiedNetworks(@ApnType int apnType, @NonNull int[] qualifiedNetworks) { 144 this.apnType = apnType; 145 this.qualifiedNetworks = Arrays.stream(qualifiedNetworks) 146 .boxed() 147 .filter(DataUtils::isValidAccessNetwork) 148 .mapToInt(Integer::intValue) 149 .toArray(); 150 } 151 152 @Override toString()153 public String toString() { 154 return "[QualifiedNetworks: apnType=" 155 + ApnSetting.getApnTypeString(apnType) 156 + ", networks=" 157 + Arrays.stream(qualifiedNetworks) 158 .mapToObj(AccessNetworkType::toString) 159 .collect(Collectors.joining(",")) 160 + "]"; 161 } 162 } 163 164 @Override handleMessage(@onNull Message msg)165 public void handleMessage(@NonNull Message msg) { 166 switch (msg.what) { 167 case EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY: 168 AsyncResult ar = (AsyncResult) msg.obj; 169 int transport = (int) ar.result; 170 onEmergencyDataNetworkPreferredTransportChanged(transport); 171 break; 172 default: 173 loge("Unexpected event " + msg.what); 174 } 175 } 176 177 private class AccessNetworksManagerDeathRecipient implements IBinder.DeathRecipient { 178 @Override binderDied()179 public void binderDied() { 180 // TODO: try to rebind the service. 181 String message = "Qualified network service " + mLastBoundPackageName + " died."; 182 // clear the anomaly report counters when QNS crash 183 mApnTypeToQnsChangeNetworkCounter.clear(); 184 loge(message); 185 AnomalyReporter.reportAnomaly(mAnomalyUUID, message, mPhone.getCarrierId()); 186 } 187 } 188 189 private final class QualifiedNetworksServiceConnection implements ServiceConnection { 190 @Override onServiceConnected(ComponentName name, IBinder service)191 public void onServiceConnected(ComponentName name, IBinder service) { 192 if (DBG) log("onServiceConnected " + name); 193 mIQualifiedNetworksService = IQualifiedNetworksService.Stub.asInterface(service); 194 mDeathRecipient = new AccessNetworksManagerDeathRecipient(); 195 mLastBoundPackageName = getQualifiedNetworksServicePackageName(); 196 197 try { 198 service.linkToDeath(mDeathRecipient, 0 /* flags */); 199 mIQualifiedNetworksService.createNetworkAvailabilityProvider(mPhone.getPhoneId(), 200 new QualifiedNetworksServiceCallback()); 201 } catch (RemoteException e) { 202 loge("Remote exception. " + e); 203 } 204 } 205 206 @Override onServiceDisconnected(ComponentName name)207 public void onServiceDisconnected(ComponentName name) { 208 if (DBG) log("onServiceDisconnected " + name); 209 mTargetBindingPackageName = null; 210 } 211 212 } 213 214 private final class QualifiedNetworksServiceCallback extends 215 IQualifiedNetworksServiceCallback.Stub { 216 @Override onQualifiedNetworkTypesChanged(int apnTypes, @NonNull int[] qualifiedNetworkTypes)217 public void onQualifiedNetworkTypesChanged(int apnTypes, 218 @NonNull int[] qualifiedNetworkTypes) { 219 if (qualifiedNetworkTypes == null) { 220 loge("onQualifiedNetworkTypesChanged: Ignored null input."); 221 return; 222 } 223 224 log("onQualifiedNetworkTypesChanged: apnTypes = [" 225 + ApnSetting.getApnTypesStringFromBitmask(apnTypes) 226 + "], networks = [" + Arrays.stream(qualifiedNetworkTypes) 227 .mapToObj(AccessNetworkType::toString).collect(Collectors.joining(",")) 228 + "]"); 229 230 if (Arrays.stream(qualifiedNetworkTypes).anyMatch(accessNetwork 231 -> !DataUtils.isValidAccessNetwork(accessNetwork))) { 232 loge("Invalid access networks " + Arrays.toString(qualifiedNetworkTypes)); 233 if (mDataConfigManager != null 234 && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) { 235 reportAnomaly("QNS requested invalid Network Type", 236 "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec4"); 237 } 238 return; 239 } 240 241 List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>(); 242 int satisfiedApnTypes = 0; 243 for (int apnType : SUPPORTED_APN_TYPES) { 244 if ((apnTypes & apnType) == apnType) { 245 // skip the APN anomaly detection if not using the T data stack 246 if (mDataConfigManager != null) { 247 satisfiedApnTypes |= apnType; 248 } 249 250 if (mAvailableNetworks.get(apnType) != null) { 251 if (Arrays.equals(mAvailableNetworks.get(apnType), 252 qualifiedNetworkTypes)) { 253 log("Available networks for " 254 + ApnSetting.getApnTypesStringFromBitmask(apnType) 255 + " not changed."); 256 continue; 257 } 258 } 259 260 // Empty array indicates QNS did not suggest any qualified networks. In this 261 // case all network requests will be routed to cellular. 262 if (qualifiedNetworkTypes.length == 0) { 263 mAvailableNetworks.remove(apnType); 264 if (getPreferredTransport(apnType) 265 == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { 266 mPreferredTransports.put(apnType, 267 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 268 mAccessNetworksManagerCallbacks.forEach(callback -> 269 callback.invokeFromExecutor(() -> 270 callback.onPreferredTransportChanged(DataUtils 271 .apnTypeToNetworkCapability(apnType)))); 272 } 273 } else { 274 mAvailableNetworks.put(apnType, qualifiedNetworkTypes); 275 qualifiedNetworksList.add(new QualifiedNetworks(apnType, 276 qualifiedNetworkTypes)); 277 278 } 279 } 280 } 281 282 // Report anomaly if any requested APN types are unsatisfied 283 if (satisfiedApnTypes != apnTypes 284 && mDataConfigManager != null 285 && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) { 286 int unsatisfied = satisfiedApnTypes ^ apnTypes; 287 reportAnomaly("QNS requested unsupported APN Types:" 288 + Integer.toBinaryString(unsatisfied), 289 "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec5"); 290 } 291 292 if (!qualifiedNetworksList.isEmpty()) { 293 setPreferredTransports(qualifiedNetworksList); 294 mQualifiedNetworksChangedRegistrants.notifyResult(qualifiedNetworksList); 295 } 296 } 297 } 298 onEmergencyDataNetworkPreferredTransportChanged( @ccessNetworkConstants.TransportType int transportType)299 private void onEmergencyDataNetworkPreferredTransportChanged( 300 @AccessNetworkConstants.TransportType int transportType) { 301 try { 302 logl("onEmergencyDataNetworkPreferredTransportChanged: " 303 + AccessNetworkConstants.transportTypeToString(transportType)); 304 if (mIQualifiedNetworksService != null) { 305 mIQualifiedNetworksService.reportEmergencyDataNetworkPreferredTransportChanged( 306 mPhone.getPhoneId(), transportType); 307 } 308 } catch (Exception ex) { 309 loge("onEmergencyDataNetworkPreferredTransportChanged: ", ex); 310 } 311 } 312 313 /** 314 * Access networks manager callback. This should be only used by {@link DataNetworkController}. 315 */ 316 public abstract static class AccessNetworksManagerCallback extends DataCallback { 317 /** 318 * Constructor 319 * 320 * @param executor The executor of the callback. 321 */ AccessNetworksManagerCallback(@onNull @allbackExecutor Executor executor)322 public AccessNetworksManagerCallback(@NonNull @CallbackExecutor Executor executor) { 323 super(executor); 324 } 325 326 /** 327 * Called when preferred transport changed. 328 * 329 * @param networkCapability The network capability. 330 */ onPreferredTransportChanged(@etCapability int networkCapability)331 public abstract void onPreferredTransportChanged(@NetCapability int networkCapability); 332 } 333 334 /** 335 * Constructor 336 * 337 * @param phone The phone object. 338 * @param looper Looper for the handler. 339 */ AccessNetworksManager(@onNull Phone phone, @NonNull Looper looper)340 public AccessNetworksManager(@NonNull Phone phone, @NonNull Looper looper) { 341 super(looper); 342 mPhone = phone; 343 mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService( 344 Context.CARRIER_CONFIG_SERVICE); 345 mLogTag = "ANM-" + mPhone.getPhoneId(); 346 mApnTypeToQnsChangeNetworkCounter = new SparseArray<>(); 347 mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 348 AccessNetworkConstants.TRANSPORT_TYPE_WLAN}; 349 350 // bindQualifiedNetworksService posts real work to handler thread. So here we can 351 // let the callback execute in binder thread to avoid post twice. 352 mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run, 353 (slotIndex, subId, carrierId, specificCarrierId) -> { 354 if (slotIndex != mPhone.getPhoneId()) return; 355 // We should wait for carrier config changed event because the target binding 356 // package name can come from the carrier config. Note that we still get this 357 // event even when SIM is absent. 358 if (DBG) log("Carrier config changed. Try to bind qualified network service."); 359 bindQualifiedNetworksService(); 360 }); 361 bindQualifiedNetworksService(); 362 363 // Using post to delay the registering because data retry manager and data config 364 // manager instances are created later than access networks manager. 365 post(() -> { 366 mPhone.getDataNetworkController().getDataRetryManager().registerCallback( 367 new DataRetryManager.DataRetryManagerCallback(this::post) { 368 @Override 369 public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) { 370 try { 371 logl("onThrottleStatusChanged: " + throttleStatuses); 372 if (mIQualifiedNetworksService != null) { 373 mIQualifiedNetworksService.reportThrottleStatusChanged( 374 mPhone.getPhoneId(), throttleStatuses); 375 } 376 } catch (Exception ex) { 377 loge("onThrottleStatusChanged: ", ex); 378 } 379 } 380 }); 381 mDataConfigManager = mPhone.getDataNetworkController().getDataConfigManager(); 382 mDataConfigManager.registerCallback( 383 new DataConfigManager.DataConfigManagerCallback(this::post) { 384 @Override 385 public void onDeviceConfigChanged() { 386 mApnTypeToQnsChangeNetworkCounter.clear(); 387 } 388 }); 389 mPhone.registerForEmergencyDomainSelected( 390 this, EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY, null); 391 }); 392 } 393 394 /** 395 * Trigger the anomaly report with the specified UUID. 396 * 397 * @param anomalyMsg Description of the event 398 * @param uuid UUID associated with that event 399 */ reportAnomaly(@onNull String anomalyMsg, @NonNull String uuid)400 private void reportAnomaly(@NonNull String anomalyMsg, @NonNull String uuid) { 401 logl(anomalyMsg); 402 AnomalyReporter.reportAnomaly(UUID.fromString(uuid), anomalyMsg, mPhone.getCarrierId()); 403 } 404 405 /** 406 * Find the qualified network service from configuration and binds to it. It reads the 407 * configuration from carrier config if it exists. If not, read it from resources. 408 */ bindQualifiedNetworksService()409 private void bindQualifiedNetworksService() { 410 post(() -> { 411 Intent intent = null; 412 String packageName = getQualifiedNetworksServicePackageName(); 413 String className = getQualifiedNetworksServiceClassName(); 414 415 if (DBG) log("Qualified network service package = " + packageName); 416 if (TextUtils.isEmpty(packageName)) { 417 loge("Can't find the binding package"); 418 return; 419 } 420 421 if (TextUtils.isEmpty(className)) { 422 intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE); 423 intent.setPackage(packageName); 424 } else { 425 ComponentName cm = new ComponentName(packageName, className); 426 intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE) 427 .setComponent(cm); 428 } 429 430 if (TextUtils.equals(packageName, mTargetBindingPackageName)) { 431 if (DBG) log("Service " + packageName + " already bound or being bound."); 432 return; 433 } 434 435 if (mIQualifiedNetworksService != null 436 && mIQualifiedNetworksService.asBinder().isBinderAlive()) { 437 // Remove the network availability updater and then unbind the service. 438 try { 439 mIQualifiedNetworksService.removeNetworkAvailabilityProvider( 440 mPhone.getPhoneId()); 441 } catch (RemoteException e) { 442 loge("Cannot remove network availability updater. " + e); 443 } 444 445 mPhone.getContext().unbindService(mServiceConnection); 446 } 447 448 try { 449 mServiceConnection = new QualifiedNetworksServiceConnection(); 450 log("bind to " + packageName); 451 if (!mPhone.getContext().bindService(intent, mServiceConnection, 452 Context.BIND_AUTO_CREATE)) { 453 loge("Cannot bind to the qualified networks service."); 454 return; 455 } 456 mTargetBindingPackageName = packageName; 457 } catch (Exception e) { 458 loge("Cannot bind to the qualified networks service. Exception: " + e); 459 } 460 }); 461 } 462 463 /** 464 * Get the qualified network service package. 465 * 466 * @return package name of the qualified networks service package. 467 */ getQualifiedNetworksServicePackageName()468 private String getQualifiedNetworksServicePackageName() { 469 // Read package name from the resource 470 String packageName = mPhone.getContext().getResources().getString( 471 com.android.internal.R.string.config_qualified_networks_service_package); 472 473 PersistableBundle b; 474 try { 475 b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(), 476 CarrierConfigManager 477 .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING); 478 if (b != null && !b.isEmpty()) { 479 // If carrier config overrides it, use the one from carrier config 480 String carrierConfigPackageName = b.getString(CarrierConfigManager 481 .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING); 482 if (!TextUtils.isEmpty(carrierConfigPackageName)) { 483 if (DBG) log("Found carrier config override " + carrierConfigPackageName); 484 packageName = carrierConfigPackageName; 485 } 486 } 487 } catch (RuntimeException e) { 488 loge("Carrier config loader is not available."); 489 } 490 491 return packageName; 492 } 493 494 /** 495 * Get the qualified network service class name. 496 * 497 * @return class name of the qualified networks service package. 498 */ getQualifiedNetworksServiceClassName()499 private String getQualifiedNetworksServiceClassName() { 500 // Read package name from the resource 501 String className = mPhone.getContext().getResources().getString( 502 com.android.internal.R.string.config_qualified_networks_service_class); 503 504 PersistableBundle b; 505 try { 506 b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(), 507 CarrierConfigManager 508 .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING); 509 if (b != null && !b.isEmpty()) { 510 // If carrier config overrides it, use the one from carrier config 511 String carrierConfigClassName = b.getString(CarrierConfigManager 512 .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING); 513 if (!TextUtils.isEmpty(carrierConfigClassName)) { 514 if (DBG) log("Found carrier config override " + carrierConfigClassName); 515 className = carrierConfigClassName; 516 } 517 } 518 } catch (RuntimeException e) { 519 loge("Carrier config loader is not available."); 520 } 521 522 return className; 523 } 524 getQualifiedNetworksList()525 private @NonNull List<QualifiedNetworks> getQualifiedNetworksList() { 526 List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>(); 527 for (int i = 0; i < mAvailableNetworks.size(); i++) { 528 qualifiedNetworksList.add(new QualifiedNetworks(mAvailableNetworks.keyAt(i), 529 mAvailableNetworks.valueAt(i))); 530 } 531 532 return qualifiedNetworksList; 533 } 534 535 /** 536 * Register for qualified networks changed event. 537 * 538 * @param h The target to post the event message to. 539 * @param what The event. 540 */ registerForQualifiedNetworksChanged(Handler h, int what)541 public void registerForQualifiedNetworksChanged(Handler h, int what) { 542 if (h != null) { 543 Registrant r = new Registrant(h, what, null); 544 mQualifiedNetworksChangedRegistrants.add(r); 545 546 // Notify for the first time if there is already something in the available network 547 // list. 548 if (mAvailableNetworks.size() != 0) { 549 r.notifyResult(getQualifiedNetworksList()); 550 } 551 } 552 } 553 554 /** 555 * @return The available transports. 556 */ getAvailableTransports()557 public @NonNull int[] getAvailableTransports() { 558 return mAvailableTransports; 559 } 560 getTransportFromAccessNetwork(int accessNetwork)561 private static @TransportType int getTransportFromAccessNetwork(int accessNetwork) { 562 return accessNetwork == AccessNetworkType.IWLAN 563 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN 564 : AccessNetworkConstants.TRANSPORT_TYPE_WWAN; 565 } 566 setPreferredTransports(@onNull List<QualifiedNetworks> networksList)567 private void setPreferredTransports(@NonNull List<QualifiedNetworks> networksList) { 568 for (QualifiedNetworks networks : networksList) { 569 if (networks.qualifiedNetworks.length > 0) { 570 int transport = getTransportFromAccessNetwork(networks.qualifiedNetworks[0]); 571 if (getPreferredTransport(networks.apnType) != transport) { 572 mPreferredTransports.put(networks.apnType, transport); 573 mAccessNetworksManagerCallbacks.forEach(callback -> 574 callback.invokeFromExecutor(() -> 575 callback.onPreferredTransportChanged(DataUtils 576 .apnTypeToNetworkCapability(networks.apnType)))); 577 logl("setPreferredTransports: apnType=" 578 + ApnSetting.getApnTypeString(networks.apnType) + ", transport=" 579 + AccessNetworkConstants.transportTypeToString(transport)); 580 } 581 } 582 } 583 } 584 585 /** 586 * Get the preferred transport. 587 * 588 * @param apnType APN type 589 * @return The preferred transport. 590 */ getPreferredTransport(@pnType int apnType)591 public @TransportType int getPreferredTransport(@ApnType int apnType) { 592 return mPreferredTransports.get(apnType) == null 593 ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mPreferredTransports.get(apnType); 594 } 595 596 /** 597 * Get the preferred transport by network capability. 598 * 599 * @param networkCapability The network capability. (Note that only APN-type capabilities are 600 * supported. 601 * @return The preferred transport. 602 */ getPreferredTransportByNetworkCapability( @etCapability int networkCapability)603 public @TransportType int getPreferredTransportByNetworkCapability( 604 @NetCapability int networkCapability) { 605 int apnType = DataUtils.networkCapabilityToApnType(networkCapability); 606 // For non-APN type capabilities, always route to WWAN. 607 if (apnType == ApnSetting.TYPE_NONE) { 608 return AccessNetworkConstants.TRANSPORT_TYPE_WWAN; 609 } 610 return getPreferredTransport(apnType); 611 } 612 613 /** 614 * Check if there is any APN type preferred on IWLAN. 615 * 616 * @return {@code true} if there is any APN is on IWLAN, otherwise {@code false}. 617 */ isAnyApnOnIwlan()618 public boolean isAnyApnOnIwlan() { 619 for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) { 620 if (getPreferredTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { 621 return true; 622 } 623 } 624 return false; 625 } 626 627 /** 628 * Unregister for qualified networks changed event. 629 * 630 * @param h The handler 631 */ unregisterForQualifiedNetworksChanged(Handler h)632 public void unregisterForQualifiedNetworksChanged(Handler h) { 633 if (h != null) { 634 mQualifiedNetworksChangedRegistrants.remove(h); 635 } 636 } 637 638 /** 639 * Register the callback for receiving information from {@link AccessNetworksManager}. 640 * 641 * @param callback The callback. 642 */ registerCallback(@onNull AccessNetworksManagerCallback callback)643 public void registerCallback(@NonNull AccessNetworksManagerCallback callback) { 644 mAccessNetworksManagerCallbacks.add(callback); 645 } 646 647 /** 648 * Unregister the callback which was previously registered through 649 * {@link #registerCallback(AccessNetworksManagerCallback)}. 650 * 651 * @param callback The callback to unregister. 652 */ unregisterCallback(@onNull AccessNetworksManagerCallback callback)653 public void unregisterCallback(@NonNull AccessNetworksManagerCallback callback) { 654 mAccessNetworksManagerCallbacks.remove(callback); 655 } 656 log(String s)657 private void log(String s) { 658 Rlog.d(mLogTag, s); 659 } 660 loge(String s)661 private void loge(String s) { 662 Rlog.e(mLogTag, s); 663 } 664 loge(String s, Exception ex)665 private void loge(String s, Exception ex) { 666 Rlog.e(mLogTag, s, ex); 667 } 668 logl(String s)669 private void logl(String s) { 670 log(s); 671 mLocalLog.log(s); 672 } 673 674 /** 675 * Dump the state of access networks manager 676 * 677 * @param fd File descriptor 678 * @param printWriter Print writer 679 * @param args Arguments 680 */ dump(FileDescriptor fd, PrintWriter printWriter, String[] args)681 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 682 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 683 pw.println(AccessNetworksManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":"); 684 pw.increaseIndent(); 685 pw.println("preferred transports="); 686 pw.increaseIndent(); 687 for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) { 688 pw.println(ApnSetting.getApnTypeString(apnType) 689 + ": " + AccessNetworkConstants.transportTypeToString( 690 getPreferredTransport(apnType))); 691 } 692 693 pw.decreaseIndent(); 694 pw.println("Local logs="); 695 pw.increaseIndent(); 696 mLocalLog.dump(fd, pw, args); 697 pw.decreaseIndent(); 698 pw.decreaseIndent(); 699 pw.flush(); 700 } 701 } 702