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