Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 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 | |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 19 | import static android.content.pm.PackageManager.PERMISSION_GRANTED; |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 20 | import static android.net.NattSocketKeepalive.NATT_PORT; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 21 | import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER; |
| 22 | import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER; |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 23 | import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE; |
| 24 | import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE; |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 25 | import static android.net.SocketKeepalive.BINDER_DIED; |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 26 | import static android.net.SocketKeepalive.DATA_RECEIVED; |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 27 | import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES; |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 28 | import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL; |
| 29 | import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; |
| 30 | import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 31 | import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 32 | import static android.net.SocketKeepalive.ERROR_UNSUPPORTED; |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 33 | import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; |
| 34 | import static android.net.SocketKeepalive.MIN_INTERVAL_SEC; |
| 35 | import static android.net.SocketKeepalive.NO_KEEPALIVE; |
| 36 | import static android.net.SocketKeepalive.SUCCESS; |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 37 | |
| 38 | import android.annotation.NonNull; |
| 39 | import android.annotation.Nullable; |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 40 | import android.content.Context; |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 41 | import android.net.ISocketKeepaliveCallback; |
Aaron Huang | 17c660d | 2019-10-02 01:39:46 +0800 | [diff] [blame^] | 42 | import android.net.InvalidPacketException; |
Nathan Harold | 26de1d3 | 2017-11-02 21:01:46 -0700 | [diff] [blame] | 43 | import android.net.KeepalivePacketData; |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 44 | import android.net.NattKeepalivePacketData; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 45 | import android.net.NetworkAgent; |
| 46 | import android.net.NetworkUtils; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 47 | import android.net.SocketKeepalive.InvalidSocketException; |
| 48 | import android.net.TcpKeepalivePacketData; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 49 | import android.net.util.IpUtils; |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 50 | import android.net.util.KeepaliveUtils; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 51 | import android.os.Binder; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 52 | import android.os.Handler; |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 53 | import android.os.IBinder; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 54 | import android.os.Message; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 55 | import android.os.Process; |
| 56 | import android.os.RemoteException; |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 57 | import android.system.ErrnoException; |
| 58 | import android.system.Os; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 59 | import android.util.Log; |
| 60 | import android.util.Pair; |
| 61 | |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 62 | import com.android.internal.R; |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 63 | import com.android.internal.util.HexDump; |
| 64 | import com.android.internal.util.IndentingPrintWriter; |
| 65 | |
| 66 | import java.io.FileDescriptor; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 67 | import java.net.InetAddress; |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 68 | import java.net.InetSocketAddress; |
| 69 | import java.net.SocketAddress; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 70 | import java.util.ArrayList; |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 71 | import java.util.Arrays; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 72 | import java.util.HashMap; |
| 73 | |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 74 | /** |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 75 | * Manages socket keepalive requests. |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 76 | * |
| 77 | * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all |
| 78 | * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 79 | * handle* methods must be called only from the ConnectivityService handler thread. |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 80 | */ |
| 81 | public class KeepaliveTracker { |
| 82 | |
| 83 | private static final String TAG = "KeepaliveTracker"; |
Joe Onorato | 12acbd7 | 2016-02-01 17:49:31 -0800 | [diff] [blame] | 84 | private static final boolean DBG = false; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 85 | |
Lorenzo Colitti | b988466 | 2015-09-08 22:02:37 +0900 | [diff] [blame] | 86 | public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 87 | |
| 88 | /** Keeps track of keepalive requests. */ |
| 89 | private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives = |
| 90 | new HashMap<> (); |
| 91 | private final Handler mConnectivityServiceHandler; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 92 | @NonNull |
| 93 | private final TcpKeepaliveController mTcpController; |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 94 | @NonNull |
| 95 | private final Context mContext; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 96 | |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 97 | // Supported keepalive count for each transport type, can be configured through |
| 98 | // config_networkSupportedKeepaliveCount. For better error handling, use |
| 99 | // {@link getSupportedKeepalivesForNetworkCapabilities} instead of direct access. |
| 100 | @NonNull |
| 101 | private final int[] mSupportedKeepalives; |
| 102 | |
| 103 | // Reserved privileged keepalive slots per transport. Caller's permission will be enforced if |
| 104 | // the number of remaining keepalive slots is less than or equal to the threshold. |
| 105 | private final int mReservedPrivilegedSlots; |
| 106 | |
junyulai | e5c3c8c | 2019-04-30 14:42:05 +0800 | [diff] [blame] | 107 | // Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if |
| 108 | // the number of remaining keepalive slots is less than or equal to the threshold. |
| 109 | private final int mAllowedUnprivilegedSlotsForUid; |
| 110 | |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 111 | public KeepaliveTracker(Context context, Handler handler) { |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 112 | mConnectivityServiceHandler = handler; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 113 | mTcpController = new TcpKeepaliveController(handler); |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 114 | mContext = context; |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 115 | mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext); |
| 116 | mReservedPrivilegedSlots = mContext.getResources().getInteger( |
| 117 | R.integer.config_reservedPrivilegedKeepaliveSlots); |
junyulai | e5c3c8c | 2019-04-30 14:42:05 +0800 | [diff] [blame] | 118 | mAllowedUnprivilegedSlotsForUid = mContext.getResources().getInteger( |
| 119 | R.integer.config_allowedUnprivilegedKeepalivePerUid); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | /** |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 123 | * Tracks information about a socket keepalive. |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 124 | * |
| 125 | * All information about this keepalive is known at construction time except the slot number, |
| 126 | * which is only returned when the hardware has successfully started the keepalive. |
| 127 | */ |
| 128 | class KeepaliveInfo implements IBinder.DeathRecipient { |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 129 | // Bookkeeping data. |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 130 | private final ISocketKeepaliveCallback mCallback; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 131 | private final int mUid; |
| 132 | private final int mPid; |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 133 | private final boolean mPrivileged; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 134 | private final NetworkAgentInfo mNai; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 135 | private final int mType; |
| 136 | private final FileDescriptor mFd; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 137 | |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 138 | public static final int TYPE_NATT = 1; |
| 139 | public static final int TYPE_TCP = 2; |
| 140 | |
| 141 | // Keepalive slot. A small integer that identifies this keepalive among the ones handled |
| 142 | // by this network. |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 143 | private int mSlot = NO_KEEPALIVE; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 144 | |
| 145 | // Packet data. |
| 146 | private final KeepalivePacketData mPacket; |
| 147 | private final int mInterval; |
| 148 | |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 149 | // Whether the keepalive is started or not. The initial state is NOT_STARTED. |
| 150 | private static final int NOT_STARTED = 1; |
| 151 | private static final int STARTING = 2; |
| 152 | private static final int STARTED = 3; |
junyulai | 7b305ce | 2019-04-01 11:33:49 +0800 | [diff] [blame] | 153 | private static final int STOPPING = 4; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 154 | private int mStartedState = NOT_STARTED; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 155 | |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 156 | KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback, |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 157 | @NonNull NetworkAgentInfo nai, |
| 158 | @NonNull KeepalivePacketData packet, |
| 159 | int interval, |
| 160 | int type, |
Junyu Lai | 17a45f4 | 2019-04-26 01:22:29 +0000 | [diff] [blame] | 161 | @Nullable FileDescriptor fd) throws InvalidSocketException { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 162 | mCallback = callback; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 163 | mPid = Binder.getCallingPid(); |
| 164 | mUid = Binder.getCallingUid(); |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 165 | mPrivileged = (PERMISSION_GRANTED == mContext.checkPermission(PERMISSION, mPid, mUid)); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 166 | |
| 167 | mNai = nai; |
| 168 | mPacket = packet; |
| 169 | mInterval = interval; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 170 | mType = type; |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 171 | |
| 172 | // For SocketKeepalive, a dup of fd is kept in mFd so the source port from which the |
| 173 | // keepalives are sent cannot be reused by another app even if the fd gets closed by |
| 174 | // the user. A null is acceptable here for backward compatibility of PacketKeepalive |
| 175 | // API. |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 176 | try { |
| 177 | if (fd != null) { |
| 178 | mFd = Os.dup(fd); |
Junyu Lai | 17a45f4 | 2019-04-26 01:22:29 +0000 | [diff] [blame] | 179 | } else { |
junyulai | 1517815 | 2019-03-27 11:00:37 +0800 | [diff] [blame] | 180 | Log.d(TAG, toString() + " calls with null fd"); |
| 181 | if (!mPrivileged) { |
| 182 | throw new SecurityException( |
| 183 | "null fd is not allowed for unprivileged access."); |
| 184 | } |
| 185 | if (mType == TYPE_TCP) { |
| 186 | throw new IllegalArgumentException( |
| 187 | "null fd is not allowed for tcp socket keepalives."); |
| 188 | } |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 189 | mFd = null; |
| 190 | } |
| 191 | } catch (ErrnoException e) { |
| 192 | Log.e(TAG, "Cannot dup fd: ", e); |
| 193 | throw new InvalidSocketException(ERROR_INVALID_SOCKET, e); |
| 194 | } |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 195 | |
| 196 | try { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 197 | mCallback.asBinder().linkToDeath(this, 0); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 198 | } catch (RemoteException e) { |
| 199 | binderDied(); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | public NetworkAgentInfo getNai() { |
| 204 | return mNai; |
| 205 | } |
| 206 | |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 207 | private String startedStateString(final int state) { |
| 208 | switch (state) { |
| 209 | case NOT_STARTED : return "NOT_STARTED"; |
| 210 | case STARTING : return "STARTING"; |
| 211 | case STARTED : return "STARTED"; |
junyulai | 0535295 | 2019-04-09 14:37:35 +0800 | [diff] [blame] | 212 | case STOPPING : return "STOPPING"; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 213 | } |
| 214 | throw new IllegalArgumentException("Unknown state"); |
| 215 | } |
| 216 | |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 217 | public String toString() { |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 218 | return "KeepaliveInfo [" |
junyulai | 02abbfd | 2019-06-03 17:51:41 +0800 | [diff] [blame] | 219 | + " type=" + mType |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 220 | + " network=" + mNai.network |
| 221 | + " startedState=" + startedStateString(mStartedState) |
| 222 | + " " |
| 223 | + IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort) |
| 224 | + "->" |
| 225 | + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort) |
| 226 | + " interval=" + mInterval |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 227 | + " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 228 | + " packetData=" + HexDump.toHexString(mPacket.getPacket()) |
| 229 | + " ]"; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 230 | } |
| 231 | |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 232 | /** Called when the application process is killed. */ |
| 233 | public void binderDied() { |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 234 | stop(BINDER_DIED); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 235 | } |
| 236 | |
| 237 | void unlinkDeathRecipient() { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 238 | if (mCallback != null) { |
| 239 | mCallback.asBinder().unlinkToDeath(this, 0); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 240 | } |
| 241 | } |
| 242 | |
| 243 | private int checkNetworkConnected() { |
| 244 | if (!mNai.networkInfo.isConnectedOrConnecting()) { |
| 245 | return ERROR_INVALID_NETWORK; |
| 246 | } |
| 247 | return SUCCESS; |
| 248 | } |
| 249 | |
| 250 | private int checkSourceAddress() { |
| 251 | // Check that we have the source address. |
| 252 | for (InetAddress address : mNai.linkProperties.getAddresses()) { |
| 253 | if (address.equals(mPacket.srcAddress)) { |
| 254 | return SUCCESS; |
| 255 | } |
| 256 | } |
| 257 | return ERROR_INVALID_IP_ADDRESS; |
| 258 | } |
| 259 | |
| 260 | private int checkInterval() { |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 261 | if (mInterval < MIN_INTERVAL_SEC || mInterval > MAX_INTERVAL_SEC) { |
| 262 | return ERROR_INVALID_INTERVAL; |
| 263 | } |
| 264 | return SUCCESS; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 265 | } |
| 266 | |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 267 | private int checkPermission() { |
| 268 | final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 269 | if (networkKeepalives == null) { |
| 270 | return ERROR_INVALID_NETWORK; |
| 271 | } |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 272 | |
| 273 | if (mPrivileged) return SUCCESS; |
| 274 | |
| 275 | final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( |
| 276 | mSupportedKeepalives, mNai.networkCapabilities); |
| 277 | |
| 278 | int takenUnprivilegedSlots = 0; |
| 279 | for (final KeepaliveInfo ki : networkKeepalives.values()) { |
| 280 | if (!ki.mPrivileged) ++takenUnprivilegedSlots; |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 281 | } |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 282 | if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) { |
| 283 | return ERROR_INSUFFICIENT_RESOURCES; |
| 284 | } |
| 285 | |
junyulai | e5c3c8c | 2019-04-30 14:42:05 +0800 | [diff] [blame] | 286 | // Count unprivileged keepalives for the same uid across networks. |
| 287 | int unprivilegedCountSameUid = 0; |
| 288 | for (final HashMap<Integer, KeepaliveInfo> kaForNetwork : mKeepalives.values()) { |
| 289 | for (final KeepaliveInfo ki : kaForNetwork.values()) { |
| 290 | if (ki.mUid == mUid) { |
| 291 | unprivilegedCountSameUid++; |
| 292 | } |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 293 | } |
| 294 | } |
junyulai | e5c3c8c | 2019-04-30 14:42:05 +0800 | [diff] [blame] | 295 | if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) { |
| 296 | return ERROR_INSUFFICIENT_RESOURCES; |
| 297 | } |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 298 | return SUCCESS; |
| 299 | } |
| 300 | |
| 301 | private int checkLimit() { |
| 302 | final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); |
| 303 | if (networkKeepalives == null) { |
| 304 | return ERROR_INVALID_NETWORK; |
| 305 | } |
| 306 | final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( |
| 307 | mSupportedKeepalives, mNai.networkCapabilities); |
| 308 | if (supported == 0) return ERROR_UNSUPPORTED; |
| 309 | if (networkKeepalives.size() > supported) return ERROR_INSUFFICIENT_RESOURCES; |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 310 | return SUCCESS; |
| 311 | } |
| 312 | |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 313 | private int isValid() { |
| 314 | synchronized (mNai) { |
| 315 | int error = checkInterval(); |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 316 | if (error == SUCCESS) error = checkLimit(); |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 317 | if (error == SUCCESS) error = checkPermission(); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 318 | if (error == SUCCESS) error = checkNetworkConnected(); |
| 319 | if (error == SUCCESS) error = checkSourceAddress(); |
| 320 | return error; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | void start(int slot) { |
junyulai | 5e7128d | 2019-01-17 15:15:45 +0800 | [diff] [blame] | 325 | mSlot = slot; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 326 | int error = isValid(); |
| 327 | if (error == SUCCESS) { |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 328 | Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name()); |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 329 | switch (mType) { |
| 330 | case TYPE_NATT: |
Aaron Huang | 706ff1b | 2019-05-02 21:14:20 +0800 | [diff] [blame] | 331 | mNai.asyncChannel.sendMessage( |
| 332 | CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket); |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 333 | mNai.asyncChannel |
| 334 | .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); |
| 335 | break; |
| 336 | case TYPE_TCP: |
junyulai | 1d5cd19 | 2019-03-04 11:49:17 +0800 | [diff] [blame] | 337 | try { |
| 338 | mTcpController.startSocketMonitor(mFd, this, mSlot); |
| 339 | } catch (InvalidSocketException e) { |
| 340 | handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET); |
| 341 | return; |
| 342 | } |
Aaron Huang | 706ff1b | 2019-05-02 21:14:20 +0800 | [diff] [blame] | 343 | mNai.asyncChannel.sendMessage( |
| 344 | CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket); |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 345 | // TODO: check result from apf and notify of failure as needed. |
| 346 | mNai.asyncChannel |
| 347 | .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); |
| 348 | break; |
| 349 | default: |
| 350 | Log.wtf(TAG, "Starting keepalive with unknown type: " + mType); |
| 351 | handleStopKeepalive(mNai, mSlot, error); |
| 352 | return; |
| 353 | } |
| 354 | mStartedState = STARTING; |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 355 | } else { |
junyulai | 5e7128d | 2019-01-17 15:15:45 +0800 | [diff] [blame] | 356 | handleStopKeepalive(mNai, mSlot, error); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 357 | return; |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | void stop(int reason) { |
| 362 | int uid = Binder.getCallingUid(); |
| 363 | if (uid != mUid && uid != Process.SYSTEM_UID) { |
| 364 | if (DBG) { |
| 365 | Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network); |
| 366 | } |
| 367 | } |
junyulai | 0535295 | 2019-04-09 14:37:35 +0800 | [diff] [blame] | 368 | Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name() + ": " + reason); |
| 369 | switch (mStartedState) { |
| 370 | case NOT_STARTED: |
| 371 | // Remove the reference of the keepalive that meet error before starting, |
| 372 | // e.g. invalid parameter. |
| 373 | cleanupStoppedKeepalive(mNai, mSlot); |
| 374 | break; |
| 375 | case STOPPING: |
| 376 | // Keepalive is already in stopping state, ignore. |
| 377 | return; |
| 378 | default: |
| 379 | mStartedState = STOPPING; |
Aaron Huang | 706ff1b | 2019-05-02 21:14:20 +0800 | [diff] [blame] | 380 | switch (mType) { |
| 381 | case TYPE_TCP: |
| 382 | mTcpController.stopSocketMonitor(mSlot); |
| 383 | // fall through |
| 384 | case TYPE_NATT: |
| 385 | mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot); |
| 386 | mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, |
| 387 | mSlot); |
| 388 | break; |
| 389 | default: |
| 390 | Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType); |
junyulai | 0535295 | 2019-04-09 14:37:35 +0800 | [diff] [blame] | 391 | } |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 392 | } |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 393 | |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 394 | // Close the duplicated fd that maintains the lifecycle of socket whenever |
| 395 | // keepalive is running. |
| 396 | if (mFd != null) { |
| 397 | try { |
| 398 | Os.close(mFd); |
| 399 | } catch (ErrnoException e) { |
| 400 | // This should not happen since system server controls the lifecycle of fd when |
| 401 | // keepalive offload is running. |
| 402 | Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e); |
| 403 | } |
| 404 | } |
| 405 | |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 406 | if (reason == SUCCESS) { |
| 407 | try { |
| 408 | mCallback.onStopped(); |
| 409 | } catch (RemoteException e) { |
| 410 | Log.w(TAG, "Discarded onStop callback: " + reason); |
| 411 | } |
| 412 | } else if (reason == DATA_RECEIVED) { |
| 413 | try { |
| 414 | mCallback.onDataReceived(); |
| 415 | } catch (RemoteException e) { |
| 416 | Log.w(TAG, "Discarded onDataReceived callback: " + reason); |
| 417 | } |
| 418 | } else { |
| 419 | notifyErrorCallback(mCallback, reason); |
| 420 | } |
| 421 | |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 422 | unlinkDeathRecipient(); |
| 423 | } |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 424 | |
| 425 | void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) { |
| 426 | handleStopKeepalive(mNai, mSlot, socketKeepaliveReason); |
| 427 | } |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 428 | } |
| 429 | |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 430 | void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) { |
| 431 | if (DBG) Log.w(TAG, "Sending onError(" + error + ") callback"); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 432 | try { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 433 | cb.onError(error); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 434 | } catch (RemoteException e) { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 435 | Log.w(TAG, "Discarded onError(" + error + ") callback"); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 436 | } |
| 437 | } |
| 438 | |
| 439 | private int findFirstFreeSlot(NetworkAgentInfo nai) { |
| 440 | HashMap networkKeepalives = mKeepalives.get(nai); |
| 441 | if (networkKeepalives == null) { |
| 442 | networkKeepalives = new HashMap<Integer, KeepaliveInfo>(); |
| 443 | mKeepalives.put(nai, networkKeepalives); |
| 444 | } |
| 445 | |
Lorenzo Colitti | 9acca09 | 2015-09-08 13:44:23 +0900 | [diff] [blame] | 446 | // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two |
| 447 | // separate chipset implementations independently came up with. |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 448 | int slot; |
Lorenzo Colitti | 9acca09 | 2015-09-08 13:44:23 +0900 | [diff] [blame] | 449 | for (slot = 1; slot <= networkKeepalives.size(); slot++) { |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 450 | if (networkKeepalives.get(slot) == null) { |
| 451 | return slot; |
| 452 | } |
| 453 | } |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 454 | return slot; |
| 455 | } |
| 456 | |
| 457 | public void handleStartKeepalive(Message message) { |
| 458 | KeepaliveInfo ki = (KeepaliveInfo) message.obj; |
| 459 | NetworkAgentInfo nai = ki.getNai(); |
| 460 | int slot = findFirstFreeSlot(nai); |
| 461 | mKeepalives.get(nai).put(slot, ki); |
| 462 | ki.start(slot); |
| 463 | } |
| 464 | |
| 465 | public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) { |
junyulai | cf43dbd | 2019-05-13 14:19:00 +0800 | [diff] [blame] | 466 | final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 467 | if (networkKeepalives != null) { |
junyulai | cf43dbd | 2019-05-13 14:19:00 +0800 | [diff] [blame] | 468 | final ArrayList<KeepaliveInfo> kalist = new ArrayList(networkKeepalives.values()); |
| 469 | for (KeepaliveInfo ki : kalist) { |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 470 | ki.stop(reason); |
junyulai | 5d7bb8b | 2019-04-25 11:24:04 +0800 | [diff] [blame] | 471 | // Clean up keepalives since the network agent is disconnected and unable to pass |
| 472 | // back asynchronous result of stop(). |
| 473 | cleanupStoppedKeepalive(nai, ki.mSlot); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 474 | } |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 475 | } |
| 476 | } |
| 477 | |
| 478 | public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) { |
Lorenzo Colitti | 9acca09 | 2015-09-08 13:44:23 +0900 | [diff] [blame] | 479 | String networkName = (nai == null) ? "(null)" : nai.name(); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 480 | HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); |
| 481 | if (networkKeepalives == null) { |
Lorenzo Colitti | 9acca09 | 2015-09-08 13:44:23 +0900 | [diff] [blame] | 482 | Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 483 | return; |
| 484 | } |
| 485 | KeepaliveInfo ki = networkKeepalives.get(slot); |
| 486 | if (ki == null) { |
Lorenzo Colitti | 9acca09 | 2015-09-08 13:44:23 +0900 | [diff] [blame] | 487 | Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 488 | return; |
| 489 | } |
| 490 | ki.stop(reason); |
junyulai | 0535295 | 2019-04-09 14:37:35 +0800 | [diff] [blame] | 491 | // Clean up keepalives will be done as a result of calling ki.stop() after the slots are |
| 492 | // freed. |
| 493 | } |
| 494 | |
| 495 | private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) { |
| 496 | String networkName = (nai == null) ? "(null)" : nai.name(); |
| 497 | HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); |
| 498 | if (networkKeepalives == null) { |
| 499 | Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName); |
| 500 | return; |
| 501 | } |
| 502 | KeepaliveInfo ki = networkKeepalives.get(slot); |
| 503 | if (ki == null) { |
| 504 | Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName); |
| 505 | return; |
| 506 | } |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 507 | networkKeepalives.remove(slot); |
junyulai | 0535295 | 2019-04-09 14:37:35 +0800 | [diff] [blame] | 508 | Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", " |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 509 | + networkKeepalives.size() + " remains."); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 510 | if (networkKeepalives.isEmpty()) { |
| 511 | mKeepalives.remove(nai); |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) { |
| 516 | HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); |
| 517 | if (networkKeepalives != null) { |
| 518 | ArrayList<Pair<Integer, Integer>> invalidKeepalives = new ArrayList<>(); |
| 519 | for (int slot : networkKeepalives.keySet()) { |
| 520 | int error = networkKeepalives.get(slot).isValid(); |
| 521 | if (error != SUCCESS) { |
| 522 | invalidKeepalives.add(Pair.create(slot, error)); |
| 523 | } |
| 524 | } |
| 525 | for (Pair<Integer, Integer> slotAndError: invalidKeepalives) { |
| 526 | handleStopKeepalive(nai, slotAndError.first, slotAndError.second); |
| 527 | } |
| 528 | } |
| 529 | } |
| 530 | |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 531 | /** Handle keepalive events from lower layer. */ |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 532 | public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai, |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 533 | @NonNull Message message) { |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 534 | int slot = message.arg1; |
| 535 | int reason = message.arg2; |
| 536 | |
| 537 | KeepaliveInfo ki = null; |
| 538 | try { |
| 539 | ki = mKeepalives.get(nai).get(slot); |
| 540 | } catch(NullPointerException e) {} |
| 541 | if (ki == null) { |
junyulai | 7b305ce | 2019-04-01 11:33:49 +0800 | [diff] [blame] | 542 | Log.e(TAG, "Event " + message.what + "," + slot + "," + reason |
| 543 | + " for unknown keepalive " + slot + " on " + nai.name()); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 544 | return; |
| 545 | } |
| 546 | |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 547 | // This can be called in a number of situations : |
| 548 | // - startedState is STARTING. |
| 549 | // - reason is SUCCESS => go to STARTED. |
| 550 | // - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive. |
| 551 | // - startedState is STARTED. |
| 552 | // - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive. |
| 553 | // - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive. |
| 554 | // The control is not supposed to ever come here if the state is NOT_STARTED. This is |
| 555 | // because in NOT_STARTED state, the code will switch to STARTING before sending messages |
| 556 | // to start, and the only way to NOT_STARTED is this function, through the edges outlined |
| 557 | // above : in all cases, keepalive gets stopped and can't restart without going into |
| 558 | // STARTING as messages are ordered. This also depends on the hardware processing the |
| 559 | // messages in order. |
| 560 | // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an |
| 561 | // option. |
junyulai | 7b305ce | 2019-04-01 11:33:49 +0800 | [diff] [blame] | 562 | if (KeepaliveInfo.STARTING == ki.mStartedState) { |
| 563 | if (SUCCESS == reason) { |
| 564 | // Keepalive successfully started. |
junyulai | edde28b | 2019-06-06 13:24:05 +0800 | [diff] [blame] | 565 | Log.d(TAG, "Started keepalive " + slot + " on " + nai.name()); |
junyulai | 7b305ce | 2019-04-01 11:33:49 +0800 | [diff] [blame] | 566 | ki.mStartedState = KeepaliveInfo.STARTED; |
| 567 | try { |
| 568 | ki.mCallback.onStarted(slot); |
| 569 | } catch (RemoteException e) { |
| 570 | Log.w(TAG, "Discarded onStarted(" + slot + ") callback"); |
| 571 | } |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 572 | } else { |
junyulai | 7b305ce | 2019-04-01 11:33:49 +0800 | [diff] [blame] | 573 | Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.name() |
| 574 | + ": " + reason); |
| 575 | // The message indicated some error trying to start: do call handleStopKeepalive. |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 576 | handleStopKeepalive(nai, slot, reason); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 577 | } |
junyulai | 7b305ce | 2019-04-01 11:33:49 +0800 | [diff] [blame] | 578 | } else if (KeepaliveInfo.STOPPING == ki.mStartedState) { |
junyulai | 0535295 | 2019-04-09 14:37:35 +0800 | [diff] [blame] | 579 | // The message indicated result of stopping : clean up keepalive slots. |
junyulai | 7b305ce | 2019-04-01 11:33:49 +0800 | [diff] [blame] | 580 | Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name() |
| 581 | + " stopped: " + reason); |
junyulai | 422a3c1 | 2019-03-27 10:31:11 +0800 | [diff] [blame] | 582 | ki.mStartedState = KeepaliveInfo.NOT_STARTED; |
junyulai | 0535295 | 2019-04-09 14:37:35 +0800 | [diff] [blame] | 583 | cleanupStoppedKeepalive(nai, slot); |
junyulai | 7b305ce | 2019-04-01 11:33:49 +0800 | [diff] [blame] | 584 | } else { |
| 585 | Log.wtf(TAG, "Event " + message.what + "," + slot + "," + reason |
| 586 | + " for keepalive in wrong state: " + ki.toString()); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 587 | } |
| 588 | } |
| 589 | |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 590 | /** |
| 591 | * Called when requesting that keepalives be started on a IPsec NAT-T socket. See |
| 592 | * {@link android.net.SocketKeepalive}. |
| 593 | **/ |
| 594 | public void startNattKeepalive(@Nullable NetworkAgentInfo nai, |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 595 | @Nullable FileDescriptor fd, |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 596 | int intervalSeconds, |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 597 | @NonNull ISocketKeepaliveCallback cb, |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 598 | @NonNull String srcAddrString, |
| 599 | int srcPort, |
| 600 | @NonNull String dstAddrString, |
| 601 | int dstPort) { |
Lorenzo Colitti | 9acca09 | 2015-09-08 13:44:23 +0900 | [diff] [blame] | 602 | if (nai == null) { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 603 | notifyErrorCallback(cb, ERROR_INVALID_NETWORK); |
Lorenzo Colitti | 9acca09 | 2015-09-08 13:44:23 +0900 | [diff] [blame] | 604 | return; |
| 605 | } |
| 606 | |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 607 | InetAddress srcAddress, dstAddress; |
| 608 | try { |
| 609 | srcAddress = NetworkUtils.numericToInetAddress(srcAddrString); |
| 610 | dstAddress = NetworkUtils.numericToInetAddress(dstAddrString); |
| 611 | } catch (IllegalArgumentException e) { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 612 | notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 613 | return; |
| 614 | } |
| 615 | |
| 616 | KeepalivePacketData packet; |
| 617 | try { |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 618 | packet = NattKeepalivePacketData.nattKeepalivePacket( |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 619 | srcAddress, srcPort, dstAddress, NATT_PORT); |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 620 | } catch (InvalidPacketException e) { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 621 | notifyErrorCallback(cb, e.error); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 622 | return; |
| 623 | } |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 624 | KeepaliveInfo ki = null; |
| 625 | try { |
| 626 | ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds, |
Junyu Lai | 17a45f4 | 2019-04-26 01:22:29 +0000 | [diff] [blame] | 627 | KeepaliveInfo.TYPE_NATT, fd); |
junyulai | 1517815 | 2019-03-27 11:00:37 +0800 | [diff] [blame] | 628 | } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) { |
| 629 | Log.e(TAG, "Fail to construct keepalive", e); |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 630 | notifyErrorCallback(cb, ERROR_INVALID_SOCKET); |
| 631 | return; |
| 632 | } |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 633 | Log.d(TAG, "Created keepalive: " + ki.toString()); |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 634 | mConnectivityServiceHandler.obtainMessage( |
| 635 | NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); |
| 636 | } |
| 637 | |
| 638 | /** |
| 639 | * Called by ConnectivityService to start TCP keepalive on a file descriptor. |
| 640 | * |
| 641 | * In order to offload keepalive for application correctly, sequence number, ack number and |
| 642 | * other fields are needed to form the keepalive packet. Thus, this function synchronously |
| 643 | * puts the socket into repair mode to get the necessary information. After the socket has been |
| 644 | * put into repair mode, the application cannot access the socket until reverted to normal. |
| 645 | * |
| 646 | * See {@link android.net.SocketKeepalive}. |
| 647 | **/ |
| 648 | public void startTcpKeepalive(@Nullable NetworkAgentInfo nai, |
| 649 | @NonNull FileDescriptor fd, |
| 650 | int intervalSeconds, |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 651 | @NonNull ISocketKeepaliveCallback cb) { |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 652 | if (nai == null) { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 653 | notifyErrorCallback(cb, ERROR_INVALID_NETWORK); |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 654 | return; |
| 655 | } |
| 656 | |
markchien | 656fbe9 | 2019-02-20 21:16:44 +0800 | [diff] [blame] | 657 | final TcpKeepalivePacketData packet; |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 658 | try { |
markchien | 656fbe9 | 2019-02-20 21:16:44 +0800 | [diff] [blame] | 659 | packet = TcpKeepaliveController.getTcpKeepalivePacket(fd); |
Aaron Huang | 17c660d | 2019-10-02 01:39:46 +0800 | [diff] [blame^] | 660 | } catch (InvalidSocketException e) { |
| 661 | notifyErrorCallback(cb, e.error); |
| 662 | return; |
| 663 | } catch (InvalidPacketException e) { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 664 | notifyErrorCallback(cb, e.error); |
junyulai | 352dc2f | 2019-01-08 20:04:33 +0800 | [diff] [blame] | 665 | return; |
| 666 | } |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 667 | KeepaliveInfo ki = null; |
| 668 | try { |
| 669 | ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds, |
Junyu Lai | 17a45f4 | 2019-04-26 01:22:29 +0000 | [diff] [blame] | 670 | KeepaliveInfo.TYPE_TCP, fd); |
junyulai | 1517815 | 2019-03-27 11:00:37 +0800 | [diff] [blame] | 671 | } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) { |
| 672 | Log.e(TAG, "Fail to construct keepalive e=" + e); |
junyulai | 0c66697 | 2019-03-04 22:45:36 +0800 | [diff] [blame] | 673 | notifyErrorCallback(cb, ERROR_INVALID_SOCKET); |
| 674 | return; |
| 675 | } |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 676 | Log.d(TAG, "Created keepalive: " + ki.toString()); |
markchien | 150e191 | 2018-12-27 22:49:51 +0800 | [diff] [blame] | 677 | mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 678 | } |
| 679 | |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 680 | /** |
| 681 | * Called when requesting that keepalives be started on a IPsec NAT-T socket. This function is |
| 682 | * identical to {@link #startNattKeepalive}, but also takes a {@code resourceId}, which is the |
| 683 | * resource index bound to the {@link UdpEncapsulationSocket} when creating by |
| 684 | * {@link com.android.server.IpSecService} to verify whether the given |
| 685 | * {@link UdpEncapsulationSocket} is legitimate. |
| 686 | **/ |
| 687 | public void startNattKeepalive(@Nullable NetworkAgentInfo nai, |
| 688 | @Nullable FileDescriptor fd, |
Junyu Lai | 17a45f4 | 2019-04-26 01:22:29 +0000 | [diff] [blame] | 689 | int resourceId, |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 690 | int intervalSeconds, |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 691 | @NonNull ISocketKeepaliveCallback cb, |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 692 | @NonNull String srcAddrString, |
| 693 | @NonNull String dstAddrString, |
| 694 | int dstPort) { |
Junyu Lai | 17a45f4 | 2019-04-26 01:22:29 +0000 | [diff] [blame] | 695 | // Ensure that the socket is created by IpSecService. |
| 696 | if (!isNattKeepaliveSocketValid(fd, resourceId)) { |
| 697 | notifyErrorCallback(cb, ERROR_INVALID_SOCKET); |
| 698 | } |
| 699 | |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 700 | // Get src port to adopt old API. |
| 701 | int srcPort = 0; |
| 702 | try { |
| 703 | final SocketAddress srcSockAddr = Os.getsockname(fd); |
| 704 | srcPort = ((InetSocketAddress) srcSockAddr).getPort(); |
| 705 | } catch (ErrnoException e) { |
junyulai | 7c46917 | 2019-01-16 20:23:34 +0800 | [diff] [blame] | 706 | notifyErrorCallback(cb, ERROR_INVALID_SOCKET); |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 707 | } |
| 708 | |
| 709 | // Forward request to old API. |
Junyu Lai | 17a45f4 | 2019-04-26 01:22:29 +0000 | [diff] [blame] | 710 | startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString, srcPort, |
| 711 | dstAddrString, dstPort); |
| 712 | } |
| 713 | |
| 714 | /** |
| 715 | * Verify if the IPsec NAT-T file descriptor and resource Id hold for IPsec keepalive is valid. |
| 716 | **/ |
| 717 | public static boolean isNattKeepaliveSocketValid(@Nullable FileDescriptor fd, int resourceId) { |
| 718 | // TODO: 1. confirm whether the fd is called from system api or created by IpSecService. |
| 719 | // 2. If the fd is created from the system api, check that it's bounded. And |
| 720 | // call dup to keep the fd open. |
| 721 | // 3. If the fd is created from IpSecService, check if the resource ID is valid. And |
| 722 | // hold the resource needed in IpSecService. |
| 723 | if (null == fd) { |
| 724 | return false; |
| 725 | } |
| 726 | return true; |
junyulai | 215b877 | 2019-01-15 11:32:44 +0800 | [diff] [blame] | 727 | } |
| 728 | |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 729 | public void dump(IndentingPrintWriter pw) { |
junyulai | a91094a | 2019-04-17 15:22:46 +0800 | [diff] [blame] | 730 | pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives)); |
| 731 | pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots); |
junyulai | e5c3c8c | 2019-04-30 14:42:05 +0800 | [diff] [blame] | 732 | pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid); |
junyulai | 0683511 | 2019-01-03 18:50:15 +0800 | [diff] [blame] | 733 | pw.println("Socket keepalives:"); |
Lorenzo Colitti | 8bf977d | 2015-06-15 14:29:22 +0900 | [diff] [blame] | 734 | pw.increaseIndent(); |
| 735 | for (NetworkAgentInfo nai : mKeepalives.keySet()) { |
| 736 | pw.println(nai.name()); |
| 737 | pw.increaseIndent(); |
| 738 | for (int slot : mKeepalives.get(nai).keySet()) { |
| 739 | KeepaliveInfo ki = mKeepalives.get(nai).get(slot); |
| 740 | pw.println(slot + ": " + ki.toString()); |
| 741 | } |
| 742 | pw.decreaseIndent(); |
| 743 | } |
| 744 | pw.decreaseIndent(); |
| 745 | } |
| 746 | } |