1 /*
2  * Copyright (C) 2015 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.server.connectivity;
18 
19 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
20 import static android.net.NattSocketKeepalive.NATT_PORT;
21 import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER;
22 import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER;
23 import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
24 import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE;
25 import static android.net.SocketKeepalive.BINDER_DIED;
26 import static android.net.SocketKeepalive.DATA_RECEIVED;
27 import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
28 import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL;
29 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
30 import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
31 import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
32 import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
33 import static android.net.SocketKeepalive.MAX_INTERVAL_SEC;
34 import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
35 import static android.net.SocketKeepalive.NO_KEEPALIVE;
36 import static android.net.SocketKeepalive.SUCCESS;
37 
38 import android.annotation.NonNull;
39 import android.annotation.Nullable;
40 import android.content.Context;
41 import android.net.ISocketKeepaliveCallback;
42 import android.net.InvalidPacketException;
43 import android.net.KeepalivePacketData;
44 import android.net.NattKeepalivePacketData;
45 import android.net.NetworkAgent;
46 import android.net.NetworkUtils;
47 import android.net.SocketKeepalive.InvalidSocketException;
48 import android.net.TcpKeepalivePacketData;
49 import android.net.util.IpUtils;
50 import android.net.util.KeepaliveUtils;
51 import android.os.Binder;
52 import android.os.Handler;
53 import android.os.IBinder;
54 import android.os.Message;
55 import android.os.Process;
56 import android.os.RemoteException;
57 import android.system.ErrnoException;
58 import android.system.Os;
59 import android.util.Log;
60 import android.util.Pair;
61 
62 import com.android.internal.R;
63 import com.android.internal.util.HexDump;
64 import com.android.internal.util.IndentingPrintWriter;
65 
66 import java.io.FileDescriptor;
67 import java.net.InetAddress;
68 import java.net.InetSocketAddress;
69 import java.net.SocketAddress;
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.HashMap;
73 
74 /**
75  * Manages socket keepalive requests.
76  *
77  * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all
78  * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its
79  * handle* methods must be called only from the ConnectivityService handler thread.
80  */
81 public class KeepaliveTracker {
82 
83     private static final String TAG = "KeepaliveTracker";
84     private static final boolean DBG = false;
85 
86     public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
87 
88     /** Keeps track of keepalive requests. */
89     private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
90             new HashMap<> ();
91     private final Handler mConnectivityServiceHandler;
92     @NonNull
93     private final TcpKeepaliveController mTcpController;
94     @NonNull
95     private final Context mContext;
96 
97     // Supported keepalive count for each transport type, can be configured through
98     // config_networkSupportedKeepaliveCount. For better error handling, use
99     // {@link getSupportedKeepalivesForNetworkCapabilities} instead of direct access.
100     @NonNull
101     private final int[] mSupportedKeepalives;
102 
103     // Reserved privileged keepalive slots per transport. Caller's permission will be enforced if
104     // the number of remaining keepalive slots is less than or equal to the threshold.
105     private final int mReservedPrivilegedSlots;
106 
107     // Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if
108     // the number of remaining keepalive slots is less than or equal to the threshold.
109     private final int mAllowedUnprivilegedSlotsForUid;
110 
KeepaliveTracker(Context context, Handler handler)111     public KeepaliveTracker(Context context, Handler handler) {
112         mConnectivityServiceHandler = handler;
113         mTcpController = new TcpKeepaliveController(handler);
114         mContext = context;
115         mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext);
116         mReservedPrivilegedSlots = mContext.getResources().getInteger(
117                 R.integer.config_reservedPrivilegedKeepaliveSlots);
118         mAllowedUnprivilegedSlotsForUid = mContext.getResources().getInteger(
119                 R.integer.config_allowedUnprivilegedKeepalivePerUid);
120     }
121 
122     /**
123      * Tracks information about a socket keepalive.
124      *
125      * All information about this keepalive is known at construction time except the slot number,
126      * which is only returned when the hardware has successfully started the keepalive.
127      */
128     class KeepaliveInfo implements IBinder.DeathRecipient {
129         // Bookkeeping data.
130         private final ISocketKeepaliveCallback mCallback;
131         private final int mUid;
132         private final int mPid;
133         private final boolean mPrivileged;
134         private final NetworkAgentInfo mNai;
135         private final int mType;
136         private final FileDescriptor mFd;
137 
138         public static final int TYPE_NATT = 1;
139         public static final int TYPE_TCP = 2;
140 
141         // Keepalive slot. A small integer that identifies this keepalive among the ones handled
142         // by this network.
143         private int mSlot = NO_KEEPALIVE;
144 
145         // Packet data.
146         private final KeepalivePacketData mPacket;
147         private final int mInterval;
148 
149         // Whether the keepalive is started or not. The initial state is NOT_STARTED.
150         private static final int NOT_STARTED = 1;
151         private static final int STARTING = 2;
152         private static final int STARTED = 3;
153         private static final int STOPPING = 4;
154         private int mStartedState = NOT_STARTED;
155 
KeepaliveInfo(@onNull ISocketKeepaliveCallback callback, @NonNull NetworkAgentInfo nai, @NonNull KeepalivePacketData packet, int interval, int type, @Nullable FileDescriptor fd)156         KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback,
157                 @NonNull NetworkAgentInfo nai,
158                 @NonNull KeepalivePacketData packet,
159                 int interval,
160                 int type,
161                 @Nullable FileDescriptor fd) throws InvalidSocketException {
162             mCallback = callback;
163             mPid = Binder.getCallingPid();
164             mUid = Binder.getCallingUid();
165             mPrivileged = (PERMISSION_GRANTED == mContext.checkPermission(PERMISSION, mPid, mUid));
166 
167             mNai = nai;
168             mPacket = packet;
169             mInterval = interval;
170             mType = type;
171 
172             // For SocketKeepalive, a dup of fd is kept in mFd so the source port from which the
173             // keepalives are sent cannot be reused by another app even if the fd gets closed by
174             // the user. A null is acceptable here for backward compatibility of PacketKeepalive
175             // API.
176             try {
177                 if (fd != null) {
178                     mFd = Os.dup(fd);
179                 }  else {
180                     Log.d(TAG, toString() + " calls with null fd");
181                     if (!mPrivileged) {
182                         throw new SecurityException(
183                                 "null fd is not allowed for unprivileged access.");
184                     }
185                     if (mType == TYPE_TCP) {
186                         throw new IllegalArgumentException(
187                                 "null fd is not allowed for tcp socket keepalives.");
188                     }
189                     mFd = null;
190                 }
191             } catch (ErrnoException e) {
192                 Log.e(TAG, "Cannot dup fd: ", e);
193                 throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
194             }
195 
196             try {
197                 mCallback.asBinder().linkToDeath(this, 0);
198             } catch (RemoteException e) {
199                 binderDied();
200             }
201         }
202 
getNai()203         public NetworkAgentInfo getNai() {
204             return mNai;
205         }
206 
startedStateString(final int state)207         private String startedStateString(final int state) {
208             switch (state) {
209                 case NOT_STARTED : return "NOT_STARTED";
210                 case STARTING : return "STARTING";
211                 case STARTED : return "STARTED";
212                 case STOPPING : return "STOPPING";
213             }
214             throw new IllegalArgumentException("Unknown state");
215         }
216 
toString()217         public String toString() {
218             return "KeepaliveInfo ["
219                     + " type=" + mType
220                     + " network=" + mNai.network
221                     + " startedState=" + startedStateString(mStartedState)
222                     + " "
223                     + IpUtils.addressAndPortToString(mPacket.getSrcAddress(), mPacket.getSrcPort())
224                     + "->"
225                     + IpUtils.addressAndPortToString(mPacket.getDstAddress(), mPacket.getDstPort())
226                     + " interval=" + mInterval
227                     + " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged
228                     + " packetData=" + HexDump.toHexString(mPacket.getPacket())
229                     + " ]";
230         }
231 
232         /** Called when the application process is killed. */
binderDied()233         public void binderDied() {
234             stop(BINDER_DIED);
235         }
236 
unlinkDeathRecipient()237         void unlinkDeathRecipient() {
238             if (mCallback != null) {
239                 mCallback.asBinder().unlinkToDeath(this, 0);
240             }
241         }
242 
checkNetworkConnected()243         private int checkNetworkConnected() {
244             if (!mNai.networkInfo.isConnectedOrConnecting()) {
245                 return ERROR_INVALID_NETWORK;
246             }
247             return SUCCESS;
248         }
249 
checkSourceAddress()250         private int checkSourceAddress() {
251             // Check that we have the source address.
252             for (InetAddress address : mNai.linkProperties.getAddresses()) {
253                 if (address.equals(mPacket.getSrcAddress())) {
254                     return SUCCESS;
255                 }
256             }
257             return ERROR_INVALID_IP_ADDRESS;
258         }
259 
checkInterval()260         private int checkInterval() {
261             if (mInterval < MIN_INTERVAL_SEC || mInterval > MAX_INTERVAL_SEC) {
262                 return ERROR_INVALID_INTERVAL;
263             }
264             return SUCCESS;
265         }
266 
checkPermission()267         private int checkPermission() {
268             final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai);
269             if (networkKeepalives == null) {
270                 return ERROR_INVALID_NETWORK;
271             }
272 
273             if (mPrivileged) return SUCCESS;
274 
275             final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
276                     mSupportedKeepalives, mNai.networkCapabilities);
277 
278             int takenUnprivilegedSlots = 0;
279             for (final KeepaliveInfo ki : networkKeepalives.values()) {
280                 if (!ki.mPrivileged) ++takenUnprivilegedSlots;
281             }
282             if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) {
283                 return ERROR_INSUFFICIENT_RESOURCES;
284             }
285 
286             // Count unprivileged keepalives for the same uid across networks.
287             int unprivilegedCountSameUid = 0;
288             for (final HashMap<Integer, KeepaliveInfo> kaForNetwork : mKeepalives.values()) {
289                 for (final KeepaliveInfo ki : kaForNetwork.values()) {
290                     if (ki.mUid == mUid) {
291                         unprivilegedCountSameUid++;
292                     }
293                 }
294             }
295             if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) {
296                 return ERROR_INSUFFICIENT_RESOURCES;
297             }
298             return SUCCESS;
299         }
300 
checkLimit()301         private int checkLimit() {
302             final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai);
303             if (networkKeepalives == null) {
304                 return ERROR_INVALID_NETWORK;
305             }
306             final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
307                     mSupportedKeepalives, mNai.networkCapabilities);
308             if (supported == 0) return ERROR_UNSUPPORTED;
309             if (networkKeepalives.size() > supported) return ERROR_INSUFFICIENT_RESOURCES;
310             return SUCCESS;
311         }
312 
isValid()313         private int isValid() {
314             synchronized (mNai) {
315                 int error = checkInterval();
316                 if (error == SUCCESS) error = checkLimit();
317                 if (error == SUCCESS) error = checkPermission();
318                 if (error == SUCCESS) error = checkNetworkConnected();
319                 if (error == SUCCESS) error = checkSourceAddress();
320                 return error;
321             }
322         }
323 
start(int slot)324         void start(int slot) {
325             mSlot = slot;
326             int error = isValid();
327             if (error == SUCCESS) {
328                 Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString());
329                 switch (mType) {
330                     case TYPE_NATT:
331                         mNai.asyncChannel.sendMessage(
332                                 CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket);
333                         mNai.asyncChannel
334                                 .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
335                         break;
336                     case TYPE_TCP:
337                         try {
338                             mTcpController.startSocketMonitor(mFd, this, mSlot);
339                         } catch (InvalidSocketException e) {
340                             handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET);
341                             return;
342                         }
343                         mNai.asyncChannel.sendMessage(
344                                 CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket);
345                         // TODO: check result from apf and notify of failure as needed.
346                         mNai.asyncChannel
347                                 .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
348                         break;
349                     default:
350                         Log.wtf(TAG, "Starting keepalive with unknown type: " + mType);
351                         handleStopKeepalive(mNai, mSlot, error);
352                         return;
353                 }
354                 mStartedState = STARTING;
355             } else {
356                 handleStopKeepalive(mNai, mSlot, error);
357                 return;
358             }
359         }
360 
stop(int reason)361         void stop(int reason) {
362             int uid = Binder.getCallingUid();
363             if (uid != mUid && uid != Process.SYSTEM_UID) {
364                 if (DBG) {
365                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
366                 }
367             }
368             Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString()
369                     + ": " + reason);
370             switch (mStartedState) {
371                 case NOT_STARTED:
372                     // Remove the reference of the keepalive that meet error before starting,
373                     // e.g. invalid parameter.
374                     cleanupStoppedKeepalive(mNai, mSlot);
375                     break;
376                 case STOPPING:
377                     // Keepalive is already in stopping state, ignore.
378                     return;
379                 default:
380                     mStartedState = STOPPING;
381                     switch (mType) {
382                         case TYPE_TCP:
383                             mTcpController.stopSocketMonitor(mSlot);
384                             // fall through
385                         case TYPE_NATT:
386                             mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
387                             mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER,
388                                     mSlot);
389                             break;
390                         default:
391                             Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
392                     }
393             }
394 
395             // Close the duplicated fd that maintains the lifecycle of socket whenever
396             // keepalive is running.
397             if (mFd != null) {
398                 try {
399                     Os.close(mFd);
400                 } catch (ErrnoException e) {
401                     // This should not happen since system server controls the lifecycle of fd when
402                     // keepalive offload is running.
403                     Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e);
404                 }
405             }
406 
407             if (reason == SUCCESS) {
408                 try {
409                     mCallback.onStopped();
410                 } catch (RemoteException e) {
411                     Log.w(TAG, "Discarded onStop callback: " + reason);
412                 }
413             } else if (reason == DATA_RECEIVED) {
414                 try {
415                     mCallback.onDataReceived();
416                 } catch (RemoteException e) {
417                     Log.w(TAG, "Discarded onDataReceived callback: " + reason);
418                 }
419             } else {
420                 notifyErrorCallback(mCallback, reason);
421             }
422 
423             unlinkDeathRecipient();
424         }
425 
onFileDescriptorInitiatedStop(final int socketKeepaliveReason)426         void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
427             handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
428         }
429     }
430 
notifyErrorCallback(ISocketKeepaliveCallback cb, int error)431     void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) {
432         if (DBG) Log.w(TAG, "Sending onError(" + error + ") callback");
433         try {
434             cb.onError(error);
435         } catch (RemoteException e) {
436             Log.w(TAG, "Discarded onError(" + error + ") callback");
437         }
438     }
439 
findFirstFreeSlot(NetworkAgentInfo nai)440     private  int findFirstFreeSlot(NetworkAgentInfo nai) {
441         HashMap networkKeepalives = mKeepalives.get(nai);
442         if (networkKeepalives == null) {
443             networkKeepalives = new HashMap<Integer, KeepaliveInfo>();
444             mKeepalives.put(nai, networkKeepalives);
445         }
446 
447         // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two
448         // separate chipset implementations independently came up with.
449         int slot;
450         for (slot = 1; slot <= networkKeepalives.size(); slot++) {
451             if (networkKeepalives.get(slot) == null) {
452                 return slot;
453             }
454         }
455         return slot;
456     }
457 
handleStartKeepalive(Message message)458     public void handleStartKeepalive(Message message) {
459         KeepaliveInfo ki = (KeepaliveInfo) message.obj;
460         NetworkAgentInfo nai = ki.getNai();
461         int slot = findFirstFreeSlot(nai);
462         mKeepalives.get(nai).put(slot, ki);
463         ki.start(slot);
464     }
465 
handleStopAllKeepalives(NetworkAgentInfo nai, int reason)466     public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
467         final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
468         if (networkKeepalives != null) {
469             final ArrayList<KeepaliveInfo> kalist = new ArrayList(networkKeepalives.values());
470             for (KeepaliveInfo ki : kalist) {
471                 ki.stop(reason);
472                 // Clean up keepalives since the network agent is disconnected and unable to pass
473                 // back asynchronous result of stop().
474                 cleanupStoppedKeepalive(nai, ki.mSlot);
475             }
476         }
477     }
478 
handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason)479     public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
480         final String networkName = NetworkAgentInfo.toShortString(nai);
481         HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
482         if (networkKeepalives == null) {
483             Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
484             return;
485         }
486         KeepaliveInfo ki = networkKeepalives.get(slot);
487         if (ki == null) {
488             Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName);
489             return;
490         }
491         ki.stop(reason);
492         // Clean up keepalives will be done as a result of calling ki.stop() after the slots are
493         // freed.
494     }
495 
cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot)496     private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
497         final String networkName = NetworkAgentInfo.toShortString(nai);
498         HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
499         if (networkKeepalives == null) {
500             Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
501             return;
502         }
503         KeepaliveInfo ki = networkKeepalives.get(slot);
504         if (ki == null) {
505             Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName);
506             return;
507         }
508         networkKeepalives.remove(slot);
509         Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", "
510                 + networkKeepalives.size() + " remains.");
511         if (networkKeepalives.isEmpty()) {
512             mKeepalives.remove(nai);
513         }
514     }
515 
handleCheckKeepalivesStillValid(NetworkAgentInfo nai)516     public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
517         HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
518         if (networkKeepalives != null) {
519             ArrayList<Pair<Integer, Integer>> invalidKeepalives = new ArrayList<>();
520             for (int slot : networkKeepalives.keySet()) {
521                 int error = networkKeepalives.get(slot).isValid();
522                 if (error != SUCCESS) {
523                     invalidKeepalives.add(Pair.create(slot, error));
524                 }
525             }
526             for (Pair<Integer, Integer> slotAndError: invalidKeepalives) {
527                 handleStopKeepalive(nai, slotAndError.first, slotAndError.second);
528             }
529         }
530     }
531 
532     /** Handle keepalive events from lower layer. */
handleEventSocketKeepalive(@onNull NetworkAgentInfo nai, @NonNull Message message)533     public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai,
534             @NonNull Message message) {
535         int slot = message.arg1;
536         int reason = message.arg2;
537 
538         KeepaliveInfo ki = null;
539         try {
540             ki = mKeepalives.get(nai).get(slot);
541         } catch(NullPointerException e) {}
542         if (ki == null) {
543             Log.e(TAG, "Event " + message.what + "," + slot + "," + reason
544                     + " for unknown keepalive " + slot + " on " + nai.toShortString());
545             return;
546         }
547 
548         // This can be called in a number of situations :
549         // - startedState is STARTING.
550         //   - reason is SUCCESS => go to STARTED.
551         //   - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive.
552         // - startedState is STARTED.
553         //   - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive.
554         //   - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive.
555         // The control is not supposed to ever come here if the state is NOT_STARTED. This is
556         // because in NOT_STARTED state, the code will switch to STARTING before sending messages
557         // to start, and the only way to NOT_STARTED is this function, through the edges outlined
558         // above : in all cases, keepalive gets stopped and can't restart without going into
559         // STARTING as messages are ordered. This also depends on the hardware processing the
560         // messages in order.
561         // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an
562         // option.
563         if (KeepaliveInfo.STARTING == ki.mStartedState) {
564             if (SUCCESS == reason) {
565                 // Keepalive successfully started.
566                 Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString());
567                 ki.mStartedState = KeepaliveInfo.STARTED;
568                 try {
569                     ki.mCallback.onStarted(slot);
570                 } catch (RemoteException e) {
571                     Log.w(TAG, "Discarded onStarted(" + slot + ") callback");
572                 }
573             } else {
574                 Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString()
575                         + ": " + reason);
576                 // The message indicated some error trying to start: do call handleStopKeepalive.
577                 handleStopKeepalive(nai, slot, reason);
578             }
579         } else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
580             // The message indicated result of stopping : clean up keepalive slots.
581             Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString()
582                     + " stopped: " + reason);
583             ki.mStartedState = KeepaliveInfo.NOT_STARTED;
584             cleanupStoppedKeepalive(nai, slot);
585         } else {
586             Log.wtf(TAG, "Event " + message.what + "," + slot + "," + reason
587                     + " for keepalive in wrong state: " + ki.toString());
588         }
589     }
590 
591     /**
592      * Called when requesting that keepalives be started on a IPsec NAT-T socket. See
593      * {@link android.net.SocketKeepalive}.
594      **/
startNattKeepalive(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, int srcPort, @NonNull String dstAddrString, int dstPort)595     public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
596             @Nullable FileDescriptor fd,
597             int intervalSeconds,
598             @NonNull ISocketKeepaliveCallback cb,
599             @NonNull String srcAddrString,
600             int srcPort,
601             @NonNull String dstAddrString,
602             int dstPort) {
603         if (nai == null) {
604             notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
605             return;
606         }
607 
608         InetAddress srcAddress, dstAddress;
609         try {
610             srcAddress = NetworkUtils.numericToInetAddress(srcAddrString);
611             dstAddress = NetworkUtils.numericToInetAddress(dstAddrString);
612         } catch (IllegalArgumentException e) {
613             notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS);
614             return;
615         }
616 
617         KeepalivePacketData packet;
618         try {
619             packet = NattKeepalivePacketData.nattKeepalivePacket(
620                     srcAddress, srcPort, dstAddress, NATT_PORT);
621         } catch (InvalidPacketException e) {
622             notifyErrorCallback(cb, e.getError());
623             return;
624         }
625         KeepaliveInfo ki = null;
626         try {
627             ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
628                     KeepaliveInfo.TYPE_NATT, fd);
629         } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
630             Log.e(TAG, "Fail to construct keepalive", e);
631             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
632             return;
633         }
634         Log.d(TAG, "Created keepalive: " + ki.toString());
635         mConnectivityServiceHandler.obtainMessage(
636                 NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
637     }
638 
639     /**
640      * Called by ConnectivityService to start TCP keepalive on a file descriptor.
641      *
642      * In order to offload keepalive for application correctly, sequence number, ack number and
643      * other fields are needed to form the keepalive packet. Thus, this function synchronously
644      * puts the socket into repair mode to get the necessary information. After the socket has been
645      * put into repair mode, the application cannot access the socket until reverted to normal.
646      *
647      * See {@link android.net.SocketKeepalive}.
648      **/
startTcpKeepalive(@ullable NetworkAgentInfo nai, @NonNull FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb)649     public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
650             @NonNull FileDescriptor fd,
651             int intervalSeconds,
652             @NonNull ISocketKeepaliveCallback cb) {
653         if (nai == null) {
654             notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
655             return;
656         }
657 
658         final TcpKeepalivePacketData packet;
659         try {
660             packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
661         } catch (InvalidSocketException e) {
662             notifyErrorCallback(cb, e.error);
663             return;
664         } catch (InvalidPacketException e) {
665             notifyErrorCallback(cb, e.getError());
666             return;
667         }
668         KeepaliveInfo ki = null;
669         try {
670             ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
671                     KeepaliveInfo.TYPE_TCP, fd);
672         } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
673             Log.e(TAG, "Fail to construct keepalive e=" + e);
674             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
675             return;
676         }
677         Log.d(TAG, "Created keepalive: " + ki.toString());
678         mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
679     }
680 
681    /**
682     * Called when requesting that keepalives be started on a IPsec NAT-T socket. This function is
683     * identical to {@link #startNattKeepalive}, but also takes a {@code resourceId}, which is the
684     * resource index bound to the {@link UdpEncapsulationSocket} when creating by
685     * {@link com.android.server.IpSecService} to verify whether the given
686     * {@link UdpEncapsulationSocket} is legitimate.
687     **/
startNattKeepalive(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int resourceId, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, @NonNull String dstAddrString, int dstPort)688     public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
689             @Nullable FileDescriptor fd,
690             int resourceId,
691             int intervalSeconds,
692             @NonNull ISocketKeepaliveCallback cb,
693             @NonNull String srcAddrString,
694             @NonNull String dstAddrString,
695             int dstPort) {
696         // Ensure that the socket is created by IpSecService.
697         if (!isNattKeepaliveSocketValid(fd, resourceId)) {
698             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
699         }
700 
701         // Get src port to adopt old API.
702         int srcPort = 0;
703         try {
704             final SocketAddress srcSockAddr = Os.getsockname(fd);
705             srcPort = ((InetSocketAddress) srcSockAddr).getPort();
706         } catch (ErrnoException e) {
707             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
708         }
709 
710         // Forward request to old API.
711         startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
712                 dstAddrString, dstPort);
713     }
714 
715     /**
716      * Verify if the IPsec NAT-T file descriptor and resource Id hold for IPsec keepalive is valid.
717      **/
isNattKeepaliveSocketValid(@ullable FileDescriptor fd, int resourceId)718     public static boolean isNattKeepaliveSocketValid(@Nullable FileDescriptor fd, int resourceId) {
719         // TODO: 1. confirm whether the fd is called from system api or created by IpSecService.
720         //       2. If the fd is created from the system api, check that it's bounded. And
721         //          call dup to keep the fd open.
722         //       3. If the fd is created from IpSecService, check if the resource ID is valid. And
723         //          hold the resource needed in IpSecService.
724         if (null == fd) {
725             return false;
726         }
727         return true;
728     }
729 
dump(IndentingPrintWriter pw)730     public void dump(IndentingPrintWriter pw) {
731         pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives));
732         pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots);
733         pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid);
734         pw.println("Socket keepalives:");
735         pw.increaseIndent();
736         for (NetworkAgentInfo nai : mKeepalives.keySet()) {
737             pw.println(nai.toShortString());
738             pw.increaseIndent();
739             for (int slot : mKeepalives.get(nai).keySet()) {
740                 KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
741                 pw.println(slot + ": " + ki.toString());
742             }
743             pw.decreaseIndent();
744         }
745         pw.decreaseIndent();
746     }
747 }
748