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.dataconnection;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.StringDef;
22 import android.os.AsyncResult;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.os.RegistrantList;
26 import android.os.SystemProperties;
27 import android.telephony.AccessNetworkConstants;
28 import android.telephony.AccessNetworkConstants.AccessNetworkType;
29 import android.telephony.AccessNetworkConstants.TransportType;
30 import android.telephony.Annotation.ApnType;
31 import android.telephony.CarrierConfigManager;
32 import android.telephony.data.ApnSetting;
33 import android.util.IndentingPrintWriter;
34 import android.util.LocalLog;
35 import android.util.SparseIntArray;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.internal.telephony.Phone;
39 import com.android.internal.telephony.RIL;
40 import com.android.internal.telephony.dataconnection.AccessNetworksManager.QualifiedNetworks;
41 import com.android.telephony.Rlog;
42 
43 import java.io.FileDescriptor;
44 import java.io.PrintWriter;
45 import java.lang.annotation.Retention;
46 import java.lang.annotation.RetentionPolicy;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.concurrent.ConcurrentHashMap;
50 import java.util.concurrent.TimeUnit;
51 
52 /**
53  * This class represents the transport manager which manages available transports (i.e. WWAN or
54  * WLAN) and determine the correct transport for {@link TelephonyNetworkFactory} to handle the data
55  * requests.
56  *
57  * The device can operate in the following modes, which is stored in the system properties
58  * ro.telephony.iwlan_operation_mode. If the system properties is missing, then it's tied to
59  * IRadio version. For 1.4 or above, it's AP-assisted mdoe. For 1.3 or below, it's legacy mode.
60  *
61  * Legacy mode:
62  *      Frameworks send all data requests to the default data service, which is the cellular data
63  *      service. IWLAN should be still reported as a RAT on cellular network service.
64  *
65  * AP-assisted mode:
66  *      IWLAN is handled by IWLAN data service extending {@link android.telephony.data.DataService},
67  *      IWLAN network service extending {@link android.telephony.NetworkService}, and qualified
68  *      network service extending {@link android.telephony.data.QualifiedNetworksService}.
69  *
70  *      The following settings for service package name need to be configured properly for
71  *      frameworks to bind.
72  *
73  *      Package name of data service:
74  *          The resource overlay 'config_wlan_data_service_package' or,
75  *          the carrier config
76  *          {@link CarrierConfigManager#KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}.
77  *          The carrier config takes precedence over the resource overlay if both exist.
78  *
79  *      Package name of network service
80  *          The resource overlay 'config_wlan_network_service_package' or
81  *          the carrier config
82  *          {@link CarrierConfigManager#KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}.
83  *          The carrier config takes precedence over the resource overlay if both exist.
84  *
85  *      Package name of qualified network service
86  *          The resource overlay 'config_qualified_networks_service_package' or
87  *          the carrier config
88  *          {@link CarrierConfigManager#
89  *          KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING}.
90  *          The carrier config takes precedence over the resource overlay if both exist.
91  */
92 public class TransportManager extends Handler {
93     private final String mLogTag;
94 
95     private static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 1;
96 
97     private static final int EVENT_EVALUATE_TRANSPORT_PREFERENCE = 2;
98 
99     // Delay the re-evaluation if transport fall back. QNS will need to quickly change the
100     // preference back to the original transport to avoid another handover request.
101     private static final long FALL_BACK_REEVALUATE_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(3);
102 
103     public static final String SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE =
104             "ro.telephony.iwlan_operation_mode";
105 
106     @Retention(RetentionPolicy.SOURCE)
107     @StringDef(prefix = {"IWLAN_OPERATION_MODE_"},
108             value = {
109                     IWLAN_OPERATION_MODE_DEFAULT,
110                     IWLAN_OPERATION_MODE_LEGACY,
111                     IWLAN_OPERATION_MODE_AP_ASSISTED})
112     public @interface IwlanOperationMode {}
113 
114     /**
115      * IWLAN default mode. On device that has IRadio 1.4 or above, it means
116      * {@link #IWLAN_OPERATION_MODE_AP_ASSISTED}. On device that has IRadio 1.3 or below, it means
117      * {@link #IWLAN_OPERATION_MODE_LEGACY}.
118      */
119     public static final String IWLAN_OPERATION_MODE_DEFAULT = "default";
120 
121     /**
122      * IWLAN legacy mode. IWLAN is completely handled by the modem, and when the device is on
123      * IWLAN, modem reports IWLAN as a RAT.
124      */
125     public static final String IWLAN_OPERATION_MODE_LEGACY = "legacy";
126 
127     /**
128      * IWLAN application processor assisted mode. IWLAN is handled by the bound IWLAN data service
129      * and network service separately.
130      */
131     public static final String IWLAN_OPERATION_MODE_AP_ASSISTED = "AP-assisted";
132 
133     private final Phone mPhone;
134 
135     private final LocalLog mLocalLog = new LocalLog(100);
136 
137     /** The available transports. Must be one or more of AccessNetworkConstants.TransportType.XXX */
138     private final int[] mAvailableTransports;
139 
140     @Nullable
141     private AccessNetworksManager mAccessNetworksManager;
142 
143     /**
144      * The current transport of the APN type. The key is the APN type, and the value is the
145      * transport.
146      */
147     private final Map<Integer, Integer> mCurrentTransports;
148 
149     /**
150      * The preferred transport of the APN type. The key is the APN type, and the value is the
151      * transport. The preferred transports are updated as soon as QNS changes the preference, while
152      * the current transports are updated after handover complete.
153      */
154     private final Map<Integer, Integer> mPreferredTransports;
155 
156     /**
157      * The pending handover list. This is a list of APNs that are being handover to the new
158      * transport. The entry will be removed once handover is completed. The key
159      * is the APN type, and the value is the target transport that the APN is handovered to.
160      */
161     private final SparseIntArray mPendingHandoverApns;
162 
163     /**
164      * The registrants for listening data handover needed events.
165      */
166     private final RegistrantList mHandoverNeededEventRegistrants;
167 
168     /**
169      * Handover parameters
170      */
171     @VisibleForTesting
172     public static final class HandoverParams {
173         /**
174          * The callback for handover complete.
175          */
176         public interface HandoverCallback {
177             /**
178              * Called when handover is completed.
179              *
180              * @param success {@true} if handover succeeded, otherwise failed.
181              * @param fallback {@true} if handover failed, the data connection fallback to the
182              * original transport
183              */
onCompleted(boolean success, boolean fallback)184             void onCompleted(boolean success, boolean fallback);
185         }
186 
187         public final @ApnType int apnType;
188         public final int targetTransport;
189         public final HandoverCallback callback;
190 
191         @VisibleForTesting
HandoverParams(int apnType, int targetTransport, HandoverCallback callback)192         public HandoverParams(int apnType, int targetTransport, HandoverCallback callback) {
193             this.apnType = apnType;
194             this.targetTransport = targetTransport;
195             this.callback = callback;
196         }
197     }
198 
TransportManager(Phone phone)199     public TransportManager(Phone phone) {
200         mPhone = phone;
201         mCurrentTransports = new ConcurrentHashMap<>();
202         mPreferredTransports = new ConcurrentHashMap<>();
203         mPendingHandoverApns = new SparseIntArray();
204         mHandoverNeededEventRegistrants = new RegistrantList();
205         mLogTag = TransportManager.class.getSimpleName() + "-" + mPhone.getPhoneId();
206 
207         if (isInLegacyMode()) {
208             log("operates in legacy mode.");
209             // For legacy mode, WWAN is the only transport to handle all data connections, even
210             // the IWLAN ones.
211             mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN};
212         } else {
213             log("operates in AP-assisted mode.");
214             mAccessNetworksManager = new AccessNetworksManager(phone);
215             mAccessNetworksManager.registerForQualifiedNetworksChanged(this,
216                     EVENT_QUALIFIED_NETWORKS_CHANGED);
217             mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
218                     AccessNetworkConstants.TRANSPORT_TYPE_WLAN};
219         }
220     }
221 
222     @Override
handleMessage(Message msg)223     public void handleMessage(Message msg) {
224         switch (msg.what) {
225             case EVENT_QUALIFIED_NETWORKS_CHANGED:
226                 AsyncResult ar = (AsyncResult) msg.obj;
227                 List<QualifiedNetworks> networks = (List<QualifiedNetworks>) ar.result;
228                 setPreferredTransports(networks);
229                 // There might be already delayed evaluate event in the queue due to fallback. Don't
230                 // send redudant ones.
231                 if (!hasMessages(EVENT_EVALUATE_TRANSPORT_PREFERENCE)) {
232                     sendEmptyMessage(EVENT_EVALUATE_TRANSPORT_PREFERENCE);
233                 }
234                 break;
235             case EVENT_EVALUATE_TRANSPORT_PREFERENCE:
236                 evaluateTransportPreference();
237                 break;
238             default:
239                 loge("Unexpected event " + msg.what);
240                 break;
241         }
242     }
243 
244     /**
245      * Set the current transport of apn type.
246      *
247      * @param apnType The APN type
248      * @param transport The transport. Must be WWAN or WLAN.
249      */
setCurrentTransport(@pnType int apnType, int transport)250     private synchronized void setCurrentTransport(@ApnType int apnType, int transport) {
251         Integer previousTransport = mCurrentTransports.put(apnType, transport);
252         if (previousTransport == null || previousTransport != transport) {
253             logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType)
254                     + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
255         }
256     }
257 
isHandoverPending()258     private boolean isHandoverPending() {
259         return mPendingHandoverApns.size() > 0;
260     }
261 
262     /**
263      * Evaluate the preferred transport for each APN type to see if handover is needed.
264      */
evaluateTransportPreference()265     private void evaluateTransportPreference() {
266         // Simultaneously handover is not supported today. Preference will be re-evaluated after
267         // handover completed.
268         if (isHandoverPending()) return;
269         logl("evaluateTransportPreference");
270         for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
271             int targetTransport = getPreferredTransport(apnType);
272             if (targetTransport != getCurrentTransport(apnType)) {
273                 logl("Handover started for APN type: "
274                         + ApnSetting.getApnTypeString(apnType)
275                         + ", target transport: "
276                         + AccessNetworkConstants.transportTypeToString(targetTransport));
277                 mPendingHandoverApns.put(apnType, targetTransport);
278                 mHandoverNeededEventRegistrants.notifyResult(
279                         new HandoverParams(apnType, targetTransport,
280                                 (success, fallback) -> {
281                                     // The callback for handover completed.
282                                     if (success) {
283                                         logl("Handover succeeded for APN type "
284                                                 + ApnSetting.getApnTypeString(apnType));
285                                     } else {
286                                         logl("APN type "
287                                                 + ApnSetting.getApnTypeString(apnType)
288                                                 + " handover to "
289                                                 + AccessNetworkConstants.transportTypeToString(
290                                                 targetTransport) + " failed"
291                                                 + ", fallback=" + fallback);
292                                     }
293 
294                                     long delay = 0;
295                                     if (fallback) {
296                                         // No need to change the preference because we should
297                                         // fallback. Re-evaluate after few seconds to give QNS
298                                         // some time to change the preference back to the original
299                                         // transport.
300                                         delay = FALL_BACK_REEVALUATE_DELAY_MILLIS;
301                                     } else {
302                                         // If handover succeeds or failed without falling back
303                                         // to the original transport, we should move to the new
304                                         // transport (even if it is failed).
305                                         setCurrentTransport(apnType, targetTransport);
306                                     }
307                                     mPendingHandoverApns.delete(apnType);
308                                     sendEmptyMessageDelayed(EVENT_EVALUATE_TRANSPORT_PREFERENCE,
309                                             delay);
310                                 }));
311 
312                 // Return here instead of processing the next APN type. The next APN type for
313                 // handover will be evaluate again once current handover is completed.
314                 return;
315             }
316         }
317     }
318 
319     /**
320      * @return The available transports. Note that on legacy devices, the only available transport
321      * would be WWAN only. If the device is configured as AP-assisted mode, the available transport
322      * will always be WWAN and WLAN (even if the device is not camped on IWLAN).
323      * See {@link #isInLegacyMode()} for mode details.
324      */
getAvailableTransports()325     public synchronized @NonNull int[] getAvailableTransports() {
326         return mAvailableTransports;
327     }
328 
329     /**
330      * @return {@code true} if the device operates in legacy mode, otherwise {@code false}.
331      */
isInLegacyMode()332     public boolean isInLegacyMode() {
333         // Get IWLAN operation mode from the system property. If the system property is configured
334         // to default or not configured, the mode is tied to IRadio version. For 1.4 or above, it's
335         // AP-assisted mode, for 1.3 or below, it's legacy mode.
336         String mode = SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE);
337 
338         if (mode.equals(IWLAN_OPERATION_MODE_AP_ASSISTED)) {
339             return false;
340         } else if (mode.equals(IWLAN_OPERATION_MODE_LEGACY)) {
341             return true;
342         }
343 
344         return mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
345     }
346 
347     /**
348      * Get the transport based on the APN type.
349      *
350      * @param apnType APN type
351      * @return The transport type
352      */
getCurrentTransport(@pnType int apnType)353     public int getCurrentTransport(@ApnType int apnType) {
354         // In legacy mode, always route to cellular.
355         if (isInLegacyMode()) {
356             return AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
357         }
358 
359         // If we can't find the corresponding transport, always route to cellular.
360         return mCurrentTransports.get(apnType) == null
361                 ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mCurrentTransports.get(apnType);
362     }
363 
364     /**
365      * Check if there is any APN type's current transport is on IWLAN.
366      *
367      * @return {@code true} if there is any APN is on IWLAN, otherwise {@code false}.
368      */
isAnyApnOnIwlan()369     public boolean isAnyApnOnIwlan() {
370         for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
371             if (getCurrentTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
372                 return true;
373             }
374         }
375         return false;
376     }
377 
378     /**
379      * Register for data handover needed event
380      *
381      * @param h The handler of the event
382      * @param what The id of the event
383      */
registerForHandoverNeededEvent(Handler h, int what)384     public void registerForHandoverNeededEvent(Handler h, int what) {
385         if (h != null) {
386             mHandoverNeededEventRegistrants.addUnique(h, what, null);
387         }
388     }
389 
390     /**
391      * Unregister for data handover needed event
392      *
393      * @param h The handler
394      */
unregisterForHandoverNeededEvent(Handler h)395     public void unregisterForHandoverNeededEvent(Handler h) {
396         mHandoverNeededEventRegistrants.remove(h);
397     }
398 
399     /**
400      * Registers the data throttler with DcTracker.
401      */
registerDataThrottler(DataThrottler dataThrottler)402     public void registerDataThrottler(DataThrottler dataThrottler) {
403         if (mAccessNetworksManager != null) {
404             mAccessNetworksManager.registerDataThrottler(dataThrottler);
405         }
406     }
407 
408     /**
409      * Get the  preferred transport.
410      *
411      * @param apnType APN type
412      * @return The preferred transport.
413      */
getPreferredTransport(@pnType int apnType)414     public @TransportType int getPreferredTransport(@ApnType int apnType) {
415         // In legacy mode, always preferred on cellular.
416         if (isInLegacyMode()) {
417             return AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
418         }
419 
420         return mPreferredTransports.get(apnType) == null
421                 ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mPreferredTransports.get(apnType);
422     }
423 
getTransportFromAccessNetwork(int accessNetwork)424     private static @TransportType int getTransportFromAccessNetwork(int accessNetwork) {
425         return accessNetwork == AccessNetworkType.IWLAN
426                 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
427                 : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
428     }
429 
setPreferredTransports(@onNull List<QualifiedNetworks> networksList)430     private void setPreferredTransports(@NonNull List<QualifiedNetworks> networksList) {
431         for (QualifiedNetworks networks : networksList) {
432             // Todo: We should support zero-lengthed qualifiedNetworks in the future. It means
433             // network should not be setup on either WWAN or WLAN.
434             if (networks.qualifiedNetworks.length > 0) {
435                 int transport = getTransportFromAccessNetwork(networks.qualifiedNetworks[0]);
436                 mPreferredTransports.put(networks.apnType, transport);
437                 logl("setPreferredTransports: apnType="
438                         + ApnSetting.getApnTypeString(networks.apnType)
439                         + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
440             }
441         }
442     }
443 
444     /**
445      * Dump the state of transport manager
446      *
447      * @param fd File descriptor
448      * @param printwriter Print writer
449      * @param args Arguments
450      */
dump(FileDescriptor fd, PrintWriter printwriter, String[] args)451     public void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
452         IndentingPrintWriter pw = new IndentingPrintWriter(printwriter, "  ");
453         pw.println(mLogTag);
454         pw.increaseIndent();
455         pw.println("mPendingHandoverApns=" + mPendingHandoverApns);
456         pw.println("current transports=");
457         pw.increaseIndent();
458         for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
459             pw.println(ApnSetting.getApnTypeString(apnType)
460                     + ": " + AccessNetworkConstants.transportTypeToString(
461                             getCurrentTransport(apnType)));
462         }
463         pw.decreaseIndent();
464         pw.println("preferred transports=");
465         pw.increaseIndent();
466         for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
467             pw.println(ApnSetting.getApnTypeString(apnType)
468                     + ": " + AccessNetworkConstants.transportTypeToString(
469                             getPreferredTransport(apnType)));
470         }
471 
472         pw.decreaseIndent();
473         pw.println("isInLegacy=" + isInLegacyMode());
474         pw.println("IWLAN operation mode="
475                 + SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE));
476         pw.println("Local logs=");
477         pw.increaseIndent();
478         mLocalLog.dump(fd, pw, args);
479         pw.decreaseIndent();
480         pw.decreaseIndent();
481         pw.flush();
482     }
483 
logl(String s)484     private void logl(String s) {
485         log(s);
486         mLocalLog.log(s);
487     }
488 
log(String s)489     private void log(String s) {
490         Rlog.d(mLogTag, s);
491     }
492 
loge(String s)493     private void loge(String s) {
494         Rlog.e(mLogTag, s);
495     }
496 }
497