1 /*
2  * Copyright (C) 2019 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.net.KeepalivePacketData;
22 import android.net.LinkProperties;
23 import android.net.NattKeepalivePacketData;
24 import android.net.NetworkAgent;
25 import android.net.NetworkAgentConfig;
26 import android.net.NetworkCapabilities;
27 import android.net.NetworkInfo;
28 import android.net.NetworkProvider;
29 import android.net.SocketKeepalive;
30 import android.net.Uri;
31 import android.os.Message;
32 import android.telephony.AccessNetworkConstants;
33 import android.telephony.AccessNetworkConstants.TransportType;
34 import android.telephony.AnomalyReporter;
35 import android.telephony.TelephonyManager;
36 import android.util.ArrayMap;
37 import android.util.LocalLog;
38 import android.util.SparseArray;
39 
40 import com.android.internal.telephony.DctConstants;
41 import com.android.internal.telephony.Phone;
42 import com.android.internal.telephony.RILConstants;
43 import com.android.internal.telephony.metrics.TelephonyMetrics;
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.time.Duration;
50 import java.util.Map;
51 import java.util.Objects;
52 import java.util.UUID;
53 
54 /**
55  * This class represents a network agent which is communication channel between
56  * {@link DataConnection} and {@link com.android.server.ConnectivityService}. The agent is
57  * created when data connection enters {@link DataConnection.DcActiveState} until it exits that
58  * state.
59  *
60  * Note that in IWLAN handover scenario, this agent could be transferred to the new
61  * {@link DataConnection} so for a short window of time this object might be accessed by two
62  * different {@link DataConnection}. Thus each method in this class needs to be synchronized.
63  */
64 public class DcNetworkAgent extends NetworkAgent {
65     private final String mTag;
66 
67     private final int mId;
68 
69     private Phone mPhone;
70 
71     private int mTransportType;
72 
73     private NetworkCapabilities mNetworkCapabilities;
74 
75     public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker();
76 
77     private DataConnection mDataConnection;
78 
79     private final LocalLog mNetCapsLocalLog = new LocalLog(50);
80 
81     private NetworkInfo mNetworkInfo;
82 
83     // For interface duplicate detection. Key is the net id, value is the interface name in string.
84     private static Map<Integer, String> sInterfaceNames = new ArrayMap<>();
85 
DcNetworkAgent(DataConnection dc, Phone phone, NetworkInfo ni, int score, NetworkAgentConfig config, NetworkProvider networkProvider, int transportType)86     DcNetworkAgent(DataConnection dc, Phone phone, NetworkInfo ni, int score,
87             NetworkAgentConfig config, NetworkProvider networkProvider, int transportType) {
88         super(phone.getContext(), dc.getHandler().getLooper(), "DcNetworkAgent",
89                 dc.getNetworkCapabilities(), dc.getLinkProperties(), score, config,
90                 networkProvider);
91         register();
92         mId = getNetwork().netId;
93         mTag = "DcNetworkAgent" + "-" + mId;
94         mPhone = phone;
95         mNetworkCapabilities = dc.getNetworkCapabilities();
96         mTransportType = transportType;
97         mDataConnection = dc;
98         mNetworkInfo = new NetworkInfo(ni);
99         setLegacyExtraInfo(ni.getExtraInfo());
100         if (dc.getLinkProperties() != null) {
101             checkDuplicateInterface(mId, dc.getLinkProperties().getInterfaceName());
102             logd("created for data connection " + dc.getName() + ", "
103                     + dc.getLinkProperties().getInterfaceName());
104         } else {
105             loge("The connection does not have a valid link properties.");
106         }
107     }
108 
checkDuplicateInterface(int netId, @Nullable String interfaceName)109     private void checkDuplicateInterface(int netId, @Nullable String interfaceName) {
110         for (Map.Entry<Integer, String> entry: sInterfaceNames.entrySet()) {
111             if (Objects.equals(interfaceName, entry.getValue())) {
112                 String message = "Duplicate interface " + interfaceName
113                         + " is detected. DcNetworkAgent-" + entry.getKey()
114                         + " already used this interface name.";
115                 loge(message);
116                 // Using fixed UUID to avoid duplicate bugreport notification
117                 AnomalyReporter.reportAnomaly(
118                         UUID.fromString("02f3d3f6-4613-4415-b6cb-8d92c8a938a6"),
119                         message);
120                 return;
121             }
122         }
123         sInterfaceNames.put(netId, interfaceName);
124     }
125 
126     /**
127      * @return The tag
128      */
getTag()129     String getTag() {
130         return mTag;
131     }
132 
133     /**
134      * Set the data connection that owns this network agent.
135      *
136      * @param dc Data connection owning this network agent.
137      * @param transportType Transport that this data connection is on.
138      */
acquireOwnership(@onNull DataConnection dc, @TransportType int transportType)139     public synchronized void acquireOwnership(@NonNull DataConnection dc,
140                                               @TransportType int transportType) {
141         mDataConnection = dc;
142         mTransportType = transportType;
143         logd(dc.getName() + " acquired the ownership of this agent.");
144     }
145 
146     /**
147      * Release the ownership of network agent.
148      */
releaseOwnership(DataConnection dc)149     public synchronized void releaseOwnership(DataConnection dc) {
150         if (mDataConnection == null) {
151             loge("releaseOwnership called on no-owner DcNetworkAgent!");
152             return;
153         } else if (mDataConnection != dc) {
154             loge("releaseOwnership: This agent belongs to "
155                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
156             return;
157         }
158         logd("Data connection " + mDataConnection.getName() + " released the ownership.");
159         mDataConnection = null;
160     }
161 
162     /**
163      * @return The data connection that owns this agent
164      */
getDataConnection()165     public synchronized DataConnection getDataConnection() {
166         return mDataConnection;
167     }
168 
169     @Override
onNetworkUnwanted()170     public synchronized void onNetworkUnwanted() {
171         if (mDataConnection == null) {
172             loge("onNetworkUnwanted found called on no-owner DcNetworkAgent!");
173             return;
174         }
175 
176         logd("onNetworkUnwanted called. Now tear down the data connection "
177                 + mDataConnection.getName());
178         mDataConnection.tearDownAll(Phone.REASON_RELEASED_BY_CONNECTIVITY_SERVICE,
179                 DcTracker.RELEASE_TYPE_DETACH, null);
180     }
181 
182     @Override
onBandwidthUpdateRequested()183     public synchronized void onBandwidthUpdateRequested() {
184         if (mDataConnection == null) {
185             loge("onBandwidthUpdateRequested called on no-owner DcNetworkAgent!");
186             return;
187         }
188 
189         if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE     // active LCE service
190                 && mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
191             mPhone.mCi.pullLceData(mDataConnection.obtainMessage(
192                     DataConnection.EVENT_BW_REFRESH_RESPONSE));
193         }
194     }
195 
196     @Override
onValidationStatus(int status, Uri redirectUri)197     public synchronized void onValidationStatus(int status, Uri redirectUri) {
198         if (mDataConnection == null) {
199             loge("onValidationStatus called on no-owner DcNetworkAgent!");
200             return;
201         }
202 
203         logd("validation status: " + status + " with redirection URL: " + redirectUri);
204         DcTracker dct = mPhone.getDcTracker(mTransportType);
205         if (dct != null) {
206             Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
207                     status, mDataConnection.getCid(), redirectUri.toString());
208             msg.sendToTarget();
209         }
210     }
211 
isOwned(DataConnection dc, String reason)212     private synchronized boolean isOwned(DataConnection dc, String reason) {
213         if (mDataConnection == null) {
214             loge(reason + " called on no-owner DcNetworkAgent!");
215             return false;
216         } else if (mDataConnection != dc) {
217             loge(reason + ": This agent belongs to "
218                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
219             return false;
220         }
221         return true;
222     }
223 
224     /**
225      * Set the network capabilities.
226      *
227      * @param networkCapabilities The network capabilities.
228      * @param dc The data connection that invokes this method.
229      */
sendNetworkCapabilities(NetworkCapabilities networkCapabilities, DataConnection dc)230     public synchronized void sendNetworkCapabilities(NetworkCapabilities networkCapabilities,
231                                                      DataConnection dc) {
232         if (!isOwned(dc, "sendNetworkCapabilities")) return;
233 
234         if (!networkCapabilities.equals(mNetworkCapabilities)) {
235             String logStr = "Changed from " + mNetworkCapabilities + " to "
236                     + networkCapabilities + ", Data RAT="
237                     + mPhone.getServiceState().getRilDataRadioTechnology()
238                     + ", dc=" + mDataConnection.getName();
239             logd(logStr);
240             mNetCapsLocalLog.log(logStr);
241             if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
242                 // only log metrics for DataConnection with NET_CAPABILITY_INTERNET
243                 if (mNetworkCapabilities == null
244                         || networkCapabilities.hasCapability(
245                                 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
246                         != mNetworkCapabilities.hasCapability(
247                                 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)) {
248                     TelephonyMetrics.getInstance().writeNetworkCapabilitiesChangedEvent(
249                             mPhone.getPhoneId(), networkCapabilities);
250                 }
251             }
252             mNetworkCapabilities = networkCapabilities;
253         }
254         sendNetworkCapabilities(networkCapabilities);
255     }
256 
257     /**
258      * Set the link properties
259      *
260      * @param linkProperties The link properties
261      * @param dc The data connection that invokes this method.
262      */
sendLinkProperties(@onNull LinkProperties linkProperties, DataConnection dc)263     public synchronized void sendLinkProperties(@NonNull LinkProperties linkProperties,
264                                                 DataConnection dc) {
265         if (!isOwned(dc, "sendLinkProperties")) return;
266 
267         sInterfaceNames.put(mId, dc.getLinkProperties().getInterfaceName());
268         sendLinkProperties(linkProperties);
269     }
270 
271     /**
272      * Set the network score.
273      *
274      * @param score The network score.
275      * @param dc The data connection that invokes this method.
276      */
sendNetworkScore(int score, DataConnection dc)277     public synchronized void sendNetworkScore(int score, DataConnection dc) {
278         if (!isOwned(dc, "sendNetworkScore")) return;
279         sendNetworkScore(score);
280     }
281 
282     /**
283      * Unregister the network agent from connectivity service.
284      *
285      * @param dc The data connection that invokes this method.
286      */
unregister(DataConnection dc)287     public synchronized void unregister(DataConnection dc) {
288         if (!isOwned(dc, "unregister")) return;
289 
290         logd("Unregister from connectivity service. " + sInterfaceNames.get(mId) + " removed.");
291         sInterfaceNames.remove(mId);
292         super.unregister();
293     }
294 
295     /**
296      * Set the network info.
297      *
298      * @param networkInfo The network info.
299      * @param dc The data connection that invokes this method.
300      */
sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc)301     public synchronized void sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc) {
302         if (!isOwned(dc, "sendNetworkInfo")) return;
303         final NetworkInfo.State oldState = mNetworkInfo.getState();
304         final NetworkInfo.State state = networkInfo.getState();
305         if (mNetworkInfo.getExtraInfo() != networkInfo.getExtraInfo()) {
306             setLegacyExtraInfo(networkInfo.getExtraInfo());
307         }
308         int subType = networkInfo.getSubtype();
309         if (mNetworkInfo.getSubtype() != subType) {
310             setLegacySubtype(subType, TelephonyManager.getNetworkTypeName(subType));
311         }
312         if ((oldState == NetworkInfo.State.SUSPENDED || oldState == NetworkInfo.State.CONNECTED)
313                 && state == NetworkInfo.State.DISCONNECTED) {
314             unregister(dc);
315         }
316         mNetworkInfo = new NetworkInfo(networkInfo);
317     }
318 
319     /**
320      * Get the latest sent network info.
321      *
322      * @return network info
323      */
getNetworkInfo()324     public synchronized NetworkInfo getNetworkInfo() {
325         return mNetworkInfo;
326     }
327 
328     @Override
onStartSocketKeepalive(int slot, @NonNull Duration interval, @NonNull KeepalivePacketData packet)329     public synchronized void onStartSocketKeepalive(int slot, @NonNull Duration interval,
330             @NonNull KeepalivePacketData packet) {
331         if (mDataConnection == null) {
332             loge("onStartSocketKeepalive called on no-owner DcNetworkAgent!");
333             return;
334         }
335 
336         if (packet instanceof NattKeepalivePacketData) {
337             mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_START_REQUEST,
338                     slot, (int) interval.getSeconds(), packet).sendToTarget();
339         } else {
340             sendSocketKeepaliveEvent(slot, SocketKeepalive.ERROR_UNSUPPORTED);
341         }
342     }
343 
344     @Override
onStopSocketKeepalive(int slot)345     public synchronized void onStopSocketKeepalive(int slot) {
346         if (mDataConnection == null) {
347             loge("onStopSocketKeepalive called on no-owner DcNetworkAgent!");
348             return;
349         }
350 
351         mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slot)
352                 .sendToTarget();
353     }
354 
355     @Override
toString()356     public String toString() {
357         return "DcNetworkAgent-"
358                 + mId
359                 + " mDataConnection="
360                 + ((mDataConnection != null) ? mDataConnection.getName() : null)
361                 + " mTransportType="
362                 + AccessNetworkConstants.transportTypeToString(mTransportType)
363                 + " " + ((mDataConnection != null) ? mDataConnection.getLinkProperties() : null)
364                 + " mNetworkCapabilities=" + mNetworkCapabilities;
365     }
366 
367     /**
368      * Dump the state of transport manager
369      *
370      * @param fd File descriptor
371      * @param printWriter Print writer
372      * @param args Arguments
373      */
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)374     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
375         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
376         pw.println(toString());
377         pw.increaseIndent();
378         pw.println("Net caps logs:");
379         mNetCapsLocalLog.dump(fd, pw, args);
380         pw.decreaseIndent();
381     }
382 
383     /**
384      * Log with debug level
385      *
386      * @param s is string log
387      */
logd(String s)388     private void logd(String s) {
389         Rlog.d(mTag, s);
390     }
391 
392     /**
393      * Log with error level
394      *
395      * @param s is string log
396      */
loge(String s)397     private void loge(String s) {
398         Rlog.e(mTag, s);
399     }
400 
401     class DcKeepaliveTracker {
402         private class KeepaliveRecord {
403             public int slotId;
404             public int currentStatus;
405 
KeepaliveRecord(int slotId, int status)406             KeepaliveRecord(int slotId, int status) {
407                 this.slotId = slotId;
408                 this.currentStatus = status;
409             }
410         }
411 
412         private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray();
413 
getHandleForSlot(int slotId)414         int getHandleForSlot(int slotId) {
415             for (int i = 0; i < mKeepalives.size(); i++) {
416                 KeepaliveRecord kr = mKeepalives.valueAt(i);
417                 if (kr.slotId == slotId) return mKeepalives.keyAt(i);
418             }
419             return -1;
420         }
421 
keepaliveStatusErrorToPacketKeepaliveError(int error)422         int keepaliveStatusErrorToPacketKeepaliveError(int error) {
423             switch(error) {
424                 case KeepaliveStatus.ERROR_NONE:
425                     return SocketKeepalive.SUCCESS;
426                 case KeepaliveStatus.ERROR_UNSUPPORTED:
427                     return SocketKeepalive.ERROR_UNSUPPORTED;
428                 case KeepaliveStatus.ERROR_NO_RESOURCES:
429                     return SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
430                 case KeepaliveStatus.ERROR_UNKNOWN:
431                 default:
432                     return SocketKeepalive.ERROR_HARDWARE_ERROR;
433             }
434         }
435 
handleKeepaliveStarted(final int slot, KeepaliveStatus ks)436         void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) {
437             switch (ks.statusCode) {
438                 case KeepaliveStatus.STATUS_INACTIVE:
439                     DcNetworkAgent.this.sendSocketKeepaliveEvent(slot,
440                             keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
441                     break;
442                 case KeepaliveStatus.STATUS_ACTIVE:
443                     DcNetworkAgent.this.sendSocketKeepaliveEvent(
444                             slot, SocketKeepalive.SUCCESS);
445                     // fall through to add record
446                 case KeepaliveStatus.STATUS_PENDING:
447                     logd("Adding keepalive handle="
448                             + ks.sessionHandle + " slot = " + slot);
449                     mKeepalives.put(ks.sessionHandle,
450                             new KeepaliveRecord(
451                                     slot, ks.statusCode));
452                     break;
453                 default:
454                     logd("Invalid KeepaliveStatus Code: " + ks.statusCode);
455                     break;
456             }
457         }
458 
handleKeepaliveStatus(KeepaliveStatus ks)459         void handleKeepaliveStatus(KeepaliveStatus ks) {
460             final KeepaliveRecord kr;
461             kr = mKeepalives.get(ks.sessionHandle);
462 
463             if (kr == null) {
464                 // If there is no slot for the session handle, we received an event
465                 // for a different data connection. This is not an error because the
466                 // keepalive session events are broadcast to all listeners.
467                 loge("Discarding keepalive event for different data connection:" + ks);
468                 return;
469             }
470             // Switch on the current state, to see what we do with the status update
471             switch (kr.currentStatus) {
472                 case KeepaliveStatus.STATUS_INACTIVE:
473                     logd("Inactive Keepalive received status!");
474                     DcNetworkAgent.this.sendSocketKeepaliveEvent(
475                             kr.slotId, SocketKeepalive.ERROR_HARDWARE_ERROR);
476                     break;
477                 case KeepaliveStatus.STATUS_PENDING:
478                     switch (ks.statusCode) {
479                         case KeepaliveStatus.STATUS_INACTIVE:
480                             DcNetworkAgent.this.sendSocketKeepaliveEvent(kr.slotId,
481                                     keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
482                             kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
483                             mKeepalives.remove(ks.sessionHandle);
484                             break;
485                         case KeepaliveStatus.STATUS_ACTIVE:
486                             logd("Pending Keepalive received active status!");
487                             kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE;
488                             DcNetworkAgent.this.sendSocketKeepaliveEvent(
489                                     kr.slotId, SocketKeepalive.SUCCESS);
490                             break;
491                         case KeepaliveStatus.STATUS_PENDING:
492                             loge("Invalid unsolicied Keepalive Pending Status!");
493                             break;
494                         default:
495                             loge("Invalid Keepalive Status received, " + ks.statusCode);
496                     }
497                     break;
498                 case KeepaliveStatus.STATUS_ACTIVE:
499                     switch (ks.statusCode) {
500                         case KeepaliveStatus.STATUS_INACTIVE:
501                             logd("Keepalive received stopped status!");
502                             DcNetworkAgent.this.sendSocketKeepaliveEvent(
503                                     kr.slotId, SocketKeepalive.SUCCESS);
504 
505                             kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
506                             mKeepalives.remove(ks.sessionHandle);
507                             break;
508                         case KeepaliveStatus.STATUS_PENDING:
509                         case KeepaliveStatus.STATUS_ACTIVE:
510                             loge("Active Keepalive received invalid status!");
511                             break;
512                         default:
513                             loge("Invalid Keepalive Status received, " + ks.statusCode);
514                     }
515                     break;
516                 default:
517                     loge("Invalid Keepalive Status received, " + kr.currentStatus);
518             }
519         }
520     }
521 }
522