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