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