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