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.KeepalivePacketData;
43 import android.net.NattKeepalivePacketData;
44 import android.net.NetworkAgent;
45 import android.net.NetworkUtils;
46 import android.net.SocketKeepalive.InvalidPacketException;
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.srcAddress, mPacket.srcPort)
224                     + "->"
225                     + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)
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.srcAddress)) {
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.name());
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.name() + ": " + reason);
369             switch (mStartedState) {
370                 case NOT_STARTED:
371                     // Remove the reference of the keepalive that meet error before starting,
372                     // e.g. invalid parameter.
373                     cleanupStoppedKeepalive(mNai, mSlot);
374                     break;
375                 case STOPPING:
376                     // Keepalive is already in stopping state, ignore.
377                     return;
378                 default:
379                     mStartedState = STOPPING;
380                     switch (mType) {
381                         case TYPE_TCP:
382                             mTcpController.stopSocketMonitor(mSlot);
383                             // fall through
384                         case TYPE_NATT:
385                             mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
386                             mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER,
387                                     mSlot);
388                             break;
389                         default:
390                             Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
391                     }
392             }
393 
394             // Close the duplicated fd that maintains the lifecycle of socket whenever
395             // keepalive is running.
396             if (mFd != null) {
397                 try {
398                     Os.close(mFd);
399                 } catch (ErrnoException e) {
400                     // This should not happen since system server controls the lifecycle of fd when
401                     // keepalive offload is running.
402                     Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e);
403                 }
404             }
405 
406             if (reason == SUCCESS) {
407                 try {
408                     mCallback.onStopped();
409                 } catch (RemoteException e) {
410                     Log.w(TAG, "Discarded onStop callback: " + reason);
411                 }
412             } else if (reason == DATA_RECEIVED) {
413                 try {
414                     mCallback.onDataReceived();
415                 } catch (RemoteException e) {
416                     Log.w(TAG, "Discarded onDataReceived callback: " + reason);
417                 }
418             } else {
419                 notifyErrorCallback(mCallback, reason);
420             }
421 
422             unlinkDeathRecipient();
423         }
424 
onFileDescriptorInitiatedStop(final int socketKeepaliveReason)425         void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
426             handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
427         }
428     }
429 
notifyErrorCallback(ISocketKeepaliveCallback cb, int error)430     void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) {
431         if (DBG) Log.w(TAG, "Sending onError(" + error + ") callback");
432         try {
433             cb.onError(error);
434         } catch (RemoteException e) {
435             Log.w(TAG, "Discarded onError(" + error + ") callback");
436         }
437     }
438 
findFirstFreeSlot(NetworkAgentInfo nai)439     private  int findFirstFreeSlot(NetworkAgentInfo nai) {
440         HashMap networkKeepalives = mKeepalives.get(nai);
441         if (networkKeepalives == null) {
442             networkKeepalives = new HashMap<Integer, KeepaliveInfo>();
443             mKeepalives.put(nai, networkKeepalives);
444         }
445 
446         // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two
447         // separate chipset implementations independently came up with.
448         int slot;
449         for (slot = 1; slot <= networkKeepalives.size(); slot++) {
450             if (networkKeepalives.get(slot) == null) {
451                 return slot;
452             }
453         }
454         return slot;
455     }
456 
handleStartKeepalive(Message message)457     public void handleStartKeepalive(Message message) {
458         KeepaliveInfo ki = (KeepaliveInfo) message.obj;
459         NetworkAgentInfo nai = ki.getNai();
460         int slot = findFirstFreeSlot(nai);
461         mKeepalives.get(nai).put(slot, ki);
462         ki.start(slot);
463     }
464 
handleStopAllKeepalives(NetworkAgentInfo nai, int reason)465     public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
466         final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
467         if (networkKeepalives != null) {
468             final ArrayList<KeepaliveInfo> kalist = new ArrayList(networkKeepalives.values());
469             for (KeepaliveInfo ki : kalist) {
470                 ki.stop(reason);
471                 // Clean up keepalives since the network agent is disconnected and unable to pass
472                 // back asynchronous result of stop().
473                 cleanupStoppedKeepalive(nai, ki.mSlot);
474             }
475         }
476         // Clean up keepalives will be done as a result of calling ki.stop() after the slots are
477         // freed.
478     }
479 
handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason)480     public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
481         String networkName = (nai == null) ? "(null)" : nai.name();
482         HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
483         if (networkKeepalives == null) {
484             Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
485             return;
486         }
487         KeepaliveInfo ki = networkKeepalives.get(slot);
488         if (ki == null) {
489             Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName);
490             return;
491         }
492         ki.stop(reason);
493         // Clean up keepalives will be done as a result of calling ki.stop() after the slots are
494         // freed.
495     }
496 
cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot)497     private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
498         String networkName = (nai == null) ? "(null)" : nai.name();
499         HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
500         if (networkKeepalives == null) {
501             Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
502             return;
503         }
504         KeepaliveInfo ki = networkKeepalives.get(slot);
505         if (ki == null) {
506             Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName);
507             return;
508         }
509         networkKeepalives.remove(slot);
510         Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", "
511                 + networkKeepalives.size() + " remains.");
512         if (networkKeepalives.isEmpty()) {
513             mKeepalives.remove(nai);
514         }
515     }
516 
handleCheckKeepalivesStillValid(NetworkAgentInfo nai)517     public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
518         HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
519         if (networkKeepalives != null) {
520             ArrayList<Pair<Integer, Integer>> invalidKeepalives = new ArrayList<>();
521             for (int slot : networkKeepalives.keySet()) {
522                 int error = networkKeepalives.get(slot).isValid();
523                 if (error != SUCCESS) {
524                     invalidKeepalives.add(Pair.create(slot, error));
525                 }
526             }
527             for (Pair<Integer, Integer> slotAndError: invalidKeepalives) {
528                 handleStopKeepalive(nai, slotAndError.first, slotAndError.second);
529             }
530         }
531     }
532 
533     /** Handle keepalive events from lower layer. */
handleEventSocketKeepalive(@onNull NetworkAgentInfo nai, @NonNull Message message)534     public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai,
535             @NonNull Message message) {
536         int slot = message.arg1;
537         int reason = message.arg2;
538 
539         KeepaliveInfo ki = null;
540         try {
541             ki = mKeepalives.get(nai).get(slot);
542         } catch(NullPointerException e) {}
543         if (ki == null) {
544             Log.e(TAG, "Event " + message.what + "," + slot + "," + reason
545                     + " for unknown keepalive " + slot + " on " + nai.name());
546             return;
547         }
548 
549         // This can be called in a number of situations :
550         // - startedState is STARTING.
551         //   - reason is SUCCESS => go to STARTED.
552         //   - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive.
553         // - startedState is STARTED.
554         //   - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive.
555         //   - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive.
556         // The control is not supposed to ever come here if the state is NOT_STARTED. This is
557         // because in NOT_STARTED state, the code will switch to STARTING before sending messages
558         // to start, and the only way to NOT_STARTED is this function, through the edges outlined
559         // above : in all cases, keepalive gets stopped and can't restart without going into
560         // STARTING as messages are ordered. This also depends on the hardware processing the
561         // messages in order.
562         // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an
563         // option.
564         if (KeepaliveInfo.STARTING == ki.mStartedState) {
565             if (SUCCESS == reason) {
566                 // Keepalive successfully started.
567                 Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
568                 ki.mStartedState = KeepaliveInfo.STARTED;
569                 try {
570                     ki.mCallback.onStarted(slot);
571                 } catch (RemoteException e) {
572                     Log.w(TAG, "Discarded onStarted(" + slot + ") callback");
573                 }
574             } else {
575                 Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.name()
576                         + ": " + reason);
577                 // The message indicated some error trying to start: do call handleStopKeepalive.
578                 handleStopKeepalive(nai, slot, reason);
579             }
580         } else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
581             // The message indicated result of stopping : clean up keepalive slots.
582             Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
583                     + " stopped: " + reason);
584             ki.mStartedState = KeepaliveInfo.NOT_STARTED;
585             cleanupStoppedKeepalive(nai, slot);
586         } else {
587             Log.wtf(TAG, "Event " + message.what + "," + slot + "," + reason
588                     + " for keepalive in wrong state: " + ki.toString());
589         }
590     }
591 
592     /**
593      * Called when requesting that keepalives be started on a IPsec NAT-T socket. See
594      * {@link android.net.SocketKeepalive}.
595      **/
startNattKeepalive(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, int srcPort, @NonNull String dstAddrString, int dstPort)596     public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
597             @Nullable FileDescriptor fd,
598             int intervalSeconds,
599             @NonNull ISocketKeepaliveCallback cb,
600             @NonNull String srcAddrString,
601             int srcPort,
602             @NonNull String dstAddrString,
603             int dstPort) {
604         if (nai == null) {
605             notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
606             return;
607         }
608 
609         InetAddress srcAddress, dstAddress;
610         try {
611             srcAddress = NetworkUtils.numericToInetAddress(srcAddrString);
612             dstAddress = NetworkUtils.numericToInetAddress(dstAddrString);
613         } catch (IllegalArgumentException e) {
614             notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS);
615             return;
616         }
617 
618         KeepalivePacketData packet;
619         try {
620             packet = NattKeepalivePacketData.nattKeepalivePacket(
621                     srcAddress, srcPort, dstAddress, NATT_PORT);
622         } catch (InvalidPacketException e) {
623             notifyErrorCallback(cb, e.error);
624             return;
625         }
626         KeepaliveInfo ki = null;
627         try {
628             ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
629                     KeepaliveInfo.TYPE_NATT, fd);
630         } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
631             Log.e(TAG, "Fail to construct keepalive", e);
632             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
633             return;
634         }
635         Log.d(TAG, "Created keepalive: " + ki.toString());
636         mConnectivityServiceHandler.obtainMessage(
637                 NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
638     }
639 
640     /**
641      * Called by ConnectivityService to start TCP keepalive on a file descriptor.
642      *
643      * In order to offload keepalive for application correctly, sequence number, ack number and
644      * other fields are needed to form the keepalive packet. Thus, this function synchronously
645      * puts the socket into repair mode to get the necessary information. After the socket has been
646      * put into repair mode, the application cannot access the socket until reverted to normal.
647      *
648      * See {@link android.net.SocketKeepalive}.
649      **/
startTcpKeepalive(@ullable NetworkAgentInfo nai, @NonNull FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb)650     public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
651             @NonNull FileDescriptor fd,
652             int intervalSeconds,
653             @NonNull ISocketKeepaliveCallback cb) {
654         if (nai == null) {
655             notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
656             return;
657         }
658 
659         final TcpKeepalivePacketData packet;
660         try {
661             packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
662         } catch (InvalidPacketException | InvalidSocketException e) {
663             notifyErrorCallback(cb, e.error);
664             return;
665         }
666         KeepaliveInfo ki = null;
667         try {
668             ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
669                     KeepaliveInfo.TYPE_TCP, fd);
670         } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
671             Log.e(TAG, "Fail to construct keepalive e=" + e);
672             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
673             return;
674         }
675         Log.d(TAG, "Created keepalive: " + ki.toString());
676         mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
677     }
678 
679    /**
680     * Called when requesting that keepalives be started on a IPsec NAT-T socket. This function is
681     * identical to {@link #startNattKeepalive}, but also takes a {@code resourceId}, which is the
682     * resource index bound to the {@link UdpEncapsulationSocket} when creating by
683     * {@link com.android.server.IpSecService} to verify whether the given
684     * {@link UdpEncapsulationSocket} is legitimate.
685     **/
startNattKeepalive(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int resourceId, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, @NonNull String dstAddrString, int dstPort)686     public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
687             @Nullable FileDescriptor fd,
688             int resourceId,
689             int intervalSeconds,
690             @NonNull ISocketKeepaliveCallback cb,
691             @NonNull String srcAddrString,
692             @NonNull String dstAddrString,
693             int dstPort) {
694         // Ensure that the socket is created by IpSecService.
695         if (!isNattKeepaliveSocketValid(fd, resourceId)) {
696             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
697         }
698 
699         // Get src port to adopt old API.
700         int srcPort = 0;
701         try {
702             final SocketAddress srcSockAddr = Os.getsockname(fd);
703             srcPort = ((InetSocketAddress) srcSockAddr).getPort();
704         } catch (ErrnoException e) {
705             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
706         }
707 
708         // Forward request to old API.
709         startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
710                 dstAddrString, dstPort);
711     }
712 
713     /**
714      * Verify if the IPsec NAT-T file descriptor and resource Id hold for IPsec keepalive is valid.
715      **/
isNattKeepaliveSocketValid(@ullable FileDescriptor fd, int resourceId)716     public static boolean isNattKeepaliveSocketValid(@Nullable FileDescriptor fd, int resourceId) {
717         // TODO: 1. confirm whether the fd is called from system api or created by IpSecService.
718         //       2. If the fd is created from the system api, check that it's bounded. And
719         //          call dup to keep the fd open.
720         //       3. If the fd is created from IpSecService, check if the resource ID is valid. And
721         //          hold the resource needed in IpSecService.
722         if (null == fd) {
723             return false;
724         }
725         return true;
726     }
727 
dump(IndentingPrintWriter pw)728     public void dump(IndentingPrintWriter pw) {
729         pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives));
730         pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots);
731         pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid);
732         pw.println("Socket keepalives:");
733         pw.increaseIndent();
734         for (NetworkAgentInfo nai : mKeepalives.keySet()) {
735             pw.println(nai.name());
736             pw.increaseIndent();
737             for (int slot : mKeepalives.get(nai).keySet()) {
738                 KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
739                 pw.println(slot + ": " + ki.toString());
740             }
741             pw.decreaseIndent();
742         }
743         pw.decreaseIndent();
744     }
745 }
746