• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.LinkProperties;
21 import android.net.NattKeepalivePacketData;
22 import android.net.NetworkAgent;
23 import android.net.NetworkCapabilities;
24 import android.net.NetworkInfo;
25 import android.net.NetworkMisc;
26 import android.net.SocketKeepalive;
27 import android.os.Message;
28 import android.telephony.AccessNetworkConstants;
29 import android.telephony.AccessNetworkConstants.TransportType;
30 import android.telephony.Rlog;
31 import android.util.LocalLog;
32 import android.util.SparseArray;
33 
34 import com.android.internal.telephony.DctConstants;
35 import com.android.internal.telephony.Phone;
36 import com.android.internal.telephony.RILConstants;
37 import com.android.internal.util.IndentingPrintWriter;
38 
39 import java.io.FileDescriptor;
40 import java.io.PrintWriter;
41 import java.util.concurrent.atomic.AtomicInteger;
42 
43 /**
44  * This class represents a network agent which is communication channel between
45  * {@link DataConnection} and {@link com.android.server.ConnectivityService}. The agent is
46  * created when data connection enters {@link DataConnection.DcActiveState} until it exits that
47  * state.
48  *
49  * Note that in IWLAN handover scenario, this agent could be transferred to the new
50  * {@link DataConnection} so for a short window of time this object might be accessed by two
51  * different {@link DataConnection}. Thus each method in this class needs to be synchronized.
52  */
53 public class DcNetworkAgent extends NetworkAgent {
54     private String mTag;
55 
56     private Phone mPhone;
57 
58     private int mTransportType;
59 
60     private NetworkCapabilities mNetworkCapabilities;
61 
62     public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker();
63 
64     private DataConnection mDataConnection;
65 
66     private final LocalLog mNetCapsLocalLog = new LocalLog(50);
67 
68     private static AtomicInteger sSerialNumber = new AtomicInteger(0);
69 
DcNetworkAgent(DataConnection dc, String tag, Phone phone, NetworkInfo ni, int score, NetworkMisc misc, int factorySerialNumber, int transportType)70     private DcNetworkAgent(DataConnection dc, String tag, Phone phone, NetworkInfo ni,
71                            int score, NetworkMisc misc, int factorySerialNumber,
72                            int transportType) {
73         super(dc.getHandler().getLooper(), phone.getContext(), tag, ni,
74                 dc.getNetworkCapabilities(), dc.getLinkProperties(), score, misc,
75                 factorySerialNumber);
76         mTag = tag;
77         mPhone = phone;
78         mNetworkCapabilities = dc.getNetworkCapabilities();
79         mTransportType = transportType;
80         mDataConnection = dc;
81         logd(tag + " created for data connection " + dc.getName());
82     }
83 
84     /**
85      * Constructor
86      *
87      * @param dc The data connection owns this network agent.
88      * @param phone The phone object.
89      * @param ni Network info.
90      * @param score Score of the data connection.
91      * @param misc The miscellaneous information of the data connection.
92      * @param factorySerialNumber Serial number of telephony network factory.
93      * @param transportType The transport of the data connection.
94      * @return The network agent
95      */
createDcNetworkAgent(DataConnection dc, Phone phone, NetworkInfo ni, int score, NetworkMisc misc, int factorySerialNumber, int transportType)96     public static DcNetworkAgent createDcNetworkAgent(DataConnection dc, Phone phone,
97                                                       NetworkInfo ni, int score, NetworkMisc misc,
98                                                       int factorySerialNumber, int transportType) {
99         // Use serial number only. Do not use transport type because it can be transferred to
100         // a different transport.
101         String tag = "DcNetworkAgent-" + sSerialNumber.incrementAndGet();
102         return new DcNetworkAgent(dc, tag, phone, ni, score, misc, factorySerialNumber,
103                 transportType);
104     }
105 
106     /**
107      * Set the data connection that owns this network agent.
108      *
109      * @param dc Data connection owning this network agent.
110      * @param transportType Transport that this data connection is on.
111      */
acquireOwnership(@onNull DataConnection dc, @TransportType int transportType)112     public synchronized void acquireOwnership(@NonNull DataConnection dc,
113                                               @TransportType int transportType) {
114         mDataConnection = dc;
115         mTransportType = transportType;
116         logd(dc.getName() + " acquired the ownership of this agent.");
117     }
118 
119     /**
120      * Release the ownership of network agent.
121      */
releaseOwnership(DataConnection dc)122     public synchronized void releaseOwnership(DataConnection dc) {
123         if (mDataConnection == null) {
124             loge("releaseOwnership called on no-owner DcNetworkAgent!");
125             return;
126         } else if (mDataConnection != dc) {
127             log("releaseOwnership: This agent belongs to "
128                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
129             return;
130         }
131         logd("Data connection " + mDataConnection.getName() + " released the ownership.");
132         mDataConnection = null;
133     }
134 
135     @Override
unwanted()136     protected synchronized void unwanted() {
137         if (mDataConnection == null) {
138             loge("Unwanted found called on no-owner DcNetworkAgent!");
139             return;
140         }
141 
142         logd("unwanted called. Now tear down the data connection "
143                 + mDataConnection.getName());
144         mDataConnection.tearDownAll(Phone.REASON_RELEASED_BY_CONNECTIVITY_SERVICE,
145                 DcTracker.RELEASE_TYPE_DETACH, null);
146     }
147 
148     @Override
pollLceData()149     protected synchronized void pollLceData() {
150         if (mDataConnection == null) {
151             loge("pollLceData called on no-owner DcNetworkAgent!");
152             return;
153         }
154 
155         if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE     // active LCE service
156                 && mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
157             mPhone.mCi.pullLceData(mDataConnection.obtainMessage(
158                     DataConnection.EVENT_BW_REFRESH_RESPONSE));
159         }
160     }
161 
162     @Override
networkStatus(int status, String redirectUrl)163     protected synchronized void networkStatus(int status, String redirectUrl) {
164         if (mDataConnection == null) {
165             loge("networkStatus called on no-owner DcNetworkAgent!");
166             return;
167         }
168 
169         logd("validation status: " + status + " with redirection URL: " + redirectUrl);
170         DcTracker dct = mPhone.getDcTracker(mTransportType);
171         if (dct != null) {
172             Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
173                     status, 0, redirectUrl);
174             msg.sendToTarget();
175         }
176     }
177 
178     /**
179      * Set the network capabilities.
180      *
181      * @param networkCapabilities The network capabilities.
182      * @param dc The data connection that invokes this method.
183      */
sendNetworkCapabilities(NetworkCapabilities networkCapabilities, DataConnection dc)184     public synchronized void sendNetworkCapabilities(NetworkCapabilities networkCapabilities,
185                                                      DataConnection dc) {
186         if (mDataConnection == null) {
187             loge("sendNetworkCapabilities called on no-owner DcNetworkAgent!");
188             return;
189         } else if (mDataConnection != dc) {
190             loge("sendNetworkCapabilities: This agent belongs to "
191                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
192             return;
193         }
194 
195         if (!networkCapabilities.equals(mNetworkCapabilities)) {
196             String logStr = "Changed from " + mNetworkCapabilities + " to "
197                     + networkCapabilities + ", Data RAT="
198                     + mPhone.getServiceState().getRilDataRadioTechnology()
199                     + ", dc=" + mDataConnection.getName();
200             logd(logStr);
201             mNetCapsLocalLog.log(logStr);
202             mNetworkCapabilities = networkCapabilities;
203         }
204         sendNetworkCapabilities(networkCapabilities);
205     }
206 
207     /**
208      * Set the link properties
209      *
210      * @param linkProperties The link properties
211      * @param dc The data connection that invokes this method.
212      */
sendLinkProperties(LinkProperties linkProperties, DataConnection dc)213     public synchronized void sendLinkProperties(LinkProperties linkProperties,
214                                                 DataConnection dc) {
215         if (mDataConnection == null) {
216             loge("sendLinkProperties called on no-owner DcNetworkAgent!");
217             return;
218         } else if (mDataConnection != dc) {
219             loge("sendLinkProperties: This agent belongs to "
220                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
221             return;
222         }
223         sendLinkProperties(linkProperties);
224     }
225 
226     /**
227      * Set the network score.
228      *
229      * @param score The network score.
230      * @param dc The data connection that invokes this method.
231      */
sendNetworkScore(int score, DataConnection dc)232     public synchronized void sendNetworkScore(int score, DataConnection dc) {
233         if (mDataConnection == null) {
234             loge("sendNetworkScore called on no-owner DcNetworkAgent!");
235             return;
236         } else if (mDataConnection != dc) {
237             loge("sendNetworkScore: This agent belongs to "
238                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
239             return;
240         }
241         sendNetworkScore(score);
242     }
243 
244     /**
245      * Set the network info.
246      *
247      * @param networkInfo The network info.
248      * @param dc The data connection that invokes this method.
249      */
sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc)250     public synchronized void sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc) {
251         if (mDataConnection == null) {
252             loge("sendNetworkInfo called on no-owner DcNetworkAgent!");
253             return;
254         } else if (mDataConnection != dc) {
255             loge("sendNetworkInfo: This agent belongs to "
256                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
257             return;
258         }
259         sendNetworkInfo(networkInfo);
260     }
261 
262     @Override
startSocketKeepalive(Message msg)263     protected synchronized void startSocketKeepalive(Message msg) {
264         if (mDataConnection == null) {
265             loge("startSocketKeepalive called on no-owner DcNetworkAgent!");
266             return;
267         }
268 
269         if (msg.obj instanceof NattKeepalivePacketData) {
270             mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_START_REQUEST,
271                     msg.arg1, msg.arg2, msg.obj).sendToTarget();
272         } else {
273             onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
274         }
275     }
276 
277     @Override
stopSocketKeepalive(Message msg)278     protected synchronized void stopSocketKeepalive(Message msg) {
279         if (mDataConnection == null) {
280             loge("stopSocketKeepalive called on no-owner DcNetworkAgent!");
281             return;
282         }
283 
284         mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST,
285                 msg.arg1, msg.arg2, msg.obj).sendToTarget();
286     }
287 
288     @Override
toString()289     public String toString() {
290         return "DcNetworkAgent:"
291                 + " mDataConnection="
292                 + ((mDataConnection != null) ? mDataConnection.getName() : null)
293                 + " mTransportType="
294                 + AccessNetworkConstants.transportTypeToString(mTransportType)
295                 + " mNetworkCapabilities=" + mNetworkCapabilities;
296     }
297 
298     /**
299      * Dump the state of transport manager
300      *
301      * @param fd File descriptor
302      * @param printWriter Print writer
303      * @param args Arguments
304      */
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)305     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
306         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
307         pw.println(toString());
308         pw.increaseIndent();
309         pw.println("Net caps logs:");
310         mNetCapsLocalLog.dump(fd, pw, args);
311         pw.decreaseIndent();
312     }
313 
314     /**
315      * Log with debug level
316      *
317      * @param s is string log
318      */
logd(String s)319     private void logd(String s) {
320         Rlog.d(mTag, s);
321     }
322 
323     /**
324      * Log with error level
325      *
326      * @param s is string log
327      */
loge(String s)328     private void loge(String s) {
329         Rlog.e(mTag, s);
330     }
331 
332     class DcKeepaliveTracker {
333         private class KeepaliveRecord {
334             public int slotId;
335             public int currentStatus;
336 
KeepaliveRecord(int slotId, int status)337             KeepaliveRecord(int slotId, int status) {
338                 this.slotId = slotId;
339                 this.currentStatus = status;
340             }
341         }
342 
343         private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray();
344 
getHandleForSlot(int slotId)345         int getHandleForSlot(int slotId) {
346             for (int i = 0; i < mKeepalives.size(); i++) {
347                 KeepaliveRecord kr = mKeepalives.valueAt(i);
348                 if (kr.slotId == slotId) return mKeepalives.keyAt(i);
349             }
350             return -1;
351         }
352 
keepaliveStatusErrorToPacketKeepaliveError(int error)353         int keepaliveStatusErrorToPacketKeepaliveError(int error) {
354             switch(error) {
355                 case KeepaliveStatus.ERROR_NONE:
356                     return SocketKeepalive.SUCCESS;
357                 case KeepaliveStatus.ERROR_UNSUPPORTED:
358                     return SocketKeepalive.ERROR_UNSUPPORTED;
359                 case KeepaliveStatus.ERROR_NO_RESOURCES:
360                     return SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
361                 case KeepaliveStatus.ERROR_UNKNOWN:
362                 default:
363                     return SocketKeepalive.ERROR_HARDWARE_ERROR;
364             }
365         }
366 
handleKeepaliveStarted(final int slot, KeepaliveStatus ks)367         void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) {
368             switch (ks.statusCode) {
369                 case KeepaliveStatus.STATUS_INACTIVE:
370                     DcNetworkAgent.this.onSocketKeepaliveEvent(slot,
371                             keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
372                     break;
373                 case KeepaliveStatus.STATUS_ACTIVE:
374                     DcNetworkAgent.this.onSocketKeepaliveEvent(
375                             slot, SocketKeepalive.SUCCESS);
376                     // fall through to add record
377                 case KeepaliveStatus.STATUS_PENDING:
378                     logd("Adding keepalive handle="
379                             + ks.sessionHandle + " slot = " + slot);
380                     mKeepalives.put(ks.sessionHandle,
381                             new KeepaliveRecord(
382                                     slot, ks.statusCode));
383                     break;
384                 default:
385                     logd("Invalid KeepaliveStatus Code: " + ks.statusCode);
386                     break;
387             }
388         }
389 
handleKeepaliveStatus(KeepaliveStatus ks)390         void handleKeepaliveStatus(KeepaliveStatus ks) {
391             final KeepaliveRecord kr;
392             kr = mKeepalives.get(ks.sessionHandle);
393 
394             if (kr == null) {
395                 // If there is no slot for the session handle, we received an event
396                 // for a different data connection. This is not an error because the
397                 // keepalive session events are broadcast to all listeners.
398                 loge("Discarding keepalive event for different data connection:" + ks);
399                 return;
400             }
401             // Switch on the current state, to see what we do with the status update
402             switch (kr.currentStatus) {
403                 case KeepaliveStatus.STATUS_INACTIVE:
404                     logd("Inactive Keepalive received status!");
405                     DcNetworkAgent.this.onSocketKeepaliveEvent(
406                             kr.slotId, SocketKeepalive.ERROR_HARDWARE_ERROR);
407                     break;
408                 case KeepaliveStatus.STATUS_PENDING:
409                     switch (ks.statusCode) {
410                         case KeepaliveStatus.STATUS_INACTIVE:
411                             DcNetworkAgent.this.onSocketKeepaliveEvent(kr.slotId,
412                                     keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
413                             kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
414                             mKeepalives.remove(ks.sessionHandle);
415                             break;
416                         case KeepaliveStatus.STATUS_ACTIVE:
417                             logd("Pending Keepalive received active status!");
418                             kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE;
419                             DcNetworkAgent.this.onSocketKeepaliveEvent(
420                                     kr.slotId, SocketKeepalive.SUCCESS);
421                             break;
422                         case KeepaliveStatus.STATUS_PENDING:
423                             loge("Invalid unsolicied Keepalive Pending Status!");
424                             break;
425                         default:
426                             loge("Invalid Keepalive Status received, " + ks.statusCode);
427                     }
428                     break;
429                 case KeepaliveStatus.STATUS_ACTIVE:
430                     switch (ks.statusCode) {
431                         case KeepaliveStatus.STATUS_INACTIVE:
432                             logd("Keepalive received stopped status!");
433                             DcNetworkAgent.this.onSocketKeepaliveEvent(
434                                     kr.slotId, SocketKeepalive.SUCCESS);
435 
436                             kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
437                             mKeepalives.remove(ks.sessionHandle);
438                             break;
439                         case KeepaliveStatus.STATUS_PENDING:
440                         case KeepaliveStatus.STATUS_ACTIVE:
441                             loge("Active Keepalive received invalid status!");
442                             break;
443                         default:
444                             loge("Invalid Keepalive Status received, " + ks.statusCode);
445                     }
446                     break;
447                 default:
448                     loge("Invalid Keepalive Status received, " + kr.currentStatus);
449             }
450         }
451     }
452 }
453