blob: af8a3666e9b75927c6befaab8bb4cedca995cc6a [file] [log] [blame]
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +09001/*
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
17package com.android.server.connectivity;
18
junyulai0c666972019-03-04 22:45:36 +080019import static android.content.pm.PackageManager.PERMISSION_GRANTED;
junyulai06835112019-01-03 18:50:15 +080020import static android.net.NattSocketKeepalive.NATT_PORT;
junyulai352dc2f2019-01-08 20:04:33 +080021import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER;
22import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER;
junyulai06835112019-01-03 18:50:15 +080023import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
24import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE;
junyulai06835112019-01-03 18:50:15 +080025import static android.net.SocketKeepalive.BINDER_DIED;
junyulai7c469172019-01-16 20:23:34 +080026import static android.net.SocketKeepalive.DATA_RECEIVED;
junyulai0c666972019-03-04 22:45:36 +080027import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
junyulai06835112019-01-03 18:50:15 +080028import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL;
29import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
30import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
junyulai215b8772019-01-15 11:32:44 +080031import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
junyulaia91094a2019-04-17 15:22:46 +080032import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
junyulai06835112019-01-03 18:50:15 +080033import static android.net.SocketKeepalive.MAX_INTERVAL_SEC;
34import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
35import static android.net.SocketKeepalive.NO_KEEPALIVE;
36import static android.net.SocketKeepalive.SUCCESS;
junyulai215b8772019-01-15 11:32:44 +080037
38import android.annotation.NonNull;
39import android.annotation.Nullable;
junyulai0c666972019-03-04 22:45:36 +080040import android.content.Context;
junyulai7c469172019-01-16 20:23:34 +080041import android.net.ISocketKeepaliveCallback;
Aaron Huang17c660d2019-10-02 01:39:46 +080042import android.net.InvalidPacketException;
Nathan Harold26de1d32017-11-02 21:01:46 -070043import android.net.KeepalivePacketData;
junyulai06835112019-01-03 18:50:15 +080044import android.net.NattKeepalivePacketData;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090045import android.net.NetworkAgent;
46import android.net.NetworkUtils;
junyulai352dc2f2019-01-08 20:04:33 +080047import android.net.SocketKeepalive.InvalidSocketException;
48import android.net.TcpKeepalivePacketData;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090049import android.net.util.IpUtils;
junyulaia91094a2019-04-17 15:22:46 +080050import android.net.util.KeepaliveUtils;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090051import android.os.Binder;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090052import android.os.Handler;
junyulai215b8772019-01-15 11:32:44 +080053import android.os.IBinder;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090054import android.os.Message;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090055import android.os.Process;
56import android.os.RemoteException;
junyulai215b8772019-01-15 11:32:44 +080057import android.system.ErrnoException;
58import android.system.Os;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090059import android.util.Log;
60import android.util.Pair;
61
junyulaia91094a2019-04-17 15:22:46 +080062import com.android.internal.R;
junyulai215b8772019-01-15 11:32:44 +080063import com.android.internal.util.HexDump;
64import com.android.internal.util.IndentingPrintWriter;
65
66import java.io.FileDescriptor;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090067import java.net.InetAddress;
junyulai215b8772019-01-15 11:32:44 +080068import java.net.InetSocketAddress;
69import java.net.SocketAddress;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090070import java.util.ArrayList;
junyulaia91094a2019-04-17 15:22:46 +080071import java.util.Arrays;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090072import java.util.HashMap;
73
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090074/**
junyulai06835112019-01-03 18:50:15 +080075 * Manages socket keepalive requests.
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090076 *
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
junyulai352dc2f2019-01-08 20:04:33 +080079 * handle* methods must be called only from the ConnectivityService handler thread.
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090080 */
81public class KeepaliveTracker {
82
83 private static final String TAG = "KeepaliveTracker";
Joe Onorato12acbd72016-02-01 17:49:31 -080084 private static final boolean DBG = false;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090085
Lorenzo Colittib9884662015-09-08 22:02:37 +090086 public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090087
88 /** Keeps track of keepalive requests. */
89 private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
90 new HashMap<> ();
91 private final Handler mConnectivityServiceHandler;
junyulai352dc2f2019-01-08 20:04:33 +080092 @NonNull
93 private final TcpKeepaliveController mTcpController;
junyulai0c666972019-03-04 22:45:36 +080094 @NonNull
95 private final Context mContext;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090096
junyulaia91094a2019-04-17 15:22:46 +080097 // 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
junyulaie5c3c8c2019-04-30 14:42:05 +0800107 // 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
junyulai0c666972019-03-04 22:45:36 +0800111 public KeepaliveTracker(Context context, Handler handler) {
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900112 mConnectivityServiceHandler = handler;
junyulai352dc2f2019-01-08 20:04:33 +0800113 mTcpController = new TcpKeepaliveController(handler);
junyulai0c666972019-03-04 22:45:36 +0800114 mContext = context;
junyulaia91094a2019-04-17 15:22:46 +0800115 mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext);
116 mReservedPrivilegedSlots = mContext.getResources().getInteger(
117 R.integer.config_reservedPrivilegedKeepaliveSlots);
junyulaie5c3c8c2019-04-30 14:42:05 +0800118 mAllowedUnprivilegedSlotsForUid = mContext.getResources().getInteger(
119 R.integer.config_allowedUnprivilegedKeepalivePerUid);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900120 }
121
122 /**
junyulai06835112019-01-03 18:50:15 +0800123 * Tracks information about a socket keepalive.
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900124 *
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 {
junyulai06835112019-01-03 18:50:15 +0800129 // Bookkeeping data.
junyulai7c469172019-01-16 20:23:34 +0800130 private final ISocketKeepaliveCallback mCallback;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900131 private final int mUid;
132 private final int mPid;
junyulai0c666972019-03-04 22:45:36 +0800133 private final boolean mPrivileged;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900134 private final NetworkAgentInfo mNai;
junyulai352dc2f2019-01-08 20:04:33 +0800135 private final int mType;
136 private final FileDescriptor mFd;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900137
junyulai352dc2f2019-01-08 20:04:33 +0800138 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.
junyulai06835112019-01-03 18:50:15 +0800143 private int mSlot = NO_KEEPALIVE;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900144
145 // Packet data.
146 private final KeepalivePacketData mPacket;
147 private final int mInterval;
148
junyulai352dc2f2019-01-08 20:04:33 +0800149 // 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;
junyulai7b305ce2019-04-01 11:33:49 +0800153 private static final int STOPPING = 4;
junyulai352dc2f2019-01-08 20:04:33 +0800154 private int mStartedState = NOT_STARTED;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900155
junyulai7c469172019-01-16 20:23:34 +0800156 KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback,
junyulai352dc2f2019-01-08 20:04:33 +0800157 @NonNull NetworkAgentInfo nai,
158 @NonNull KeepalivePacketData packet,
159 int interval,
160 int type,
Junyu Lai17a45f42019-04-26 01:22:29 +0000161 @Nullable FileDescriptor fd) throws InvalidSocketException {
junyulai7c469172019-01-16 20:23:34 +0800162 mCallback = callback;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900163 mPid = Binder.getCallingPid();
164 mUid = Binder.getCallingUid();
junyulai0c666972019-03-04 22:45:36 +0800165 mPrivileged = (PERMISSION_GRANTED == mContext.checkPermission(PERMISSION, mPid, mUid));
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900166
167 mNai = nai;
168 mPacket = packet;
169 mInterval = interval;
junyulai352dc2f2019-01-08 20:04:33 +0800170 mType = type;
junyulai0c666972019-03-04 22:45:36 +0800171
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.
junyulai0c666972019-03-04 22:45:36 +0800176 try {
177 if (fd != null) {
178 mFd = Os.dup(fd);
Junyu Lai17a45f42019-04-26 01:22:29 +0000179 } else {
junyulai15178152019-03-27 11:00:37 +0800180 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 }
junyulai0c666972019-03-04 22:45:36 +0800189 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 Colitti8bf977d2015-06-15 14:29:22 +0900195
196 try {
junyulai7c469172019-01-16 20:23:34 +0800197 mCallback.asBinder().linkToDeath(this, 0);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900198 } catch (RemoteException e) {
199 binderDied();
200 }
201 }
202
203 public NetworkAgentInfo getNai() {
204 return mNai;
205 }
206
junyulai352dc2f2019-01-08 20:04:33 +0800207 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";
junyulai05352952019-04-09 14:37:35 +0800212 case STOPPING : return "STOPPING";
junyulai352dc2f2019-01-08 20:04:33 +0800213 }
214 throw new IllegalArgumentException("Unknown state");
215 }
216
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900217 public String toString() {
junyulai352dc2f2019-01-08 20:04:33 +0800218 return "KeepaliveInfo ["
junyulai02abbfd2019-06-03 17:51:41 +0800219 + " type=" + mType
junyulai352dc2f2019-01-08 20:04:33 +0800220 + " 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
junyulai0c666972019-03-04 22:45:36 +0800227 + " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged
junyulai352dc2f2019-01-08 20:04:33 +0800228 + " packetData=" + HexDump.toHexString(mPacket.getPacket())
229 + " ]";
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900230 }
231
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900232 /** Called when the application process is killed. */
233 public void binderDied() {
junyulai352dc2f2019-01-08 20:04:33 +0800234 stop(BINDER_DIED);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900235 }
236
237 void unlinkDeathRecipient() {
junyulai7c469172019-01-16 20:23:34 +0800238 if (mCallback != null) {
239 mCallback.asBinder().unlinkToDeath(this, 0);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900240 }
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() {
junyulai06835112019-01-03 18:50:15 +0800261 if (mInterval < MIN_INTERVAL_SEC || mInterval > MAX_INTERVAL_SEC) {
262 return ERROR_INVALID_INTERVAL;
263 }
264 return SUCCESS;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900265 }
266
junyulai0c666972019-03-04 22:45:36 +0800267 private int checkPermission() {
268 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai);
junyulai0c666972019-03-04 22:45:36 +0800269 if (networkKeepalives == null) {
270 return ERROR_INVALID_NETWORK;
271 }
junyulaia91094a2019-04-17 15:22:46 +0800272
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;
junyulai0c666972019-03-04 22:45:36 +0800281 }
junyulaia91094a2019-04-17 15:22:46 +0800282 if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) {
283 return ERROR_INSUFFICIENT_RESOURCES;
284 }
285
junyulaie5c3c8c2019-04-30 14:42:05 +0800286 // 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 }
junyulai0c666972019-03-04 22:45:36 +0800293 }
294 }
junyulaie5c3c8c2019-04-30 14:42:05 +0800295 if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) {
296 return ERROR_INSUFFICIENT_RESOURCES;
297 }
junyulaia91094a2019-04-17 15:22:46 +0800298 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;
junyulai0c666972019-03-04 22:45:36 +0800310 return SUCCESS;
311 }
312
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900313 private int isValid() {
314 synchronized (mNai) {
315 int error = checkInterval();
junyulaia91094a2019-04-17 15:22:46 +0800316 if (error == SUCCESS) error = checkLimit();
junyulai0c666972019-03-04 22:45:36 +0800317 if (error == SUCCESS) error = checkPermission();
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900318 if (error == SUCCESS) error = checkNetworkConnected();
319 if (error == SUCCESS) error = checkSourceAddress();
320 return error;
321 }
322 }
323
324 void start(int slot) {
junyulai5e7128d2019-01-17 15:15:45 +0800325 mSlot = slot;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900326 int error = isValid();
327 if (error == SUCCESS) {
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900328 Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
junyulai352dc2f2019-01-08 20:04:33 +0800329 switch (mType) {
330 case TYPE_NATT:
Aaron Huang706ff1b2019-05-02 21:14:20 +0800331 mNai.asyncChannel.sendMessage(
332 CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket);
junyulai352dc2f2019-01-08 20:04:33 +0800333 mNai.asyncChannel
334 .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
335 break;
336 case TYPE_TCP:
junyulai1d5cd192019-03-04 11:49:17 +0800337 try {
338 mTcpController.startSocketMonitor(mFd, this, mSlot);
339 } catch (InvalidSocketException e) {
340 handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET);
341 return;
342 }
Aaron Huang706ff1b2019-05-02 21:14:20 +0800343 mNai.asyncChannel.sendMessage(
344 CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket);
junyulai352dc2f2019-01-08 20:04:33 +0800345 // 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 Colitti8bf977d2015-06-15 14:29:22 +0900355 } else {
junyulai5e7128d2019-01-17 15:15:45 +0800356 handleStopKeepalive(mNai, mSlot, error);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900357 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 }
junyulai05352952019-04-09 14:37:35 +0800368 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 Huang706ff1b2019-05-02 21:14:20 +0800380 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);
junyulai05352952019-04-09 14:37:35 +0800391 }
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900392 }
junyulai7c469172019-01-16 20:23:34 +0800393
junyulai0c666972019-03-04 22:45:36 +0800394 // 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
junyulai7c469172019-01-16 20:23:34 +0800406 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 Colitti8bf977d2015-06-15 14:29:22 +0900422 unlinkDeathRecipient();
423 }
junyulai352dc2f2019-01-08 20:04:33 +0800424
425 void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
426 handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
427 }
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900428 }
429
junyulai7c469172019-01-16 20:23:34 +0800430 void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) {
431 if (DBG) Log.w(TAG, "Sending onError(" + error + ") callback");
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900432 try {
junyulai7c469172019-01-16 20:23:34 +0800433 cb.onError(error);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900434 } catch (RemoteException e) {
junyulai7c469172019-01-16 20:23:34 +0800435 Log.w(TAG, "Discarded onError(" + error + ") callback");
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900436 }
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 Colitti9acca092015-09-08 13:44:23 +0900446 // 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 Colitti8bf977d2015-06-15 14:29:22 +0900448 int slot;
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900449 for (slot = 1; slot <= networkKeepalives.size(); slot++) {
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900450 if (networkKeepalives.get(slot) == null) {
451 return slot;
452 }
453 }
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900454 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) {
junyulaicf43dbd2019-05-13 14:19:00 +0800466 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900467 if (networkKeepalives != null) {
junyulaicf43dbd2019-05-13 14:19:00 +0800468 final ArrayList<KeepaliveInfo> kalist = new ArrayList(networkKeepalives.values());
469 for (KeepaliveInfo ki : kalist) {
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900470 ki.stop(reason);
junyulai5d7bb8b2019-04-25 11:24:04 +0800471 // 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 Colitti8bf977d2015-06-15 14:29:22 +0900474 }
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900475 }
476 }
477
478 public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900479 String networkName = (nai == null) ? "(null)" : nai.name();
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900480 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
481 if (networkKeepalives == null) {
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900482 Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900483 return;
484 }
485 KeepaliveInfo ki = networkKeepalives.get(slot);
486 if (ki == null) {
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900487 Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900488 return;
489 }
490 ki.stop(reason);
junyulai05352952019-04-09 14:37:35 +0800491 // 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 Colitti8bf977d2015-06-15 14:29:22 +0900507 networkKeepalives.remove(slot);
junyulai05352952019-04-09 14:37:35 +0800508 Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", "
junyulai0c666972019-03-04 22:45:36 +0800509 + networkKeepalives.size() + " remains.");
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900510 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
junyulai215b8772019-01-15 11:32:44 +0800531 /** Handle keepalive events from lower layer. */
junyulai06835112019-01-03 18:50:15 +0800532 public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai,
junyulai215b8772019-01-15 11:32:44 +0800533 @NonNull Message message) {
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900534 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) {
junyulai7b305ce2019-04-01 11:33:49 +0800542 Log.e(TAG, "Event " + message.what + "," + slot + "," + reason
543 + " for unknown keepalive " + slot + " on " + nai.name());
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900544 return;
545 }
546
junyulai352dc2f2019-01-08 20:04:33 +0800547 // 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.
junyulai7b305ce2019-04-01 11:33:49 +0800562 if (KeepaliveInfo.STARTING == ki.mStartedState) {
563 if (SUCCESS == reason) {
564 // Keepalive successfully started.
junyulaiedde28b2019-06-06 13:24:05 +0800565 Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
junyulai7b305ce2019-04-01 11:33:49 +0800566 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 Colitti8bf977d2015-06-15 14:29:22 +0900572 } else {
junyulai7b305ce2019-04-01 11:33:49 +0800573 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.
junyulai352dc2f2019-01-08 20:04:33 +0800576 handleStopKeepalive(nai, slot, reason);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900577 }
junyulai7b305ce2019-04-01 11:33:49 +0800578 } else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
junyulai05352952019-04-09 14:37:35 +0800579 // The message indicated result of stopping : clean up keepalive slots.
junyulai7b305ce2019-04-01 11:33:49 +0800580 Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
581 + " stopped: " + reason);
junyulai422a3c12019-03-27 10:31:11 +0800582 ki.mStartedState = KeepaliveInfo.NOT_STARTED;
junyulai05352952019-04-09 14:37:35 +0800583 cleanupStoppedKeepalive(nai, slot);
junyulai7b305ce2019-04-01 11:33:49 +0800584 } else {
585 Log.wtf(TAG, "Event " + message.what + "," + slot + "," + reason
586 + " for keepalive in wrong state: " + ki.toString());
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900587 }
588 }
589
junyulai215b8772019-01-15 11:32:44 +0800590 /**
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,
junyulai0c666972019-03-04 22:45:36 +0800595 @Nullable FileDescriptor fd,
junyulai215b8772019-01-15 11:32:44 +0800596 int intervalSeconds,
junyulai7c469172019-01-16 20:23:34 +0800597 @NonNull ISocketKeepaliveCallback cb,
junyulai215b8772019-01-15 11:32:44 +0800598 @NonNull String srcAddrString,
599 int srcPort,
600 @NonNull String dstAddrString,
601 int dstPort) {
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900602 if (nai == null) {
junyulai7c469172019-01-16 20:23:34 +0800603 notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900604 return;
605 }
606
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900607 InetAddress srcAddress, dstAddress;
608 try {
609 srcAddress = NetworkUtils.numericToInetAddress(srcAddrString);
610 dstAddress = NetworkUtils.numericToInetAddress(dstAddrString);
611 } catch (IllegalArgumentException e) {
junyulai7c469172019-01-16 20:23:34 +0800612 notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900613 return;
614 }
615
616 KeepalivePacketData packet;
617 try {
junyulai06835112019-01-03 18:50:15 +0800618 packet = NattKeepalivePacketData.nattKeepalivePacket(
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900619 srcAddress, srcPort, dstAddress, NATT_PORT);
junyulai06835112019-01-03 18:50:15 +0800620 } catch (InvalidPacketException e) {
junyulai7c469172019-01-16 20:23:34 +0800621 notifyErrorCallback(cb, e.error);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900622 return;
623 }
junyulai0c666972019-03-04 22:45:36 +0800624 KeepaliveInfo ki = null;
625 try {
626 ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
Junyu Lai17a45f42019-04-26 01:22:29 +0000627 KeepaliveInfo.TYPE_NATT, fd);
junyulai15178152019-03-27 11:00:37 +0800628 } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
629 Log.e(TAG, "Fail to construct keepalive", e);
junyulai0c666972019-03-04 22:45:36 +0800630 notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
631 return;
632 }
junyulai7c469172019-01-16 20:23:34 +0800633 Log.d(TAG, "Created keepalive: " + ki.toString());
junyulai352dc2f2019-01-08 20:04:33 +0800634 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,
junyulai7c469172019-01-16 20:23:34 +0800651 @NonNull ISocketKeepaliveCallback cb) {
junyulai352dc2f2019-01-08 20:04:33 +0800652 if (nai == null) {
junyulai7c469172019-01-16 20:23:34 +0800653 notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
junyulai352dc2f2019-01-08 20:04:33 +0800654 return;
655 }
656
markchien656fbe92019-02-20 21:16:44 +0800657 final TcpKeepalivePacketData packet;
junyulai352dc2f2019-01-08 20:04:33 +0800658 try {
markchien656fbe92019-02-20 21:16:44 +0800659 packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
Aaron Huang17c660d2019-10-02 01:39:46 +0800660 } catch (InvalidSocketException e) {
661 notifyErrorCallback(cb, e.error);
662 return;
663 } catch (InvalidPacketException e) {
junyulai7c469172019-01-16 20:23:34 +0800664 notifyErrorCallback(cb, e.error);
junyulai352dc2f2019-01-08 20:04:33 +0800665 return;
666 }
junyulai0c666972019-03-04 22:45:36 +0800667 KeepaliveInfo ki = null;
668 try {
669 ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
Junyu Lai17a45f42019-04-26 01:22:29 +0000670 KeepaliveInfo.TYPE_TCP, fd);
junyulai15178152019-03-27 11:00:37 +0800671 } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
672 Log.e(TAG, "Fail to construct keepalive e=" + e);
junyulai0c666972019-03-04 22:45:36 +0800673 notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
674 return;
675 }
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900676 Log.d(TAG, "Created keepalive: " + ki.toString());
markchien150e1912018-12-27 22:49:51 +0800677 mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900678 }
679
junyulai215b8772019-01-15 11:32:44 +0800680 /**
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 Lai17a45f42019-04-26 01:22:29 +0000689 int resourceId,
junyulai215b8772019-01-15 11:32:44 +0800690 int intervalSeconds,
junyulai7c469172019-01-16 20:23:34 +0800691 @NonNull ISocketKeepaliveCallback cb,
junyulai215b8772019-01-15 11:32:44 +0800692 @NonNull String srcAddrString,
693 @NonNull String dstAddrString,
694 int dstPort) {
Junyu Lai17a45f42019-04-26 01:22:29 +0000695 // Ensure that the socket is created by IpSecService.
696 if (!isNattKeepaliveSocketValid(fd, resourceId)) {
697 notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
698 }
699
junyulai215b8772019-01-15 11:32:44 +0800700 // 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) {
junyulai7c469172019-01-16 20:23:34 +0800706 notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
junyulai215b8772019-01-15 11:32:44 +0800707 }
708
709 // Forward request to old API.
Junyu Lai17a45f42019-04-26 01:22:29 +0000710 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;
junyulai215b8772019-01-15 11:32:44 +0800727 }
728
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900729 public void dump(IndentingPrintWriter pw) {
junyulaia91094a2019-04-17 15:22:46 +0800730 pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives));
731 pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots);
junyulaie5c3c8c2019-04-30 14:42:05 +0800732 pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid);
junyulai06835112019-01-03 18:50:15 +0800733 pw.println("Socket keepalives:");
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900734 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}