1 /* 2 * Copyright (C) 2015 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.content.pm.PackageManager.PERMISSION_GRANTED; 20 import static android.net.NattSocketKeepalive.NATT_PORT; 21 import static android.net.SocketKeepalive.BINDER_DIED; 22 import static android.net.SocketKeepalive.DATA_RECEIVED; 23 import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES; 24 import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL; 25 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; 26 import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; 27 import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; 28 import static android.net.SocketKeepalive.ERROR_NO_SUCH_SLOT; 29 import static android.net.SocketKeepalive.ERROR_STOP_REASON_UNINITIALIZED; 30 import static android.net.SocketKeepalive.ERROR_UNSUPPORTED; 31 import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; 32 import static android.net.SocketKeepalive.MIN_INTERVAL_SEC; 33 import static android.net.SocketKeepalive.NO_KEEPALIVE; 34 import static android.net.SocketKeepalive.SUCCESS; 35 import static android.net.SocketKeepalive.SUCCESS_PAUSED; 36 37 import android.annotation.NonNull; 38 import android.annotation.Nullable; 39 import android.content.Context; 40 import android.net.ISocketKeepaliveCallback; 41 import android.net.InetAddresses; 42 import android.net.InvalidPacketException; 43 import android.net.KeepalivePacketData; 44 import android.net.NattKeepalivePacketData; 45 import android.net.NetworkAgent; 46 import android.net.SocketKeepalive.InvalidSocketException; 47 import android.net.TcpKeepalivePacketData; 48 import android.net.util.KeepaliveUtils; 49 import android.os.Binder; 50 import android.os.Handler; 51 import android.os.IBinder; 52 import android.os.Process; 53 import android.os.RemoteException; 54 import android.system.ErrnoException; 55 import android.system.Os; 56 import android.util.Log; 57 58 import com.android.connectivity.resources.R; 59 import com.android.internal.annotations.VisibleForTesting; 60 import com.android.internal.util.IndentingPrintWriter; 61 import com.android.net.module.util.HexDump; 62 import com.android.net.module.util.IpUtils; 63 64 import java.io.FileDescriptor; 65 import java.net.InetAddress; 66 import java.net.InetSocketAddress; 67 import java.net.SocketAddress; 68 import java.util.ArrayList; 69 import java.util.Arrays; 70 import java.util.HashMap; 71 72 /** 73 * Manages socket keepalive requests. 74 * 75 * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all 76 * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its 77 * handle* methods must be called only from the ConnectivityService handler thread. 78 */ 79 public class KeepaliveTracker { 80 81 private static final String TAG = "KeepaliveTracker"; 82 private static final boolean DBG = false; 83 84 public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD; 85 86 /** Keeps track of keepalive requests. */ 87 private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives = 88 new HashMap<> (); 89 @NonNull 90 private final TcpKeepaliveController mTcpController; 91 @NonNull 92 private final Context mContext; 93 94 // Supported keepalive count for each transport type, can be configured through 95 // config_networkSupportedKeepaliveCount. For better error handling, use 96 // {@link getSupportedKeepalivesForNetworkCapabilities} instead of direct access. 97 @NonNull 98 private final int[] mSupportedKeepalives; 99 100 // Reserved privileged keepalive slots per transport. Caller's permission will be enforced if 101 // the number of remaining keepalive slots is less than or equal to the threshold. 102 private final int mReservedPrivilegedSlots; 103 104 // Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if 105 // the number of remaining keepalive slots is less than or equal to the threshold. 106 private final int mAllowedUnprivilegedSlotsForUid; 107 KeepaliveTracker(Context context, Handler handler)108 public KeepaliveTracker(Context context, Handler handler) { 109 this(context, handler, new TcpKeepaliveController(handler)); 110 } 111 112 @VisibleForTesting KeepaliveTracker(Context context, Handler handler, TcpKeepaliveController tcpController)113 KeepaliveTracker(Context context, Handler handler, TcpKeepaliveController tcpController) { 114 mTcpController = tcpController; 115 mContext = context; 116 117 mSupportedKeepalives = KeepaliveResourceUtil.getSupportedKeepalives(context); 118 119 final ConnectivityResources res = new ConnectivityResources(mContext); 120 mReservedPrivilegedSlots = res.get().getInteger( 121 R.integer.config_reservedPrivilegedKeepaliveSlots); 122 mAllowedUnprivilegedSlotsForUid = res.get().getInteger( 123 R.integer.config_allowedUnprivilegedKeepalivePerUid); 124 } 125 126 /** 127 * Tracks information about a socket keepalive. 128 * 129 * All information about this keepalive is known at construction time except the slot number, 130 * which is only returned when the hardware has successfully started the keepalive. 131 */ 132 @VisibleForTesting 133 public class KeepaliveInfo implements IBinder.DeathRecipient { 134 // TODO : remove this member. Only AutoOnOffKeepalive should have a reference to this. 135 public final ISocketKeepaliveCallback mCallback; 136 // Bookkeeping data. 137 private final int mUid; 138 private final int mPid; 139 private final boolean mPrivileged; 140 public final NetworkAgentInfo mNai; 141 private final int mType; 142 public final FileDescriptor mFd; 143 // True if this was resumed from a previously turned off keepalive, otherwise false. 144 // This is necessary to send the correct callbacks. 145 public final boolean mResumed; 146 147 public static final int TYPE_NATT = 1; 148 public static final int TYPE_TCP = 2; 149 150 // Keepalive slot. A small integer that identifies this keepalive among the ones handled 151 // by this network. This is initialized to NO_KEEPALIVE for new keepalives, but to the 152 // old slot for resumed keepalives. 153 private int mSlot; 154 155 // Packet data. 156 private final KeepalivePacketData mPacket; 157 private final int mInterval; 158 159 // Whether the keepalive is started or not. The initial state is NOT_STARTED. 160 private static final int NOT_STARTED = 1; 161 private static final int STARTING = 2; 162 private static final int STARTED = 3; 163 private static final int STOPPING = 4; 164 private int mStartedState = NOT_STARTED; 165 private int mStopReason = ERROR_STOP_REASON_UNINITIALIZED; 166 KeepaliveInfo(@onNull ISocketKeepaliveCallback callback, @NonNull NetworkAgentInfo nai, @NonNull KeepalivePacketData packet, int interval, int type, @Nullable FileDescriptor fd)167 KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback, 168 @NonNull NetworkAgentInfo nai, 169 @NonNull KeepalivePacketData packet, 170 int interval, 171 int type, 172 @Nullable FileDescriptor fd) throws InvalidSocketException { 173 this(callback, nai, packet, Binder.getCallingPid(), Binder.getCallingUid(), interval, 174 type, fd, NO_KEEPALIVE /* slot */, false /* resumed */); 175 } 176 KeepaliveInfo(@onNull ISocketKeepaliveCallback callback, @NonNull NetworkAgentInfo nai, @NonNull KeepalivePacketData packet, int pid, int uid, int interval, int type, @Nullable FileDescriptor fd, int slot, boolean resumed)177 KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback, 178 @NonNull NetworkAgentInfo nai, 179 @NonNull KeepalivePacketData packet, 180 int pid, 181 int uid, 182 int interval, 183 int type, 184 @Nullable FileDescriptor fd, 185 int slot, 186 boolean resumed) throws InvalidSocketException { 187 mCallback = callback; 188 mPid = pid; 189 mUid = uid; 190 mPrivileged = (PERMISSION_GRANTED == mContext.checkPermission(PERMISSION, mPid, mUid)); 191 192 mNai = nai; 193 mPacket = packet; 194 mInterval = interval; 195 mType = type; 196 mSlot = slot; 197 mResumed = resumed; 198 199 // For SocketKeepalive, a dup of fd is kept in mFd so the source port from which the 200 // keepalives are sent cannot be reused by another app even if the fd gets closed by 201 // the user. A null is acceptable here for backward compatibility of PacketKeepalive 202 // API. 203 try { 204 if (fd != null) { 205 mFd = Os.dup(fd); 206 } else { 207 Log.d(TAG, toString() + " calls with null fd"); 208 if (!mPrivileged) { 209 throw new SecurityException( 210 "null fd is not allowed for unprivileged access."); 211 } 212 if (mType == TYPE_TCP) { 213 throw new IllegalArgumentException( 214 "null fd is not allowed for tcp socket keepalives."); 215 } 216 mFd = null; 217 } 218 } catch (ErrnoException e) { 219 Log.e(TAG, "Cannot dup fd: ", e); 220 throw new InvalidSocketException(ERROR_INVALID_SOCKET, e); 221 } 222 223 try { 224 mCallback.asBinder().linkToDeath(this, 0); 225 } catch (RemoteException e) { 226 binderDied(); 227 } 228 } 229 getNai()230 public NetworkAgentInfo getNai() { 231 return mNai; 232 } 233 startedStateString(final int state)234 private String startedStateString(final int state) { 235 switch (state) { 236 case NOT_STARTED : return "NOT_STARTED"; 237 case STARTING : return "STARTING"; 238 case STARTED : return "STARTED"; 239 case STOPPING : return "STOPPING"; 240 } 241 throw new IllegalArgumentException("Unknown state"); 242 } 243 toString()244 public String toString() { 245 return "KeepaliveInfo [" 246 + " type=" + mType 247 + " network=" + mNai.network 248 + " startedState=" + startedStateString(mStartedState) 249 + " " 250 + IpUtils.addressAndPortToString(mPacket.getSrcAddress(), mPacket.getSrcPort()) 251 + "->" 252 + IpUtils.addressAndPortToString(mPacket.getDstAddress(), mPacket.getDstPort()) 253 + " interval=" + mInterval 254 + " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged 255 + " packetData=" + HexDump.toHexString(mPacket.getPacket()) 256 + " ]"; 257 } 258 259 /** Called when the application process is killed. */ binderDied()260 public void binderDied() { 261 // TODO b/267106526 : this is not called on the handler thread but stop() happily 262 // assumes it is, which means this is a pretty dangerous race condition. 263 stop(BINDER_DIED); 264 } 265 unlinkDeathRecipient()266 void unlinkDeathRecipient() { 267 if (mCallback != null) { 268 mCallback.asBinder().unlinkToDeath(this, 0); 269 } 270 } 271 getSlot()272 public int getSlot() { 273 return mSlot; 274 } 275 getKeepaliveIntervalSec()276 int getKeepaliveIntervalSec() { 277 return mInterval; 278 } 279 getUid()280 public int getUid() { 281 return mUid; 282 } 283 checkNetworkConnected()284 private int checkNetworkConnected() { 285 if (!mNai.networkInfo.isConnectedOrConnecting()) { 286 return ERROR_INVALID_NETWORK; 287 } 288 return SUCCESS; 289 } 290 checkSourceAddress()291 private int checkSourceAddress() { 292 // Check that we have the source address. 293 for (InetAddress address : mNai.linkProperties.getAddresses()) { 294 if (address.equals(mPacket.getSrcAddress())) { 295 return SUCCESS; 296 } 297 } 298 return ERROR_INVALID_IP_ADDRESS; 299 } 300 checkInterval()301 private int checkInterval() { 302 if (mInterval < MIN_INTERVAL_SEC || mInterval > MAX_INTERVAL_SEC) { 303 return ERROR_INVALID_INTERVAL; 304 } 305 return SUCCESS; 306 } 307 checkPermission()308 private int checkPermission() { 309 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); 310 if (networkKeepalives == null) { 311 return ERROR_INVALID_NETWORK; 312 } 313 314 if (mPrivileged) return SUCCESS; 315 316 final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( 317 mSupportedKeepalives, mNai.networkCapabilities); 318 319 int takenUnprivilegedSlots = 0; 320 for (final KeepaliveInfo ki : networkKeepalives.values()) { 321 if (!ki.mPrivileged) ++takenUnprivilegedSlots; 322 } 323 if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) { 324 return ERROR_INSUFFICIENT_RESOURCES; 325 } 326 327 // Count unprivileged keepalives for the same uid across networks. 328 int unprivilegedCountSameUid = 0; 329 for (final HashMap<Integer, KeepaliveInfo> kaForNetwork : mKeepalives.values()) { 330 for (final KeepaliveInfo ki : kaForNetwork.values()) { 331 if (ki.mUid == mUid) { 332 unprivilegedCountSameUid++; 333 } 334 } 335 } 336 if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) { 337 return ERROR_INSUFFICIENT_RESOURCES; 338 } 339 return SUCCESS; 340 } 341 checkLimit()342 private int checkLimit() { 343 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); 344 if (networkKeepalives == null) { 345 return ERROR_INVALID_NETWORK; 346 } 347 final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( 348 mSupportedKeepalives, mNai.networkCapabilities); 349 if (supported == 0) return ERROR_UNSUPPORTED; 350 if (networkKeepalives.size() > supported) return ERROR_INSUFFICIENT_RESOURCES; 351 return SUCCESS; 352 } 353 354 /** 355 * Checks if the keepalive info is valid to start. 356 * 357 * @return SUCCESS if the keepalive is valid and the error reason otherwise. 358 */ isValid()359 public int isValid() { 360 synchronized (mNai) { 361 int error = checkInterval(); 362 if (error == SUCCESS) error = checkLimit(); 363 if (error == SUCCESS) error = checkPermission(); 364 if (error == SUCCESS) error = checkNetworkConnected(); 365 if (error == SUCCESS) error = checkSourceAddress(); 366 return error; 367 } 368 } 369 370 /** 371 * Attempt to start the keepalive on the given slot. 372 * 373 * @param slot the slot to start the keepalive on. 374 * @return SUCCESS if the keepalive is successfully starting and the error reason otherwise. 375 */ start(int slot)376 int start(int slot) { 377 // BINDER_DIED can happen if the binder died before the KeepaliveInfo was created and 378 // the constructor set the state to BINDER_DIED. If that's the case, the KI is already 379 // cleaned up. 380 if (BINDER_DIED == mStartedState) return BINDER_DIED; 381 mSlot = slot; 382 int error = isValid(); 383 if (error == SUCCESS) { 384 Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString()); 385 switch (mType) { 386 case TYPE_NATT: 387 final NattKeepalivePacketData nattData = (NattKeepalivePacketData) mPacket; 388 mNai.onAddNattKeepalivePacketFilter(slot, nattData); 389 mNai.onStartNattSocketKeepalive(slot, mInterval, nattData); 390 break; 391 case TYPE_TCP: 392 try { 393 mTcpController.startSocketMonitor(mFd, mCallback, mSlot); 394 } catch (InvalidSocketException e) { 395 handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET); 396 return ERROR_INVALID_SOCKET; 397 } 398 final TcpKeepalivePacketData tcpData = (TcpKeepalivePacketData) mPacket; 399 mNai.onAddTcpKeepalivePacketFilter(slot, tcpData); 400 // TODO: check result from apf and notify of failure as needed. 401 mNai.onStartTcpSocketKeepalive(slot, mInterval, tcpData); 402 break; 403 default: 404 Log.wtf(TAG, "Starting keepalive with unknown type: " + mType); 405 handleStopKeepalive(mNai, mSlot, ERROR_UNSUPPORTED); 406 return ERROR_UNSUPPORTED; 407 } 408 mStartedState = STARTING; 409 return SUCCESS; 410 } else { 411 handleStopKeepalive(mNai, mSlot, error); 412 return error; 413 } 414 } 415 stop(int reason)416 void stop(int reason) { 417 int uid = Binder.getCallingUid(); 418 if (uid != mUid && uid != Process.SYSTEM_UID) { 419 if (DBG) { 420 Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network); 421 } 422 } 423 // To prevent races from re-entrance of stop(), return if the state is already stopping. 424 // This might happen if multiple event sources stop keepalive in a short time. Such as 425 // network disconnect after user calls stop(), or tear down socket after binder died. 426 // Note that it's always possible this method is called by the auto keepalive timer 427 // or any other way after the binder died, hence the check for BINDER_DIED. If the 428 // binder has died, then the KI has already been cleaned up. 429 if (mStartedState == STOPPING || mStartedState == BINDER_DIED) return; 430 431 // Store the reason of stopping, and report it after the keepalive is fully stopped. 432 if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) { 433 throw new IllegalStateException("Unexpected stop reason: " + mStopReason); 434 } 435 mStopReason = reason; 436 Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString() 437 + ": " + reason); 438 switch (mStartedState) { 439 case NOT_STARTED: 440 // Remove the reference to this keepalive that had an error before starting, 441 // e.g. invalid parameter. 442 cleanupStoppedKeepalive(mNai, mSlot); 443 if (BINDER_DIED == reason) mStartedState = BINDER_DIED; 444 break; 445 default: 446 mStartedState = STOPPING; 447 switch (mType) { 448 case TYPE_TCP: 449 mTcpController.stopSocketMonitor(mSlot); 450 // fall through 451 case TYPE_NATT: 452 mNai.onStopSocketKeepalive(mSlot); 453 mNai.onRemoveKeepalivePacketFilter(mSlot); 454 break; 455 default: 456 Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType); 457 } 458 } 459 460 // Close the duplicated fd that maintains the lifecycle of socket whenever 461 // keepalive is running. 462 if (mFd != null) { 463 try { 464 Os.close(mFd); 465 } catch (ErrnoException e) { 466 // This should not happen since system server controls the lifecycle of fd when 467 // keepalive offload is running. 468 Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e); 469 } 470 } 471 } 472 473 /** 474 * Construct a new KeepaliveInfo from existing KeepaliveInfo with a new fd. 475 */ withFd(@onNull FileDescriptor fd)476 public KeepaliveInfo withFd(@NonNull FileDescriptor fd) throws InvalidSocketException { 477 return new KeepaliveInfo(mCallback, mNai, mPacket, mPid, mUid, mInterval, mType, 478 fd, mSlot, true /* resumed */); 479 } 480 } 481 notifyErrorCallback(ISocketKeepaliveCallback cb, int error)482 void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) { 483 if (DBG) Log.w(TAG, "Sending onError(" + error + ") callback"); 484 try { 485 cb.onError(error); 486 } catch (RemoteException e) { 487 Log.w(TAG, "Discarded onError(" + error + ") callback"); 488 } 489 } 490 findFirstFreeSlot(NetworkAgentInfo nai)491 private int findFirstFreeSlot(NetworkAgentInfo nai) { 492 HashMap networkKeepalives = mKeepalives.get(nai); 493 if (networkKeepalives == null) { 494 networkKeepalives = new HashMap<Integer, KeepaliveInfo>(); 495 mKeepalives.put(nai, networkKeepalives); 496 } 497 498 // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two 499 // separate chipset implementations independently came up with. 500 int slot; 501 for (slot = 1; slot <= networkKeepalives.size(); slot++) { 502 if (networkKeepalives.get(slot) == null) { 503 return slot; 504 } 505 } 506 return slot; 507 } 508 509 /** 510 * Handle start keepalives with the message. 511 * 512 * @param ki the keepalive to start. 513 * @return SUCCESS if the keepalive is successfully starting and the error reason otherwise. 514 */ handleStartKeepalive(KeepaliveInfo ki)515 public int handleStartKeepalive(KeepaliveInfo ki) { 516 NetworkAgentInfo nai = ki.getNai(); 517 // If this was a paused keepalive, then reuse the same slot that was kept for it. Otherwise, 518 // use the first free slot for this network agent. 519 final int slot = NO_KEEPALIVE != ki.mSlot ? ki.mSlot : findFirstFreeSlot(nai); 520 mKeepalives.get(nai).put(slot, ki); 521 return ki.start(slot); 522 } 523 handleStopAllKeepalives(NetworkAgentInfo nai, int reason)524 public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) { 525 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 526 if (networkKeepalives != null) { 527 final ArrayList<KeepaliveInfo> kalist = new ArrayList(networkKeepalives.values()); 528 for (KeepaliveInfo ki : kalist) { 529 // Check if keepalive is already stopped 530 if (ki.mStopReason == SUCCESS_PAUSED) continue; 531 ki.stop(reason); 532 // Clean up keepalives since the network agent is disconnected and unable to pass 533 // back asynchronous result of stop(). 534 cleanupStoppedKeepalive(nai, ki.mSlot); 535 } 536 } 537 } 538 handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason)539 public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) { 540 final String networkName = NetworkAgentInfo.toShortString(nai); 541 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 542 if (networkKeepalives == null) { 543 Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName); 544 return; 545 } 546 KeepaliveInfo ki = networkKeepalives.get(slot); 547 if (ki == null) { 548 Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName); 549 return; 550 } 551 ki.stop(reason); 552 // Clean up keepalives will be done as a result of calling ki.stop() after the slots are 553 // freed. 554 } 555 cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot)556 private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) { 557 final String networkName = NetworkAgentInfo.toShortString(nai); 558 HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 559 if (networkKeepalives == null) { 560 Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName); 561 return; 562 } 563 KeepaliveInfo ki = networkKeepalives.get(slot); 564 if (ki == null) { 565 Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName); 566 return; 567 } 568 569 // If the keepalive was stopped for good, remove it from the hash table so the slot can 570 // be considered available when reusing it. If it was only a pause, let it sit in the map 571 // so it sits on the slot. 572 final int reason = ki.mStopReason; 573 if (reason != SUCCESS_PAUSED) { 574 networkKeepalives.remove(slot); 575 Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", " 576 + networkKeepalives.size() + " remains."); 577 } else { 578 Log.d(TAG, "Pause keepalive " + slot + " on " + networkName + ", keep slot reserved"); 579 } 580 if (networkKeepalives.isEmpty()) { 581 mKeepalives.remove(nai); 582 } 583 584 // Notify app that the keepalive is stopped. 585 if (reason == SUCCESS) { 586 try { 587 ki.mCallback.onStopped(); 588 } catch (RemoteException e) { 589 Log.w(TAG, "Discarded onStop callback: " + reason); 590 } 591 } else if (reason == SUCCESS_PAUSED) { 592 try { 593 ki.mCallback.onPaused(); 594 } catch (RemoteException e) { 595 Log.w(TAG, "Discarded onPaused callback: " + reason); 596 } 597 } else if (reason == DATA_RECEIVED) { 598 try { 599 ki.mCallback.onDataReceived(); 600 } catch (RemoteException e) { 601 Log.w(TAG, "Discarded onDataReceived callback: " + reason); 602 } 603 } else if (reason == ERROR_STOP_REASON_UNINITIALIZED) { 604 throw new IllegalStateException("Unexpected stop reason: " + reason); 605 } else if (reason == ERROR_NO_SUCH_SLOT) { 606 // There are multiple independent reasons a keepalive can stop. Some 607 // are software (e.g. the app stops the keepalive) and some are hardware 608 // (e.g. the SIM card gets removed). Therefore, there is a very low 609 // probability that both of these happen at the same time, which would 610 // result in the first stop attempt returning SUCCESS and the second 611 // stop attempt returning NO_SUCH_SLOT. Such a race condition can be 612 // ignored with a log. 613 // This should still be reported because if it happens with any frequency 614 // it probably means there is a bug where the system server is trying 615 // to use a non-existing hardware slot. 616 // TODO : separate the non-existing hardware slot from the case where 617 // there is no keepalive running on this slot. 618 Log.wtf(TAG, "Keepalive on slot " + slot + " can't be stopped : " + reason); 619 notifyErrorCallback(ki.mCallback, reason); 620 } else { 621 notifyErrorCallback(ki.mCallback, reason); 622 } 623 624 ki.unlinkDeathRecipient(); 625 } 626 627 /** 628 * Finalize a paused keepalive. 629 * 630 * This will send the appropriate callback after checking that this keepalive is indeed paused, 631 * and free the slot. 632 * 633 * @param ki the keepalive to finalize 634 * @param reason the reason the keepalive is stopped 635 */ finalizePausedKeepalive(@onNull final KeepaliveInfo ki, int reason)636 public void finalizePausedKeepalive(@NonNull final KeepaliveInfo ki, int reason) { 637 if (SUCCESS_PAUSED != ki.mStopReason) { 638 throw new IllegalStateException("Keepalive is not paused"); 639 } 640 if (reason == SUCCESS) { 641 try { 642 ki.mCallback.onStopped(); 643 } catch (RemoteException e) { 644 Log.w(TAG, "Discarded onStopped callback while finalizing paused keepalive"); 645 } 646 } else { 647 notifyErrorCallback(ki.mCallback, reason); 648 } 649 650 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(ki.mNai); 651 if (networkKeepalives == null) { 652 Log.e(TAG, "Attempt to finalize keepalive on nonexistent network " + ki.mNai); 653 return; 654 } 655 networkKeepalives.remove(ki.mSlot); 656 } 657 658 /** 659 * Handle keepalive events from lower layer. 660 * 661 * @return false if the event caused handleStopKeepalive to be called, i.e. the keepalive is 662 * forced to stop. Otherwise, return true. 663 */ handleEventSocketKeepalive(@onNull NetworkAgentInfo nai, int slot, int reason)664 public boolean handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai, int slot, int reason) { 665 KeepaliveInfo ki = null; 666 try { 667 ki = mKeepalives.get(nai).get(slot); 668 } catch(NullPointerException e) {} 669 if (ki == null) { 670 Log.e(TAG, "Event " + NetworkAgent.EVENT_SOCKET_KEEPALIVE + "," + slot + "," + reason 671 + " for unknown keepalive " + slot + " on " + nai.toShortString()); 672 return true; 673 } 674 675 // This can be called in a number of situations : 676 // - startedState is STARTING. 677 // - reason is SUCCESS => go to STARTED. 678 // - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive. 679 // - startedState is STARTED. 680 // - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive. 681 // - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive. 682 // The control is not supposed to ever come here if the state is NOT_STARTED. This is 683 // because in NOT_STARTED state, the code will switch to STARTING before sending messages 684 // to start, and the only way to NOT_STARTED is this function, through the edges outlined 685 // above : in all cases, keepalive gets stopped and can't restart without going into 686 // STARTING as messages are ordered. This also depends on the hardware processing the 687 // messages in order. 688 // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an 689 // option. 690 if (KeepaliveInfo.STARTING == ki.mStartedState) { 691 if (SUCCESS == reason) { 692 // Keepalive successfully started. 693 Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString()); 694 ki.mStartedState = KeepaliveInfo.STARTED; 695 try { 696 if (ki.mResumed) { 697 ki.mCallback.onResumed(); 698 } else { 699 ki.mCallback.onStarted(); 700 } 701 } catch (RemoteException e) { 702 Log.w(TAG, "Discarded " + (ki.mResumed ? "onResumed" : "onStarted") 703 + " callback for slot " + slot); 704 } 705 return true; 706 } else { 707 Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString() 708 + ": " + reason); 709 // The message indicated some error trying to start: do call handleStopKeepalive. 710 handleStopKeepalive(nai, slot, reason); 711 return false; 712 } 713 } else if (KeepaliveInfo.STOPPING == ki.mStartedState) { 714 // The message indicated result of stopping : clean up keepalive slots. 715 Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString() 716 + " stopped: " + reason); 717 ki.mStartedState = KeepaliveInfo.NOT_STARTED; 718 cleanupStoppedKeepalive(nai, slot); 719 return true; 720 } else { 721 Log.wtf(TAG, "Event " + NetworkAgent.EVENT_SOCKET_KEEPALIVE + "," + slot + "," + reason 722 + " for keepalive in wrong state: " + ki.toString()); 723 // Although this is an unexpected event, the keepalive is not stopped here. 724 return true; 725 } 726 } 727 728 /** 729 * Called when requesting that keepalives be started on a IPsec NAT-T socket. See 730 * {@link android.net.SocketKeepalive}. 731 **/ 732 @Nullable makeNattKeepaliveInfo(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, int srcPort, @NonNull String dstAddrString, int dstPort)733 public KeepaliveInfo makeNattKeepaliveInfo(@Nullable NetworkAgentInfo nai, 734 @Nullable FileDescriptor fd, 735 int intervalSeconds, 736 @NonNull ISocketKeepaliveCallback cb, 737 @NonNull String srcAddrString, 738 int srcPort, 739 @NonNull String dstAddrString, 740 int dstPort) { 741 if (nai == null) { 742 notifyErrorCallback(cb, ERROR_INVALID_NETWORK); 743 return null; 744 } 745 746 InetAddress srcAddress, dstAddress; 747 try { 748 srcAddress = InetAddresses.parseNumericAddress(srcAddrString); 749 dstAddress = InetAddresses.parseNumericAddress(dstAddrString); 750 } catch (IllegalArgumentException e) { 751 notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS); 752 return null; 753 } 754 755 KeepalivePacketData packet; 756 try { 757 packet = NattKeepalivePacketData.nattKeepalivePacket( 758 srcAddress, srcPort, dstAddress, NATT_PORT); 759 } catch (InvalidPacketException e) { 760 notifyErrorCallback(cb, e.getError()); 761 return null; 762 } 763 KeepaliveInfo ki = null; 764 try { 765 ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds, 766 KeepaliveInfo.TYPE_NATT, fd); 767 } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) { 768 Log.e(TAG, "Fail to construct keepalive", e); 769 notifyErrorCallback(cb, ERROR_INVALID_SOCKET); 770 return null; 771 } 772 Log.d(TAG, "Created keepalive: " + ki); 773 return ki; 774 } 775 776 /** 777 * Make a KeepaliveInfo for a TCP socket. 778 * 779 * In order to offload keepalive for application correctly, sequence number, ack number and 780 * other fields are needed to form the keepalive packet. Thus, this function synchronously 781 * puts the socket into repair mode to get the necessary information. After the socket has been 782 * put into repair mode, the application cannot access the socket until reverted to normal. 783 * 784 * See {@link android.net.SocketKeepalive}. 785 **/ 786 @Nullable makeTcpKeepaliveInfo(@ullable NetworkAgentInfo nai, @NonNull FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb)787 public KeepaliveInfo makeTcpKeepaliveInfo(@Nullable NetworkAgentInfo nai, 788 @NonNull FileDescriptor fd, 789 int intervalSeconds, 790 @NonNull ISocketKeepaliveCallback cb) { 791 if (nai == null) { 792 notifyErrorCallback(cb, ERROR_INVALID_NETWORK); 793 return null; 794 } 795 796 final TcpKeepalivePacketData packet; 797 try { 798 packet = TcpKeepaliveController.getTcpKeepalivePacket(fd); 799 } catch (InvalidSocketException e) { 800 notifyErrorCallback(cb, e.error); 801 return null; 802 } catch (InvalidPacketException e) { 803 notifyErrorCallback(cb, e.getError()); 804 return null; 805 } 806 KeepaliveInfo ki = null; 807 try { 808 ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds, 809 KeepaliveInfo.TYPE_TCP, fd); 810 } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) { 811 Log.e(TAG, "Fail to construct keepalive e=" + e); 812 notifyErrorCallback(cb, ERROR_INVALID_SOCKET); 813 return null; 814 } 815 Log.d(TAG, "Created keepalive: " + ki.toString()); 816 return ki; 817 } 818 819 /** 820 * Make a KeepaliveInfo for an IPSec NAT-T socket. 821 * 822 * This function is identical to {@link #makeNattKeepaliveInfo}, but also takes a 823 * {@code resourceId}, which is the resource index bound to the {@link UdpEncapsulationSocket} 824 * when creating by {@link com.android.server.IpSecService} to verify whether the given 825 * {@link UdpEncapsulationSocket} is legitimate. 826 **/ 827 @Nullable makeNattKeepaliveInfo(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int resourceId, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, @NonNull String dstAddrString, int dstPort)828 public KeepaliveInfo makeNattKeepaliveInfo(@Nullable NetworkAgentInfo nai, 829 @Nullable FileDescriptor fd, 830 int resourceId, 831 int intervalSeconds, 832 @NonNull ISocketKeepaliveCallback cb, 833 @NonNull String srcAddrString, 834 @NonNull String dstAddrString, 835 int dstPort) { 836 // Ensure that the socket is created by IpSecService. 837 if (!isNattKeepaliveSocketValid(fd, resourceId)) { 838 notifyErrorCallback(cb, ERROR_INVALID_SOCKET); 839 return null; 840 } 841 842 // Get src port to adopt old API. 843 int srcPort = 0; 844 try { 845 final SocketAddress srcSockAddr = Os.getsockname(fd); 846 srcPort = ((InetSocketAddress) srcSockAddr).getPort(); 847 } catch (ErrnoException e) { 848 notifyErrorCallback(cb, ERROR_INVALID_SOCKET); 849 return null; 850 } 851 852 // Forward request to old API. 853 return makeNattKeepaliveInfo(nai, fd, intervalSeconds, cb, srcAddrString, srcPort, 854 dstAddrString, dstPort); 855 } 856 857 /** 858 * Verify if the IPsec NAT-T file descriptor and resource Id hold for IPsec keepalive is valid. 859 **/ isNattKeepaliveSocketValid(@ullable FileDescriptor fd, int resourceId)860 public static boolean isNattKeepaliveSocketValid(@Nullable FileDescriptor fd, int resourceId) { 861 // TODO: 1. confirm whether the fd is called from system api or created by IpSecService. 862 // 2. If the fd is created from the system api, check that it's bounded. And 863 // call dup to keep the fd open. 864 // 3. If the fd is created from IpSecService, check if the resource ID is valid. And 865 // hold the resource needed in IpSecService. 866 if (null == fd) { 867 return false; 868 } 869 return true; 870 } 871 872 /** 873 * Dump KeepaliveTracker state. 874 */ dump(IndentingPrintWriter pw)875 public void dump(IndentingPrintWriter pw) { 876 pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives)); 877 pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots); 878 pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid); 879 pw.println("Socket keepalives:"); 880 pw.increaseIndent(); 881 for (NetworkAgentInfo nai : mKeepalives.keySet()) { 882 pw.println(nai.toShortString()); 883 pw.increaseIndent(); 884 for (int slot : mKeepalives.get(nai).keySet()) { 885 KeepaliveInfo ki = mKeepalives.get(nai).get(slot); 886 pw.println(slot + ": " + ki.toString()); 887 } 888 pw.decreaseIndent(); 889 } 890 pw.decreaseIndent(); 891 } 892 } 893