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