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