• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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