• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.net.SocketKeepalive.ERROR_INVALID_SOCKET;
20 import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
21 import static android.net.SocketKeepalive.SUCCESS;
22 import static android.net.SocketKeepalive.SUCCESS_PAUSED;
23 import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
24 import static android.system.OsConstants.AF_INET;
25 import static android.system.OsConstants.AF_INET6;
26 import static android.system.OsConstants.SOL_SOCKET;
27 import static android.system.OsConstants.SO_SNDTIMEO;
28 
29 import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE;
30 import static com.android.net.module.util.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE;
31 import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
32 import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS;
33 
34 import android.annotation.IntDef;
35 import android.annotation.NonNull;
36 import android.annotation.Nullable;
37 import android.app.AlarmManager;
38 import android.content.Context;
39 import android.net.INetd;
40 import android.net.ISocketKeepaliveCallback;
41 import android.net.MarkMaskParcel;
42 import android.net.Network;
43 import android.net.SocketKeepalive.InvalidSocketException;
44 import android.os.FileUtils;
45 import android.os.Handler;
46 import android.os.IBinder;
47 import android.os.Message;
48 import android.os.RemoteException;
49 import android.os.SystemClock;
50 import android.system.ErrnoException;
51 import android.system.Os;
52 import android.system.OsConstants;
53 import android.system.StructTimeval;
54 import android.util.LocalLog;
55 import android.util.Log;
56 import android.util.Pair;
57 import android.util.SparseArray;
58 
59 import com.android.internal.annotations.VisibleForTesting;
60 import com.android.internal.util.IndentingPrintWriter;
61 import com.android.net.module.util.BinderUtils;
62 import com.android.net.module.util.CollectionUtils;
63 import com.android.net.module.util.DeviceConfigUtils;
64 import com.android.net.module.util.HexDump;
65 import com.android.net.module.util.SocketUtils;
66 import com.android.net.module.util.netlink.InetDiagMessage;
67 import com.android.net.module.util.netlink.NetlinkMessage;
68 import com.android.net.module.util.netlink.NetlinkUtils;
69 import com.android.net.module.util.netlink.StructNlAttr;
70 
71 import java.io.FileDescriptor;
72 import java.io.InterruptedIOException;
73 import java.lang.annotation.Retention;
74 import java.lang.annotation.RetentionPolicy;
75 import java.net.SocketException;
76 import java.nio.BufferUnderflowException;
77 import java.nio.ByteBuffer;
78 import java.util.ArrayList;
79 import java.util.List;
80 import java.util.Objects;
81 
82 /**
83  * Manages automatic on/off socket keepalive requests.
84  *
85  * Provides methods to stop and start automatic keepalive requests, and keeps track of keepalives
86  * across all networks. This class is tightly coupled to ConnectivityService. It is not
87  * thread-safe and its handle* methods must be called only from the ConnectivityService handler
88  * thread.
89  */
90 public class AutomaticOnOffKeepaliveTracker {
91     private static final String TAG = "AutomaticOnOffKeepaliveTracker";
92     private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
93     private static final long LOW_TCP_POLLING_INTERVAL_MS = 1_000L;
94     private static final int ADJUST_TCP_POLLING_DELAY_MS = 2000;
95     private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
96             "automatic_on_off_keepalive_version";
97     public static final long METRICS_COLLECTION_DURATION_MS = 24 * 60 * 60 * 1_000L;
98 
99     // ConnectivityService parses message constants from itself and AutomaticOnOffKeepaliveTracker
100     // with MessageUtils for debugging purposes, and crashes if some messages have the same values.
101     private static final int BASE = 2000;
102     /**
103      * Sent by AutomaticOnOffKeepaliveTracker periodically (when relevant) to trigger monitor
104      * automatic keepalive request.
105      *
106      * NATT keepalives have an automatic mode where the system only sends keepalive packets when
107      * TCP sockets are open over a VPN. The system will check periodically for presence of
108      * such open sockets, and this message is what triggers the re-evaluation.
109      *
110      * obj = A Binder object associated with the keepalive.
111      */
112     public static final int CMD_MONITOR_AUTOMATIC_KEEPALIVE = BASE + 1;
113 
114     /**
115      * Sent by AutomaticOnOffKeepaliveTracker to ConnectivityService to start a keepalive.
116      *
117      * obj = AutomaticKeepaliveInfo object
118      */
119     public static final int CMD_REQUEST_START_KEEPALIVE = BASE + 2;
120 
121     /**
122      * States for {@code #AutomaticOnOffKeepalive}.
123      *
124      * If automatic mode is off for this keepalive, the state is STATE_ALWAYS_ON and it stays
125      * so for the entire lifetime of this object.
126      *
127      * If enabled, a new AutomaticOnOffKeepalive starts with STATE_ENABLED. The system will monitor
128      * the TCP sockets on VPN networks running on top of the specified network, and turn off
129      * keepalive if there is no TCP socket any of the VPN networks. Conversely, it will turn
130      * keepalive back on if any TCP socket is open on any of the VPN networks.
131      *
132      * When there is no TCP socket on any of the VPN networks, the state becomes STATE_SUSPENDED.
133      * The {@link KeepaliveTracker.KeepaliveInfo} object is kept to remember the parameters so it
134      * is possible to resume keepalive later with the same parameters.
135      *
136      * When the system detects some TCP socket is open on one of the VPNs while in STATE_SUSPENDED,
137      * this AutomaticOnOffKeepalive goes to STATE_ENABLED again.
138      *
139      * When finishing keepalive, this object is deleted.
140      */
141     private static final int STATE_ENABLED = 0;
142     private static final int STATE_SUSPENDED = 1;
143     private static final int STATE_ALWAYS_ON = 2;
144     @Retention(RetentionPolicy.SOURCE)
145     @IntDef(prefix = { "STATE_" }, value = {
146             STATE_ENABLED,
147             STATE_SUSPENDED,
148             STATE_ALWAYS_ON
149     })
150     private @interface AutomaticOnOffState {}
151 
152     @NonNull
153     private final Handler mConnectivityServiceHandler;
154     @NonNull
155     private final KeepaliveTracker mKeepaliveTracker;
156     @NonNull
157     private final Context mContext;
158     @NonNull
159     private final AlarmManager mAlarmManager;
160 
161     /**
162      * The {@code inetDiagReqV2} messages for different IP family.
163      *
164      *   Key: Ip family type.
165      * Value: Bytes array represent the {@code inetDiagReqV2}.
166      *
167      * This should only be accessed in the connectivity service handler thread.
168      */
169     private final SparseArray<byte[]> mSockDiagMsg = new SparseArray<>();
170     private final Dependencies mDependencies;
171     private final INetd mNetd;
172     /**
173      * Keeps track of automatic on/off keepalive requests.
174      * This should be only updated in ConnectivityService handler thread.
175      */
176     private final ArrayList<AutomaticOnOffKeepalive> mAutomaticOnOffKeepalives = new ArrayList<>();
177     // TODO: Remove this when TCP polling design is replaced with callback.
178     private long mTestLowTcpPollingTimerUntilMs = 0;
179 
180     private static final int MAX_EVENTS_LOGS = 40;
181     private final LocalLog mEventLog = new LocalLog(MAX_EVENTS_LOGS);
182 
183     private final KeepaliveStatsTracker mKeepaliveStatsTracker;
184 
185     private final long mMetricsWriteTimeBase;
186 
187     /**
188      * Information about a managed keepalive.
189      *
190      * The keepalive in mKi is managed by this object. This object can be in one of three states
191      * (in mAutomatiOnOffState) :
192      * • STATE_ALWAYS_ON : this keepalive is always on
193      * • STATE_ENABLED : this keepalive is currently on, and monitored for possibly being turned
194      *                   off if no TCP socket is open on the VPN.
195      * • STATE_SUSPENDED : this keepalive is currently off, and monitored for possibly being
196      *                     resumed if a TCP socket is open on the VPN.
197      * See the documentation for the states for more detail.
198      */
199     public class AutomaticOnOffKeepalive implements IBinder.DeathRecipient {
200         @NonNull
201         private final KeepaliveTracker.KeepaliveInfo mKi;
202         @NonNull
203         private final ISocketKeepaliveCallback mCallback;
204         @Nullable
205         private final FileDescriptor mFd;
206         @Nullable
207         private final AlarmManager.OnAlarmListener mAlarmListener;
208         @AutomaticOnOffState
209         private int mAutomaticOnOffState;
210         @Nullable
211         private final Network mUnderpinnedNetwork;
212 
AutomaticOnOffKeepalive(@onNull final KeepaliveTracker.KeepaliveInfo ki, final boolean autoOnOff, @Nullable Network underpinnedNetwork)213         AutomaticOnOffKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki,
214                 final boolean autoOnOff, @Nullable Network underpinnedNetwork)
215                 throws InvalidSocketException {
216             this.mKi = Objects.requireNonNull(ki);
217             mCallback = ki.mCallback;
218             mUnderpinnedNetwork = underpinnedNetwork;
219             if (autoOnOff && mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
220                     true /* defaultEnabled */)) {
221                 mAutomaticOnOffState = STATE_ENABLED;
222                 if (null == ki.mFd) {
223                     throw new IllegalArgumentException("fd can't be null with automatic "
224                             + "on/off keepalives");
225                 }
226                 try {
227                     mFd = Os.dup(ki.mFd);
228                 } catch (ErrnoException e) {
229                     Log.e(TAG, "Cannot dup fd: ", e);
230                     throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
231                 }
232                 mAlarmListener = () -> mConnectivityServiceHandler.obtainMessage(
233                         CMD_MONITOR_AUTOMATIC_KEEPALIVE, mCallback.asBinder())
234                         .sendToTarget();
235             } else {
236                 mAutomaticOnOffState = STATE_ALWAYS_ON;
237                 // A null fd is acceptable in KeepaliveInfo for backward compatibility of
238                 // PacketKeepalive API, but it must never happen with automatic keepalives.
239                 // TODO : remove mFd from KeepaliveInfo or from this class.
240                 mFd = ki.mFd;
241                 mAlarmListener = null;
242             }
243         }
244 
245         @VisibleForTesting
getCallback()246         public ISocketKeepaliveCallback getCallback() {
247             return mCallback;
248         }
249 
getNetwork()250         public Network getNetwork() {
251             return mKi.getNai().network();
252         }
253 
254         @Nullable
getUnderpinnedNetwork()255         public Network getUnderpinnedNetwork() {
256             return mUnderpinnedNetwork;
257         }
258 
match(Network network, int slot)259         public boolean match(Network network, int slot) {
260             return mKi.getNai().network().equals(network) && mKi.getSlot() == slot;
261         }
262 
263         @Override
binderDied()264         public void binderDied() {
265             mEventLog.log("Binder died : " + mCallback);
266             mConnectivityServiceHandler.post(() -> cleanupAutoOnOffKeepalive(this));
267         }
268 
269         /** Close this automatic on/off keepalive */
close()270         public void close() {
271             // Close the duplicated fd that maintains the lifecycle of socket. If this fd was
272             // not duplicated this is a no-op.
273             FileUtils.closeQuietly(mFd);
274         }
275 
getAutomaticOnOffStateName(int state)276         private String getAutomaticOnOffStateName(int state) {
277             switch (state) {
278                 case STATE_ENABLED:
279                     return "STATE_ENABLED";
280                 case STATE_SUSPENDED:
281                     return "STATE_SUSPENDED";
282                 case STATE_ALWAYS_ON:
283                     return "STATE_ALWAYS_ON";
284                 default:
285                     Log.e(TAG, "Get unexpected state:" + state);
286                     return Integer.toString(state);
287             }
288         }
289 
290         @Override
toString()291         public String toString() {
292             return "AutomaticOnOffKeepalive [ "
293                     + mKi
294                     + ", state=" + getAutomaticOnOffStateName(mAutomaticOnOffState)
295                     + " ]";
296         }
297     }
298 
AutomaticOnOffKeepaliveTracker(@onNull Context context, @NonNull Handler handler)299     public AutomaticOnOffKeepaliveTracker(@NonNull Context context, @NonNull Handler handler) {
300         this(context, handler, new Dependencies(context));
301     }
302 
303     @VisibleForTesting
AutomaticOnOffKeepaliveTracker(@onNull Context context, @NonNull Handler handler, @NonNull Dependencies dependencies)304     public AutomaticOnOffKeepaliveTracker(@NonNull Context context, @NonNull Handler handler,
305             @NonNull Dependencies dependencies) {
306         mContext = Objects.requireNonNull(context);
307         mDependencies = Objects.requireNonNull(dependencies);
308         mConnectivityServiceHandler = Objects.requireNonNull(handler);
309         mNetd = mDependencies.getNetd();
310         mKeepaliveTracker = mDependencies.newKeepaliveTracker(
311                 mContext, mConnectivityServiceHandler);
312 
313         mAlarmManager = mDependencies.getAlarmManager(context);
314         mKeepaliveStatsTracker =
315                 mDependencies.newKeepaliveStatsTracker(context, handler);
316 
317         final long time = mDependencies.getElapsedRealtime();
318         mMetricsWriteTimeBase = time % METRICS_COLLECTION_DURATION_MS;
319         final long triggerAtMillis = mMetricsWriteTimeBase + METRICS_COLLECTION_DURATION_MS;
320         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, TAG,
321                 this::writeMetricsAndRescheduleAlarm, handler);
322     }
323 
writeMetricsAndRescheduleAlarm()324     private void writeMetricsAndRescheduleAlarm() {
325         mKeepaliveStatsTracker.writeAndResetMetrics();
326 
327         final long time = mDependencies.getElapsedRealtime();
328         final long triggerAtMillis =
329                 mMetricsWriteTimeBase
330                         + (time - time % METRICS_COLLECTION_DURATION_MS)
331                         + METRICS_COLLECTION_DURATION_MS;
332         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, TAG,
333                 this::writeMetricsAndRescheduleAlarm, mConnectivityServiceHandler);
334     }
335 
startTcpPollingAlarm(@onNull AutomaticOnOffKeepalive ki)336     private void startTcpPollingAlarm(@NonNull AutomaticOnOffKeepalive ki) {
337         if (ki.mAlarmListener == null) return;
338 
339         final long triggerAtMillis =
340                 mDependencies.getElapsedRealtime() + getTcpPollingIntervalMs(ki);
341         // Setup a non-wake up alarm.
342         mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, null /* tag */,
343                 ki.mAlarmListener, mConnectivityServiceHandler);
344     }
345 
346     /**
347      * Determine if any state transition is needed for the specific automatic keepalive.
348      */
handleMonitorAutomaticKeepalive(@onNull final AutomaticOnOffKeepalive ki, final int vpnNetId)349     public void handleMonitorAutomaticKeepalive(@NonNull final AutomaticOnOffKeepalive ki,
350             final int vpnNetId) {
351         // Might happen if the automatic keepalive was removed by the app just as the alarm fires.
352         if (!mAutomaticOnOffKeepalives.contains(ki)) return;
353         if (STATE_ALWAYS_ON == ki.mAutomaticOnOffState) {
354             throw new IllegalStateException("Should not monitor non-auto keepalive");
355         }
356 
357         handleMonitorTcpConnections(ki, vpnNetId);
358     }
359 
360     /**
361      * Determine if disable or re-enable keepalive is needed or not based on TCP sockets status.
362      */
handleMonitorTcpConnections(@onNull AutomaticOnOffKeepalive ki, int vpnNetId)363     private void handleMonitorTcpConnections(@NonNull AutomaticOnOffKeepalive ki, int vpnNetId) {
364         // Might happen if the automatic keepalive was removed by the app just as the alarm fires.
365         if (!mAutomaticOnOffKeepalives.contains(ki)) return;
366         if (STATE_ALWAYS_ON == ki.mAutomaticOnOffState) {
367             throw new IllegalStateException("Should not monitor non-auto keepalive");
368         }
369         if (!isAnyTcpSocketConnected(vpnNetId)) {
370             // No TCP socket exists. Stop keepalive if ENABLED, and remain SUSPENDED if currently
371             // SUSPENDED.
372             if (ki.mAutomaticOnOffState == STATE_ENABLED) {
373                 ki.mAutomaticOnOffState = STATE_SUSPENDED;
374                 handlePauseKeepalive(ki.mKi);
375             }
376         } else {
377             handleMaybeResumeKeepalive(ki);
378         }
379         // TODO: listen to socket status instead of periodically check.
380         startTcpPollingAlarm(ki);
381     }
382 
383     /**
384      * Resume an auto on/off keepalive, unless it's already resumed
385      * @param autoKi the keepalive to resume
386      */
handleMaybeResumeKeepalive(@onNull AutomaticOnOffKeepalive autoKi)387     public void handleMaybeResumeKeepalive(@NonNull AutomaticOnOffKeepalive autoKi) {
388         mEventLog.log("Resume keepalive " + autoKi.mCallback + " on " + autoKi.getNetwork());
389         // Might happen if the automatic keepalive was removed by the app just as the alarm fires.
390         if (!mAutomaticOnOffKeepalives.contains(autoKi)) return;
391         if (STATE_ALWAYS_ON == autoKi.mAutomaticOnOffState) {
392             throw new IllegalStateException("Should not resume non-auto keepalive");
393         }
394         if (autoKi.mAutomaticOnOffState == STATE_ENABLED) return;
395         KeepaliveTracker.KeepaliveInfo newKi;
396         try {
397             // Get fd from AutomaticOnOffKeepalive since the fd in the original
398             // KeepaliveInfo should be closed.
399             newKi = autoKi.mKi.withFd(autoKi.mFd);
400         } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
401             Log.e(TAG, "Fail to construct keepalive", e);
402             mKeepaliveTracker.notifyErrorCallback(autoKi.mCallback, ERROR_INVALID_SOCKET);
403             return;
404         }
405         autoKi.mAutomaticOnOffState = STATE_ENABLED;
406         final int error = handleResumeKeepalive(newKi);
407         if (error != SUCCESS) {
408             // Failed to start the keepalive
409             cleanupAutoOnOffKeepalive(autoKi);
410         }
411     }
412 
413     /**
414      * Find the AutomaticOnOffKeepalive associated with a given callback.
415      * @return the keepalive associated with this callback, or null if none
416      */
417     @Nullable
getKeepaliveForBinder(@onNull final IBinder token)418     public AutomaticOnOffKeepalive getKeepaliveForBinder(@NonNull final IBinder token) {
419         ensureRunningOnHandlerThread();
420 
421         return CollectionUtils.findFirst(mAutomaticOnOffKeepalives,
422                 it -> it.mCallback.asBinder().equals(token));
423     }
424 
425     /**
426      * Handle keepalive events from lower layer.
427      *
428      * Forward to KeepaliveTracker.
429      */
handleEventSocketKeepalive(@onNull NetworkAgentInfo nai, int slot, int reason)430     public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai, int slot, int reason) {
431         if (mKeepaliveTracker.handleEventSocketKeepalive(nai, slot, reason)) return;
432 
433         // The keepalive was stopped and so the autoKi should be cleaned up.
434         final AutomaticOnOffKeepalive autoKi =
435                 CollectionUtils.findFirst(
436                         mAutomaticOnOffKeepalives, it -> it.match(nai.network(), slot));
437         if (autoKi == null) {
438             // This may occur when the autoKi gets cleaned up elsewhere (i.e
439             // handleCheckKeepalivesStillValid) while waiting for the network agent to
440             // start the keepalive and the network agent returns an error event.
441             Log.e(TAG, "Attempt cleanup on unknown network, slot");
442             return;
443         }
444         cleanupAutoOnOffKeepalive(autoKi);
445     }
446 
447     /**
448      * Handle stop all keepalives on the specific network.
449      */
handleStopAllKeepalives(NetworkAgentInfo nai, int reason)450     public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
451         mEventLog.log("Stop all keepalives on " + nai.network + " because " + reason);
452         mKeepaliveTracker.handleStopAllKeepalives(nai, reason);
453         final List<AutomaticOnOffKeepalive> matches =
454                 CollectionUtils.filter(mAutomaticOnOffKeepalives, it -> it.mKi.getNai() == nai);
455         for (final AutomaticOnOffKeepalive ki : matches) {
456             if (ki.mAutomaticOnOffState == STATE_SUSPENDED) {
457                 mKeepaliveTracker.finalizePausedKeepalive(ki.mKi, reason);
458             }
459             cleanupAutoOnOffKeepalive(ki);
460         }
461     }
462 
463     /**
464      * Handle start keepalive contained within a message.
465      *
466      * The message is expected to contain a KeepaliveTracker.KeepaliveInfo.
467      */
handleStartKeepalive(Message message)468     public void handleStartKeepalive(Message message) {
469         final AutomaticOnOffKeepalive autoKi = (AutomaticOnOffKeepalive) message.obj;
470         final int error = mKeepaliveTracker.handleStartKeepalive(autoKi.mKi);
471         if (error != SUCCESS) {
472             mEventLog.log("Failed to start keepalive " + autoKi.mCallback + " on "
473                     + autoKi.getNetwork() + " with error " + error);
474             return;
475         }
476         mEventLog.log("Start keepalive " + autoKi.mCallback + " on " + autoKi.getNetwork());
477         mKeepaliveStatsTracker.onStartKeepalive(
478                 autoKi.getNetwork(),
479                 autoKi.mKi.getSlot(),
480                 autoKi.mKi.getNai().networkCapabilities,
481                 autoKi.mKi.getKeepaliveIntervalSec(),
482                 autoKi.mKi.getUid(),
483                 STATE_ALWAYS_ON != autoKi.mAutomaticOnOffState);
484 
485         // Add automatic on/off request into list to track its life cycle.
486         try {
487             autoKi.mKi.mCallback.asBinder().linkToDeath(autoKi, 0);
488         } catch (RemoteException e) {
489             // The underlying keepalive performs its own cleanup
490             autoKi.binderDied();
491             return;
492         }
493         mAutomaticOnOffKeepalives.add(autoKi);
494         if (STATE_ALWAYS_ON != autoKi.mAutomaticOnOffState) {
495             startTcpPollingAlarm(autoKi);
496         }
497     }
498 
499     /**
500      * Handle resume keepalive with the given KeepaliveInfo
501      *
502      * @return SUCCESS if the keepalive is successfully starting and the error reason otherwise.
503      */
handleResumeKeepalive(@onNull final KeepaliveTracker.KeepaliveInfo ki)504     private int handleResumeKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
505         final int error = mKeepaliveTracker.handleStartKeepalive(ki);
506         if (error != SUCCESS) {
507             mEventLog.log("Failed to resume keepalive " + ki.mCallback + " on " + ki.mNai
508                     + " with error " + error);
509             return error;
510         }
511         mKeepaliveStatsTracker.onResumeKeepalive(ki.getNai().network(), ki.getSlot());
512         mEventLog.log("Resumed successfully keepalive " + ki.mCallback + " on " + ki.mNai);
513 
514         return SUCCESS;
515     }
516 
handlePauseKeepalive(@onNull final KeepaliveTracker.KeepaliveInfo ki)517     private void handlePauseKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
518         mEventLog.log("Suspend keepalive " + ki.mCallback + " on " + ki.mNai);
519         mKeepaliveStatsTracker.onPauseKeepalive(ki.getNai().network(), ki.getSlot());
520         // TODO : mKT.handleStopKeepalive should take a KeepaliveInfo instead
521         mKeepaliveTracker.handleStopKeepalive(ki.getNai(), ki.getSlot(), SUCCESS_PAUSED);
522     }
523 
524     /**
525      * Handle stop keepalives on the specific network with given slot.
526      */
handleStopKeepalive(@onNull final AutomaticOnOffKeepalive autoKi, int reason)527     public void handleStopKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi, int reason) {
528         mEventLog.log("Stop keepalive " + autoKi.mCallback + " because " + reason);
529         // Stop the keepalive unless it was suspended. This includes the case where it's managed
530         // but enabled, and the case where it's always on.
531         if (autoKi.mAutomaticOnOffState != STATE_SUSPENDED) {
532             final KeepaliveTracker.KeepaliveInfo ki = autoKi.mKi;
533             mKeepaliveTracker.handleStopKeepalive(ki.getNai(), ki.getSlot(), reason);
534         } else {
535             mKeepaliveTracker.finalizePausedKeepalive(autoKi.mKi, reason);
536         }
537 
538         cleanupAutoOnOffKeepalive(autoKi);
539     }
540 
cleanupAutoOnOffKeepalive(@onNull final AutomaticOnOffKeepalive autoKi)541     private void cleanupAutoOnOffKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi) {
542         ensureRunningOnHandlerThread();
543         mKeepaliveStatsTracker.onStopKeepalive(autoKi.getNetwork(), autoKi.mKi.getSlot());
544         autoKi.close();
545         if (null != autoKi.mAlarmListener) mAlarmManager.cancel(autoKi.mAlarmListener);
546 
547         // If the KI is not in the array, it's because it was already removed, or it was never
548         // added ; the only ways this can happen is if the keepalive is stopped by the app and the
549         // app dies immediately, or if the app died before the link to death could be registered.
550         if (!mAutomaticOnOffKeepalives.remove(autoKi)) return;
551 
552         autoKi.mKi.mCallback.asBinder().unlinkToDeath(autoKi, 0);
553     }
554 
555     /**
556      * Called when requesting that keepalives be started on a IPsec NAT-T socket. See
557      * {@link android.net.SocketKeepalive}.
558      *
559      * Forward to KeepaliveTracker.
560      **/
startNattKeepalive(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, int srcPort, @NonNull String dstAddrString, int dstPort, boolean automaticOnOffKeepalives, @Nullable Network underpinnedNetwork)561     public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
562             @Nullable FileDescriptor fd,
563             int intervalSeconds,
564             @NonNull ISocketKeepaliveCallback cb,
565             @NonNull String srcAddrString,
566             int srcPort,
567             @NonNull String dstAddrString,
568             int dstPort, boolean automaticOnOffKeepalives, @Nullable Network underpinnedNetwork) {
569         final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeNattKeepaliveInfo(nai, fd,
570                 intervalSeconds, cb, srcAddrString, srcPort, dstAddrString, dstPort);
571         if (null == ki) return;
572         try {
573             final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
574                     automaticOnOffKeepalives, underpinnedNetwork);
575             mEventLog.log("Start natt keepalive " + cb + " on " + nai.network
576                     + " " + srcAddrString + ":" + srcPort
577                     + " → " + dstAddrString + ":" + dstPort
578                     + " auto=" + autoKi
579                     + " underpinned=" + underpinnedNetwork);
580             mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
581                     .sendToTarget();
582         } catch (InvalidSocketException e) {
583             mKeepaliveTracker.notifyErrorCallback(cb, e.error);
584         }
585     }
586 
587     /**
588      * Called when requesting that keepalives be started on a IPsec NAT-T socket. See
589      * {@link android.net.SocketKeepalive}.
590      *
591      * Forward to KeepaliveTracker.
592      **/
startNattKeepalive(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int resourceId, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, @NonNull String dstAddrString, int dstPort, boolean automaticOnOffKeepalives, @Nullable Network underpinnedNetwork)593     public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
594             @Nullable FileDescriptor fd,
595             int resourceId,
596             int intervalSeconds,
597             @NonNull ISocketKeepaliveCallback cb,
598             @NonNull String srcAddrString,
599             @NonNull String dstAddrString,
600             int dstPort,
601             boolean automaticOnOffKeepalives,
602             @Nullable Network underpinnedNetwork) {
603         final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeNattKeepaliveInfo(nai, fd,
604                 resourceId, intervalSeconds, cb, srcAddrString, dstAddrString, dstPort);
605         if (null == ki) return;
606         try {
607             final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
608                     automaticOnOffKeepalives, underpinnedNetwork);
609             mEventLog.log("Start natt keepalive " + cb + " on " + nai.network
610                     + " " + srcAddrString
611                     + " → " + dstAddrString + ":" + dstPort
612                     + " auto=" + autoKi
613                     + " underpinned=" + underpinnedNetwork);
614             mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
615                     .sendToTarget();
616         } catch (InvalidSocketException e) {
617             mKeepaliveTracker.notifyErrorCallback(cb, e.error);
618         }
619     }
620 
621     /**
622      * Called by ConnectivityService to start TCP keepalive on a file descriptor.
623      *
624      * In order to offload keepalive for application correctly, sequence number, ack number and
625      * other fields are needed to form the keepalive packet. Thus, this function synchronously
626      * puts the socket into repair mode to get the necessary information. After the socket has been
627      * put into repair mode, the application cannot access the socket until reverted to normal.
628      * See {@link android.net.SocketKeepalive}.
629      *
630      * Forward to KeepaliveTracker.
631      **/
startTcpKeepalive(@ullable NetworkAgentInfo nai, @NonNull FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb)632     public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
633             @NonNull FileDescriptor fd,
634             int intervalSeconds,
635             @NonNull ISocketKeepaliveCallback cb) {
636         final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeTcpKeepaliveInfo(nai, fd,
637                 intervalSeconds, cb);
638         if (null == ki) return;
639         try {
640             final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
641                     false /* autoOnOff, tcp keepalives are never auto on/off */,
642                     null /* underpinnedNetwork, tcp keepalives do not refer to this */);
643             mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
644                     .sendToTarget();
645         } catch (InvalidSocketException e) {
646             mKeepaliveTracker.notifyErrorCallback(cb, e.error);
647         }
648     }
649 
650     /**
651      * Dump AutomaticOnOffKeepaliveTracker state.
652      */
dump(IndentingPrintWriter pw)653     public void dump(IndentingPrintWriter pw) {
654         mKeepaliveTracker.dump(pw);
655         final boolean featureEnabled = mDependencies.isFeatureEnabled(
656                 AUTOMATIC_ON_OFF_KEEPALIVE_VERSION, true /* defaultEnabled */);
657         pw.println("AutomaticOnOff enabled: " + featureEnabled);
658         pw.increaseIndent();
659         for (AutomaticOnOffKeepalive autoKi : mAutomaticOnOffKeepalives) {
660             pw.println(autoKi.toString());
661         }
662         pw.decreaseIndent();
663 
664         pw.println("Events (most recent first):");
665         pw.increaseIndent();
666         mEventLog.reverseDump(pw);
667         pw.decreaseIndent();
668     }
669 
670     /**
671      * Check all keepalives on the network are still valid.
672      *
673      * Forward to KeepaliveTracker.
674      */
handleCheckKeepalivesStillValid(NetworkAgentInfo nai)675     public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
676         ArrayList<Pair<AutomaticOnOffKeepalive, Integer>> invalidKeepalives = null;
677 
678         for (final AutomaticOnOffKeepalive autoKi : mAutomaticOnOffKeepalives) {
679             if (!nai.equals(autoKi.mKi.mNai)) continue;
680             final int error = autoKi.mKi.isValid();
681             if (error != SUCCESS) {
682                 if (invalidKeepalives == null) {
683                     invalidKeepalives = new ArrayList<>();
684                 }
685                 invalidKeepalives.add(Pair.create(autoKi, error));
686             }
687         }
688         if (invalidKeepalives == null) return;
689         for (final Pair<AutomaticOnOffKeepalive, Integer> keepaliveAndError : invalidKeepalives) {
690             handleStopKeepalive(keepaliveAndError.first, keepaliveAndError.second);
691         }
692     }
693 
694     @VisibleForTesting
isAnyTcpSocketConnected(int netId)695     boolean isAnyTcpSocketConnected(int netId) {
696         FileDescriptor fd = null;
697 
698         try {
699             fd = mDependencies.createConnectedNetlinkSocket();
700 
701             // Get network mask
702             final MarkMaskParcel parcel = mNetd.getFwmarkForNetwork(netId);
703             final int networkMark = (parcel != null) ? parcel.mark : NetlinkUtils.UNKNOWN_MARK;
704             final int networkMask = (parcel != null) ? parcel.mask : NetlinkUtils.NULL_MASK;
705 
706             // Send request for each IP family
707             for (final int family : ADDRESS_FAMILIES) {
708                 if (isAnyTcpSocketConnectedForFamily(fd, family, networkMark, networkMask)) {
709                     return true;
710                 }
711             }
712         } catch (ErrnoException | SocketException | InterruptedIOException | RemoteException e) {
713             Log.e(TAG, "Fail to get socket info via netlink.", e);
714         } finally {
715             SocketUtils.closeSocketQuietly(fd);
716         }
717 
718         return false;
719     }
720 
isAnyTcpSocketConnectedForFamily(FileDescriptor fd, int family, int networkMark, int networkMask)721     private boolean isAnyTcpSocketConnectedForFamily(FileDescriptor fd, int family, int networkMark,
722             int networkMask) throws ErrnoException, InterruptedIOException {
723         ensureRunningOnHandlerThread();
724         // Build SocketDiag messages and cache it.
725         if (mSockDiagMsg.get(family) == null) {
726             mSockDiagMsg.put(family, InetDiagMessage.buildInetDiagReqForAliveTcpSockets(family));
727         }
728         mDependencies.sendRequest(fd, mSockDiagMsg.get(family));
729 
730         // Iteration limitation as a protection to avoid possible infinite loops.
731         // DEFAULT_RECV_BUFSIZE could read more than 20 sockets per time. Max iteration
732         // should be enough to go through reasonable TCP sockets in the device.
733         final int maxIteration = 100;
734         int parsingIteration = 0;
735         while (parsingIteration < maxIteration) {
736             final ByteBuffer bytes = mDependencies.recvSockDiagResponse(fd);
737 
738             try {
739                 while (NetlinkUtils.enoughBytesRemainForValidNlMsg(bytes)) {
740                     final int startPos = bytes.position();
741 
742                     final int nlmsgLen = bytes.getInt();
743                     final int nlmsgType = bytes.getShort();
744                     if (isEndOfMessageOrError(nlmsgType)) return false;
745                     // TODO: Parse InetDiagMessage to get uid and dst address information to filter
746                     //  socket via NetlinkMessage.parse.
747 
748                     // Skip the header to move to data part.
749                     bytes.position(startPos + SOCKDIAG_MSG_HEADER_SIZE);
750 
751                     if (isTargetTcpSocket(bytes, nlmsgLen, networkMark, networkMask)) {
752                         if (Log.isLoggable(TAG, Log.DEBUG)) {
753                             bytes.position(startPos);
754                             final InetDiagMessage diagMsg = (InetDiagMessage) NetlinkMessage.parse(
755                                     bytes, OsConstants.NETLINK_INET_DIAG);
756                             Log.d(TAG, String.format("Found open TCP connection by uid %d to %s"
757                                             + " cookie %d",
758                                     diagMsg.inetDiagMsg.idiag_uid,
759                                     diagMsg.inetDiagMsg.id.remSocketAddress,
760                                     diagMsg.inetDiagMsg.id.cookie));
761                         }
762                         return true;
763                     }
764                 }
765             } catch (BufferUnderflowException e) {
766                 // The exception happens in random place in either header position or any data
767                 // position. Partial bytes from the middle of the byte buffer may not be enough to
768                 // clarify, so print out the content before the error to possibly prevent printing
769                 // the whole 8K buffer.
770                 final int exceptionPos = bytes.position();
771                 final String hex = HexDump.dumpHexString(bytes.array(), 0, exceptionPos);
772                 Log.e(TAG, "Unexpected socket info parsing: " + hex, e);
773             }
774 
775             parsingIteration++;
776         }
777         return false;
778     }
779 
isEndOfMessageOrError(int nlmsgType)780     private boolean isEndOfMessageOrError(int nlmsgType) {
781         return nlmsgType == NLMSG_DONE || nlmsgType != SOCK_DIAG_BY_FAMILY;
782     }
783 
isTargetTcpSocket(@onNull ByteBuffer bytes, int nlmsgLen, int networkMark, int networkMask)784     private boolean isTargetTcpSocket(@NonNull ByteBuffer bytes, int nlmsgLen, int networkMark,
785             int networkMask) {
786         final int mark = readSocketDataAndReturnMark(bytes, nlmsgLen);
787         return (mark & networkMask) == networkMark;
788     }
789 
readSocketDataAndReturnMark(@onNull ByteBuffer bytes, int nlmsgLen)790     private int readSocketDataAndReturnMark(@NonNull ByteBuffer bytes, int nlmsgLen) {
791         final int nextMsgOffset = bytes.position() + nlmsgLen - SOCKDIAG_MSG_HEADER_SIZE;
792         int mark = NetlinkUtils.INIT_MARK_VALUE;
793         // Get socket mark
794         // TODO: Add a parsing method in NetlinkMessage.parse to support this to skip the remaining
795         //  data.
796         while (bytes.position() < nextMsgOffset) {
797             final StructNlAttr nlattr = StructNlAttr.parse(bytes);
798             if (nlattr != null && nlattr.nla_type == NetlinkUtils.INET_DIAG_MARK) {
799                 mark = nlattr.getValueAsInteger();
800             }
801         }
802         return mark;
803     }
804 
ensureRunningOnHandlerThread()805     private void ensureRunningOnHandlerThread() {
806         if (mConnectivityServiceHandler.getLooper().getThread() != Thread.currentThread()) {
807             throw new IllegalStateException(
808                     "Not running on handler thread: " + Thread.currentThread().getName());
809         }
810     }
811 
getTcpPollingIntervalMs(@onNull AutomaticOnOffKeepalive ki)812     private long getTcpPollingIntervalMs(@NonNull AutomaticOnOffKeepalive ki) {
813         final boolean useLowTimer = mTestLowTcpPollingTimerUntilMs > System.currentTimeMillis();
814         // Adjust the polling interval to be smaller than the keepalive delay to preserve
815         // some time for the system to restart the keepalive.
816         final int timer = ki.mKi.getKeepaliveIntervalSec() * 1000 - ADJUST_TCP_POLLING_DELAY_MS;
817         if (timer < MIN_INTERVAL_SEC) {
818             Log.wtf(TAG, "Unreasonably low keepalive delay: " + ki.mKi.getKeepaliveIntervalSec());
819         }
820         return useLowTimer ? LOW_TCP_POLLING_INTERVAL_MS : Math.max(timer, MIN_INTERVAL_SEC);
821     }
822 
823     /**
824      * Temporarily use low TCP polling timer for testing.
825      * The value works when the time set is more than {@link System.currentTimeMillis()}.
826      */
handleSetTestLowTcpPollingTimer(long timeMs)827     public void handleSetTestLowTcpPollingTimer(long timeMs) {
828         Log.d(TAG, "handleSetTestLowTcpPollingTimer: " + timeMs);
829         mTestLowTcpPollingTimerUntilMs = timeMs;
830     }
831 
832     /**
833      * Dependencies class for testing.
834      */
835     @VisibleForTesting
836     public static class Dependencies {
837         private final Context mContext;
838 
Dependencies(final Context context)839         public Dependencies(final Context context) {
840             mContext = context;
841         }
842 
843         /**
844          * Create a netlink socket connected to the kernel.
845          *
846          * @return fd the fileDescriptor of the socket.
847          */
createConnectedNetlinkSocket()848         public FileDescriptor createConnectedNetlinkSocket()
849                 throws ErrnoException, SocketException {
850             final FileDescriptor fd = NetlinkUtils.createNetLinkInetDiagSocket();
851             NetlinkUtils.connectSocketToNetlink(fd);
852             Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO,
853                     StructTimeval.fromMillis(IO_TIMEOUT_MS));
854             return fd;
855         }
856 
857         /**
858          * Send composed message request to kernel.
859          *
860          * The given FileDescriptor is expected to be created by
861          * {@link #createConnectedNetlinkSocket} or equivalent way.
862          *
863          * @param fd a netlink socket {@code FileDescriptor} connected to the kernel.
864          * @param msg the byte array representing the request message to write to kernel.
865          */
sendRequest(@onNull final FileDescriptor fd, @NonNull final byte[] msg)866         public void sendRequest(@NonNull final FileDescriptor fd,
867                 @NonNull final byte[] msg)
868                 throws ErrnoException, InterruptedIOException {
869             Os.write(fd, msg, 0 /* byteOffset */, msg.length);
870         }
871 
872         /**
873          * Get an INetd connector.
874          */
getNetd()875         public INetd getNetd() {
876             return INetd.Stub.asInterface(
877                     (IBinder) mContext.getSystemService(Context.NETD_SERVICE));
878         }
879 
880         /**
881          * Get an instance of AlarmManager
882          */
getAlarmManager(@onNull final Context ctx)883         public AlarmManager getAlarmManager(@NonNull final Context ctx) {
884             return ctx.getSystemService(AlarmManager.class);
885         }
886 
887         /**
888          * Receive the response message from kernel via given {@code FileDescriptor}.
889          * The usage should follow the {@code #sendRequest} call with the same
890          * FileDescriptor.
891          *
892          * The overall response may be large but the individual messages should not be
893          * excessively large(8-16kB) because trying to get the kernel to return
894          * everything in one big buffer is inefficient as it forces the kernel to allocate
895          * large chunks of linearly physically contiguous memory. The usage should iterate the
896          * call of this method until the end of the overall message.
897          *
898          * The default receiving buffer size should be small enough that it is always
899          * processed within the {@link NetlinkUtils#IO_TIMEOUT_MS} timeout.
900          */
recvSockDiagResponse(@onNull final FileDescriptor fd)901         public ByteBuffer recvSockDiagResponse(@NonNull final FileDescriptor fd)
902                 throws ErrnoException, InterruptedIOException {
903             return NetlinkUtils.recvMessage(
904                     fd, NetlinkUtils.DEFAULT_RECV_BUFSIZE, NetlinkUtils.IO_TIMEOUT_MS);
905         }
906 
907         /**
908          * Construct a new KeepaliveTracker.
909          */
newKeepaliveTracker(@onNull Context context, @NonNull Handler connectivityserviceHander)910         public KeepaliveTracker newKeepaliveTracker(@NonNull Context context,
911                 @NonNull Handler connectivityserviceHander) {
912             return new KeepaliveTracker(mContext, connectivityserviceHander);
913         }
914 
915         /**
916          * Construct a new KeepaliveStatsTracker.
917          */
newKeepaliveStatsTracker(@onNull Context context, @NonNull Handler connectivityserviceHander)918         public KeepaliveStatsTracker newKeepaliveStatsTracker(@NonNull Context context,
919                 @NonNull Handler connectivityserviceHander) {
920             return new KeepaliveStatsTracker(context, connectivityserviceHander);
921         }
922 
923         /**
924          * Find out if a feature is enabled from DeviceConfig.
925          *
926          * @param name The name of the property to look up.
927          * @param defaultEnabled whether to consider the feature enabled in the absence of
928          *                       the flag. This MUST be a statically-known constant.
929          * @return whether the feature is enabled
930          */
isFeatureEnabled(@onNull final String name, final boolean defaultEnabled)931         public boolean isFeatureEnabled(@NonNull final String name, final boolean defaultEnabled) {
932             // Reading DeviceConfig will check if the calling uid and calling package name are the
933             // same. Clear calling identity to align the calling uid and package so that it won't
934             // fail if cts would like to do the dump()
935             return BinderUtils.withCleanCallingIdentity(() ->
936                     DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_TETHERING, name,
937                     DeviceConfigUtils.TETHERING_MODULE_NAME, defaultEnabled));
938         }
939 
940         /**
941          * Returns milliseconds since boot, including time spent in sleep.
942          *
943          * @return elapsed milliseconds since boot.
944          */
getElapsedRealtime()945         public long getElapsedRealtime() {
946             return SystemClock.elapsedRealtime();
947         }
948     }
949 }
950