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