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