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