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