1 /*
2  * Copyright (C) 2016 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 static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
20 
21 import android.net.NetworkCapabilities;
22 import android.net.NetworkFactory;
23 import android.net.NetworkRequest;
24 import android.net.TelephonyNetworkSpecifier;
25 import android.os.AsyncResult;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.telephony.AccessNetworkConstants;
31 import android.telephony.Annotation.ApnType;
32 import android.telephony.SubscriptionManager;
33 import android.telephony.data.ApnSetting;
34 import android.util.LocalLog;
35 
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.internal.telephony.Phone;
38 import com.android.internal.telephony.PhoneSwitcher;
39 import com.android.internal.telephony.SubscriptionController;
40 import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
41 import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
42 import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams;
43 import com.android.internal.telephony.metrics.NetworkRequestsStats;
44 import com.android.internal.util.IndentingPrintWriter;
45 import com.android.telephony.Rlog;
46 
47 import java.io.FileDescriptor;
48 import java.io.PrintWriter;
49 import java.util.HashMap;
50 import java.util.Map;
51 
52 public class TelephonyNetworkFactory extends NetworkFactory {
53     public final String LOG_TAG;
54     protected static final boolean DBG = true;
55 
56     private static final int REQUEST_LOG_SIZE = 40;
57 
58     private static final int ACTION_NO_OP   = 0;
59     private static final int ACTION_REQUEST = 1;
60     private static final int ACTION_RELEASE = 2;
61 
62     private static final int TELEPHONY_NETWORK_SCORE = 50;
63 
64     @VisibleForTesting
65     public static final int EVENT_ACTIVE_PHONE_SWITCH               = 1;
66     @VisibleForTesting
67     public static final int EVENT_SUBSCRIPTION_CHANGED              = 2;
68     private static final int EVENT_NETWORK_REQUEST                  = 3;
69     private static final int EVENT_NETWORK_RELEASE                  = 4;
70     private static final int EVENT_DATA_HANDOVER_NEEDED             = 5;
71     private static final int EVENT_DATA_HANDOVER_COMPLETED          = 6;
72 
73     private final PhoneSwitcher mPhoneSwitcher;
74     private final SubscriptionController mSubscriptionController;
75     private final LocalLog mLocalLog = new LocalLog(REQUEST_LOG_SIZE);
76 
77     // Key: network request. Value: the transport of DcTracker it applies to,
78     // AccessNetworkConstants.TRANSPORT_TYPE_INVALID if not applied.
79     private final Map<NetworkRequest, Integer> mNetworkRequests = new HashMap<>();
80 
81     private final Map<Message, HandoverParams> mPendingHandovers = new HashMap<>();
82 
83     private final Phone mPhone;
84 
85     private final TransportManager mTransportManager;
86 
87     private int mSubscriptionId;
88 
89     @VisibleForTesting
90     public final Handler mInternalHandler;
91 
92 
TelephonyNetworkFactory(Looper looper, Phone phone)93     public TelephonyNetworkFactory(Looper looper, Phone phone) {
94         super(looper, phone.getContext(), "TelephonyNetworkFactory[" + phone.getPhoneId()
95                 + "]", null);
96         mPhone = phone;
97         mTransportManager = mPhone.getTransportManager();
98         mInternalHandler = new InternalHandler(looper);
99 
100         mSubscriptionController = SubscriptionController.getInstance();
101 
102         setCapabilityFilter(makeNetworkFilter(mSubscriptionController, mPhone.getPhoneId()));
103         setScoreFilter(TELEPHONY_NETWORK_SCORE);
104 
105         mPhoneSwitcher = PhoneSwitcher.getInstance();
106         LOG_TAG = "TelephonyNetworkFactory[" + mPhone.getPhoneId() + "]";
107 
108         mPhoneSwitcher.registerForActivePhoneSwitch(mInternalHandler, EVENT_ACTIVE_PHONE_SWITCH,
109                 null);
110         mTransportManager.registerForHandoverNeededEvent(mInternalHandler,
111                 EVENT_DATA_HANDOVER_NEEDED);
112 
113         mSubscriptionId = INVALID_SUBSCRIPTION_ID;
114         SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
115                 mSubscriptionsChangedListener);
116 
117         register();
118     }
119 
120     private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener =
121             new SubscriptionManager.OnSubscriptionsChangedListener() {
122                 @Override
123                 public void onSubscriptionsChanged() {
124                     mInternalHandler.sendEmptyMessage(EVENT_SUBSCRIPTION_CHANGED);
125                 }
126             };
127 
makeNetworkFilter(SubscriptionController subscriptionController, int phoneId)128     private NetworkCapabilities makeNetworkFilter(SubscriptionController subscriptionController,
129             int phoneId) {
130         final int subscriptionId = subscriptionController.getSubIdUsingPhoneId(phoneId);
131         return makeNetworkFilter(subscriptionId);
132     }
133 
134     /**
135      * Build the network request filter used by this factory.
136      * @param subscriptionId the subscription ID to listen to
137      * @return the filter to send to the system server
138      */
139     // This is used by the test to simulate the behavior of the system server, which is to
140     // send requests that match the network filter.
141     @VisibleForTesting
makeNetworkFilter(int subscriptionId)142     public NetworkCapabilities makeNetworkFilter(int subscriptionId) {
143         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
144                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
145                 .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
146                 .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)
147                 .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
148                 .addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)
149                 .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
150                 .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
151                 .addCapability(NetworkCapabilities.NET_CAPABILITY_IA)
152                 .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
153                 .addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)
154                 .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
155                 .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
156                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
157                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
158                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
159                 .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
160                 .setSubscriptionId(subscriptionId).build());
161         return builder.build();
162     }
163 
164     private class InternalHandler extends Handler {
InternalHandler(Looper looper)165         public InternalHandler(Looper looper) {
166             super(looper);
167         }
168 
169         @Override
handleMessage(Message msg)170         public void handleMessage(Message msg) {
171             switch (msg.what) {
172                 case EVENT_ACTIVE_PHONE_SWITCH: {
173                     onActivePhoneSwitch();
174                     break;
175                 }
176                 case EVENT_SUBSCRIPTION_CHANGED: {
177                     onSubIdChange();
178                     break;
179                 }
180                 case EVENT_NETWORK_REQUEST: {
181                     onNeedNetworkFor(msg);
182                     break;
183                 }
184                 case EVENT_NETWORK_RELEASE: {
185                     onReleaseNetworkFor(msg);
186                     break;
187                 }
188                 case EVENT_DATA_HANDOVER_NEEDED: {
189                     AsyncResult ar = (AsyncResult) msg.obj;
190                     HandoverParams handoverParams = (HandoverParams) ar.result;
191                     onDataHandoverNeeded(handoverParams.apnType, handoverParams.targetTransport,
192                             handoverParams);
193                     break;
194                 }
195                 case EVENT_DATA_HANDOVER_COMPLETED: {
196                     Bundle bundle = msg.getData();
197                     NetworkRequest nr = bundle.getParcelable(
198                             DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST);
199                     boolean success = bundle.getBoolean(
200                             DcTracker.DATA_COMPLETE_MSG_EXTRA_SUCCESS);
201                     int transport = bundle.getInt(
202                             DcTracker.DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE);
203                     boolean fallback = bundle.getBoolean(
204                             DcTracker.DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK);
205                     HandoverParams handoverParams = mPendingHandovers.remove(msg);
206                     if (handoverParams != null) {
207                         onDataHandoverSetupCompleted(nr, success, transport, fallback,
208                                 handoverParams);
209                     } else {
210                         logl("Handover completed but cannot find handover entry!");
211                     }
212                     break;
213                 }
214             }
215         }
216     }
217 
getTransportTypeFromNetworkRequest(NetworkRequest networkRequest)218     private int getTransportTypeFromNetworkRequest(NetworkRequest networkRequest) {
219         int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
220         return mTransportManager.getCurrentTransport(apnType);
221     }
222 
223     /**
224      * Request network
225      *
226      * @param networkRequest Network request from clients
227      * @param requestType The request type
228      * @param transport Transport type
229      * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
230      * handover is completed. For normal request, this should be null.
231      */
requestNetworkInternal(NetworkRequest networkRequest, @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg)232     private void requestNetworkInternal(NetworkRequest networkRequest,
233             @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg) {
234         NetworkRequestsStats.addNetworkRequest(networkRequest, mSubscriptionId);
235         if (mPhone.getDcTracker(transport) != null) {
236             mPhone.getDcTracker(transport).requestNetwork(networkRequest, requestType,
237                     onHandoverCompleteMsg);
238         }
239     }
240 
releaseNetworkInternal(NetworkRequest networkRequest, @ReleaseNetworkType int releaseType, int transport)241     private void releaseNetworkInternal(NetworkRequest networkRequest,
242                                         @ReleaseNetworkType int releaseType,
243                                         int transport) {
244         NetworkRequestsStats.addNetworkRelease(networkRequest, mSubscriptionId);
245         if (mPhone.getDcTracker(transport) != null) {
246             mPhone.getDcTracker(transport).releaseNetwork(networkRequest, releaseType);
247         }
248     }
249 
getAction(boolean wasActive, boolean isActive)250     private static int getAction(boolean wasActive, boolean isActive) {
251         if (!wasActive && isActive) {
252             return ACTION_REQUEST;
253         } else if (wasActive && !isActive) {
254             return ACTION_RELEASE;
255         } else {
256             return ACTION_NO_OP;
257         }
258     }
259 
260     // apply or revoke requests if our active-ness changes
onActivePhoneSwitch()261     private void onActivePhoneSwitch() {
262         for (HashMap.Entry<NetworkRequest, Integer> entry : mNetworkRequests.entrySet()) {
263             NetworkRequest networkRequest = entry.getKey();
264             boolean applied = entry.getValue() != AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
265 
266             boolean shouldApply = mPhoneSwitcher.shouldApplyNetworkRequest(
267                     networkRequest, mPhone.getPhoneId());
268 
269             int action = getAction(applied, shouldApply);
270             if (action == ACTION_NO_OP) continue;
271 
272             logl("onActivePhoneSwitch: " + ((action == ACTION_REQUEST)
273                     ? "Requesting" : "Releasing") + " network request " + networkRequest);
274             int transportType = getTransportTypeFromNetworkRequest(networkRequest);
275             if (action == ACTION_REQUEST) {
276                 requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
277                         getTransportTypeFromNetworkRequest(networkRequest), null);
278             } else if (action == ACTION_RELEASE) {
279                 releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_DETACH,
280                         getTransportTypeFromNetworkRequest(networkRequest));
281             }
282 
283             mNetworkRequests.put(networkRequest,
284                     shouldApply ? transportType : AccessNetworkConstants.TRANSPORT_TYPE_INVALID);
285         }
286     }
287 
288     // watch for phone->subId changes, reapply new filter and let
289     // that flow through to apply/revoke of requests
onSubIdChange()290     private void onSubIdChange() {
291         final int newSubscriptionId = mSubscriptionController.getSubIdUsingPhoneId(
292                 mPhone.getPhoneId());
293         if (mSubscriptionId != newSubscriptionId) {
294             if (DBG) log("onSubIdChange " + mSubscriptionId + "->" + newSubscriptionId);
295             mSubscriptionId = newSubscriptionId;
296             setCapabilityFilter(makeNetworkFilter(mSubscriptionId));
297         }
298     }
299 
300     @Override
needNetworkFor(NetworkRequest networkRequest)301     public void needNetworkFor(NetworkRequest networkRequest) {
302         Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST);
303         msg.obj = networkRequest;
304         msg.sendToTarget();
305     }
306 
onNeedNetworkFor(Message msg)307     private void onNeedNetworkFor(Message msg) {
308         NetworkRequest networkRequest = (NetworkRequest) msg.obj;
309         boolean shouldApply = mPhoneSwitcher.shouldApplyNetworkRequest(
310                 networkRequest, mPhone.getPhoneId());
311 
312         mNetworkRequests.put(networkRequest, shouldApply
313                 ? getTransportTypeFromNetworkRequest(networkRequest)
314                 : AccessNetworkConstants.TRANSPORT_TYPE_INVALID);
315 
316         logl("onNeedNetworkFor " + networkRequest + " shouldApply " + shouldApply);
317 
318         if (shouldApply) {
319             requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
320                     getTransportTypeFromNetworkRequest(networkRequest), null);
321         }
322     }
323 
324     @Override
releaseNetworkFor(NetworkRequest networkRequest)325     public void releaseNetworkFor(NetworkRequest networkRequest) {
326         Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_RELEASE);
327         msg.obj = networkRequest;
328         msg.sendToTarget();
329     }
330 
onReleaseNetworkFor(Message msg)331     private void onReleaseNetworkFor(Message msg) {
332         NetworkRequest networkRequest = (NetworkRequest) msg.obj;
333         boolean applied = mNetworkRequests.get(networkRequest)
334                 != AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
335 
336         mNetworkRequests.remove(networkRequest);
337 
338         logl("onReleaseNetworkFor " + networkRequest + " applied " + applied);
339 
340         if (applied) {
341             // Most of the time, the network request only exists in one of the DcTracker, but in the
342             // middle of handover, the network request temporarily exists in both DcTrackers. If
343             // connectivity service releases the network request while handover is ongoing, we need
344             // to remove network requests from both DcTrackers.
345             // Note that this part will be refactored in T, where we won't even have DcTracker at
346             // all.
347             releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
348                     AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
349             releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
350                     AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
351         }
352     }
353 
onDataHandoverNeeded(@pnType int apnType, int targetTransport, HandoverParams handoverParams)354     private void onDataHandoverNeeded(@ApnType int apnType, int targetTransport,
355                                       HandoverParams handoverParams) {
356         log("onDataHandoverNeeded: apnType=" + ApnSetting.getApnTypeString(apnType)
357                 + ", target transport="
358                 + AccessNetworkConstants.transportTypeToString(targetTransport));
359         if (mTransportManager.getCurrentTransport(apnType) == targetTransport) {
360             log("APN type " + ApnSetting.getApnTypeString(apnType) + " is already on "
361                     + AccessNetworkConstants.transportTypeToString(targetTransport));
362             return;
363         }
364 
365         boolean handoverPending = false;
366         for (HashMap.Entry<NetworkRequest, Integer> entry : mNetworkRequests.entrySet()) {
367             NetworkRequest networkRequest = entry.getKey();
368             int currentTransport = entry.getValue();
369             boolean applied = currentTransport != AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
370             if (ApnContext.getApnTypeFromNetworkRequest(networkRequest) == apnType
371                     && applied
372                     && currentTransport != targetTransport) {
373                 DcTracker dcTracker = mPhone.getDcTracker(currentTransport);
374                 if (dcTracker != null) {
375                     DataConnection dc = dcTracker.getDataConnectionByApnType(
376                             ApnSetting.getApnTypeString(apnType));
377                     if (dc != null && (dc.isActive())) {
378                         Message onCompleteMsg = mInternalHandler.obtainMessage(
379                                 EVENT_DATA_HANDOVER_COMPLETED);
380                         onCompleteMsg.getData().putParcelable(
381                                 DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST, networkRequest);
382                         mPendingHandovers.put(onCompleteMsg, handoverParams);
383                         requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_HANDOVER,
384                                 targetTransport, onCompleteMsg);
385                         log("Requested handover " + ApnSetting.getApnTypeString(apnType)
386                                 + " to "
387                                 + AccessNetworkConstants.transportTypeToString(targetTransport)
388                                 + ". " + networkRequest);
389                         handoverPending = true;
390                     } else {
391                         // Request is there, but no actual data connection. In this case, just move
392                         // the request to the new transport.
393                         log("The network request is on transport " + AccessNetworkConstants
394                                 .transportTypeToString(currentTransport) + ", but no live data "
395                                 + "connection. Just move the request to transport "
396                                 + AccessNetworkConstants.transportTypeToString(targetTransport)
397                                 + ", dc=" + dc);
398                         entry.setValue(targetTransport);
399                         releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
400                                 currentTransport);
401                         requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
402                                 targetTransport, null);
403                     }
404                 } else {
405                     log("DcTracker on " + AccessNetworkConstants.transportTypeToString(
406                             currentTransport) + " is not available.");
407                 }
408             }
409         }
410 
411         if (!handoverPending) {
412             log("No handover request pending. Handover process is now completed");
413             handoverParams.callback.onCompleted(true, false);
414         }
415     }
416 
onDataHandoverSetupCompleted(NetworkRequest networkRequest, boolean success, int targetTransport, boolean fallback, HandoverParams handoverParams)417     private void onDataHandoverSetupCompleted(NetworkRequest networkRequest, boolean success,
418                                               int targetTransport, boolean fallback,
419                                               HandoverParams handoverParams) {
420         log("onDataHandoverSetupCompleted: " + networkRequest + ", success=" + success
421                 + ", targetTransport="
422                 + AccessNetworkConstants.transportTypeToString(targetTransport)
423                 + ", fallback=" + fallback);
424 
425         // At this point, handover setup has been completed on the target transport.
426         // If it succeeded, or it failed without falling back to the original transport,
427         // we should release the request from the original transport.
428         if (!fallback) {
429             int originTransport = (targetTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
430                     ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
431                     : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
432             int releaseType = success
433                     ? DcTracker.RELEASE_TYPE_HANDOVER
434                     // If handover fails, we need to tear down the existing connection, so the
435                     // new data connection can be re-established on the new transport. If we leave
436                     // the existing data connection in current transport, then DCT and qualified
437                     // network service will be out of sync. Specifying release type to detach
438                     // the transport is moved to the other transport, but network request is still
439                     // there, connectivity service will not call unwanted to tear down the network.
440                     // We need explicitly tear down the data connection here so the new data
441                     // connection can be re-established on the other transport.
442                     : DcTracker.RELEASE_TYPE_DETACH;
443             releaseNetworkInternal(networkRequest, releaseType, originTransport);
444 
445             // Before updating the network request with the target transport, make sure the request
446             // is still there because it's possible that connectivity service has already released
447             // the network while handover is ongoing. If connectivity service already released
448             // the network request, we need to tear down the just-handovered data connection on the
449             // target transport.
450             if (mNetworkRequests.containsKey(networkRequest)) {
451                 // Update it with the target transport.
452                 mNetworkRequests.put(networkRequest, targetTransport);
453             }
454         } else {
455             // If handover fails and requires to fallback, the context of target transport needs to
456             // be released
457             if (!success) {
458                 releaseNetworkInternal(networkRequest,
459                         DcTracker.RELEASE_TYPE_NORMAL, targetTransport);
460             }
461         }
462 
463         handoverParams.callback.onCompleted(success, fallback);
464     }
465 
log(String s)466     protected void log(String s) {
467         Rlog.d(LOG_TAG, s);
468     }
469 
logl(String s)470     protected void logl(String s) {
471         log(s);
472         mLocalLog.log(s);
473     }
474 
dump(FileDescriptor fd, PrintWriter writer, String[] args)475     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
476         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
477         pw.println("Network Requests:");
478         pw.increaseIndent();
479         for (HashMap.Entry<NetworkRequest, Integer> entry : mNetworkRequests.entrySet()) {
480             NetworkRequest nr = entry.getKey();
481             int transport = entry.getValue();
482             pw.println(nr + (transport != AccessNetworkConstants.TRANSPORT_TYPE_INVALID
483                     ? (" applied on " + transport) : " not applied"));
484         }
485         mLocalLog.dump(fd, pw, args);
486         pw.decreaseIndent();
487     }
488 }
489