• 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.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