blob: 9e1f6b85ce390903f484419bdde66e938fb58a96 [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
19import com.android.internal.util.HexDump;
20import com.android.internal.util.IndentingPrintWriter;
21import com.android.server.connectivity.KeepalivePacketData;
22import com.android.server.connectivity.NetworkAgentInfo;
23import android.net.ConnectivityManager;
24import android.net.ConnectivityManager.PacketKeepalive;
25import android.net.LinkAddress;
26import android.net.NetworkAgent;
27import android.net.NetworkUtils;
28import android.net.util.IpUtils;
29import android.os.Binder;
30import android.os.IBinder;
31import android.os.Handler;
32import android.os.Message;
33import android.os.Messenger;
34import android.os.Process;
35import android.os.RemoteException;
36import android.system.OsConstants;
37import android.util.Log;
38import android.util.Pair;
39
40import java.nio.ByteBuffer;
41import java.nio.ByteOrder;
42import java.net.Inet4Address;
43import java.net.Inet6Address;
44import java.net.InetAddress;
45import java.util.ArrayList;
46import java.util.HashMap;
47
48import static android.net.ConnectivityManager.PacketKeepalive.*;
49import static android.net.NetworkAgent.CMD_START_PACKET_KEEPALIVE;
50import static android.net.NetworkAgent.CMD_STOP_PACKET_KEEPALIVE;
51import static android.net.NetworkAgent.EVENT_PACKET_KEEPALIVE;
52
53/**
54 * Manages packet keepalive requests.
55 *
56 * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all
57 * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its
58 * methods must be called only from the ConnectivityService handler thread.
59 */
60public class KeepaliveTracker {
61
62 private static final String TAG = "KeepaliveTracker";
Joe Onorato12acbd72016-02-01 17:49:31 -080063 private static final boolean DBG = false;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090064
Lorenzo Colittib9884662015-09-08 22:02:37 +090065 public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +090066
67 /** Keeps track of keepalive requests. */
68 private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
69 new HashMap<> ();
70 private final Handler mConnectivityServiceHandler;
71
72 public KeepaliveTracker(Handler handler) {
73 mConnectivityServiceHandler = handler;
74 }
75
76 /**
77 * Tracks information about a packet keepalive.
78 *
79 * All information about this keepalive is known at construction time except the slot number,
80 * which is only returned when the hardware has successfully started the keepalive.
81 */
82 class KeepaliveInfo implements IBinder.DeathRecipient {
83 // Bookkeping data.
84 private final Messenger mMessenger;
85 private final IBinder mBinder;
86 private final int mUid;
87 private final int mPid;
88 private final NetworkAgentInfo mNai;
89
90 /** Keepalive slot. A small integer that identifies this keepalive among the ones handled
91 * by this network. */
92 private int mSlot = PacketKeepalive.NO_KEEPALIVE;
93
94 // Packet data.
95 private final KeepalivePacketData mPacket;
96 private final int mInterval;
97
98 // Whether the keepalive is started or not.
99 public boolean isStarted;
100
101 public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai,
102 KeepalivePacketData packet, int interval) {
103 mMessenger = messenger;
104 mBinder = binder;
105 mPid = Binder.getCallingPid();
106 mUid = Binder.getCallingUid();
107
108 mNai = nai;
109 mPacket = packet;
110 mInterval = interval;
111
112 try {
113 mBinder.linkToDeath(this, 0);
114 } catch (RemoteException e) {
115 binderDied();
116 }
117 }
118
119 public NetworkAgentInfo getNai() {
120 return mNai;
121 }
122
123 public String toString() {
124 return new StringBuffer("KeepaliveInfo [")
125 .append(" network=").append(mNai.network)
126 .append(" isStarted=").append(isStarted)
127 .append(" ")
128 .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort))
129 .append("->")
130 .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort))
131 .append(" interval=" + mInterval)
132 .append(" data=" + HexDump.toHexString(mPacket.data))
133 .append(" uid=").append(mUid).append(" pid=").append(mPid)
134 .append(" ]")
135 .toString();
136 }
137
138 /** Sends a message back to the application via its PacketKeepalive.Callback. */
139 void notifyMessenger(int slot, int err) {
140 KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err);
141 }
142
143 /** Called when the application process is killed. */
144 public void binderDied() {
145 // Not called from ConnectivityService handler thread, so send it a message.
146 mConnectivityServiceHandler.obtainMessage(
147 NetworkAgent.CMD_STOP_PACKET_KEEPALIVE,
148 mSlot, PacketKeepalive.BINDER_DIED, mNai.network).sendToTarget();
149 }
150
151 void unlinkDeathRecipient() {
152 if (mBinder != null) {
153 mBinder.unlinkToDeath(this, 0);
154 }
155 }
156
157 private int checkNetworkConnected() {
158 if (!mNai.networkInfo.isConnectedOrConnecting()) {
159 return ERROR_INVALID_NETWORK;
160 }
161 return SUCCESS;
162 }
163
164 private int checkSourceAddress() {
165 // Check that we have the source address.
166 for (InetAddress address : mNai.linkProperties.getAddresses()) {
167 if (address.equals(mPacket.srcAddress)) {
168 return SUCCESS;
169 }
170 }
171 return ERROR_INVALID_IP_ADDRESS;
172 }
173
174 private int checkInterval() {
175 return mInterval >= 20 ? SUCCESS : ERROR_INVALID_INTERVAL;
176 }
177
178 private int isValid() {
179 synchronized (mNai) {
180 int error = checkInterval();
181 if (error == SUCCESS) error = checkNetworkConnected();
182 if (error == SUCCESS) error = checkSourceAddress();
183 return error;
184 }
185 }
186
187 void start(int slot) {
188 int error = isValid();
189 if (error == SUCCESS) {
190 mSlot = slot;
191 Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
192 mNai.asyncChannel.sendMessage(CMD_START_PACKET_KEEPALIVE, slot, mInterval, mPacket);
193 } else {
194 notifyMessenger(NO_KEEPALIVE, error);
195 return;
196 }
197 }
198
199 void stop(int reason) {
200 int uid = Binder.getCallingUid();
201 if (uid != mUid && uid != Process.SYSTEM_UID) {
202 if (DBG) {
203 Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
204 }
205 }
206 if (isStarted) {
207 Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
208 mNai.asyncChannel.sendMessage(CMD_STOP_PACKET_KEEPALIVE, mSlot);
209 }
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900210 // TODO: at the moment we unconditionally return failure here. In cases where the
211 // NetworkAgent is alive, should we ask it to reply, so it can return failure?
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900212 notifyMessenger(mSlot, reason);
213 unlinkDeathRecipient();
214 }
215 }
216
217 void notifyMessenger(Messenger messenger, int slot, int err) {
218 Message message = Message.obtain();
219 message.what = EVENT_PACKET_KEEPALIVE;
220 message.arg1 = slot;
221 message.arg2 = err;
222 message.obj = null;
223 try {
224 messenger.send(message);
225 } catch (RemoteException e) {
226 // Process died?
227 }
228 }
229
230 private int findFirstFreeSlot(NetworkAgentInfo nai) {
231 HashMap networkKeepalives = mKeepalives.get(nai);
232 if (networkKeepalives == null) {
233 networkKeepalives = new HashMap<Integer, KeepaliveInfo>();
234 mKeepalives.put(nai, networkKeepalives);
235 }
236
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900237 // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two
238 // separate chipset implementations independently came up with.
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900239 int slot;
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900240 for (slot = 1; slot <= networkKeepalives.size(); slot++) {
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900241 if (networkKeepalives.get(slot) == null) {
242 return slot;
243 }
244 }
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900245 return slot;
246 }
247
248 public void handleStartKeepalive(Message message) {
249 KeepaliveInfo ki = (KeepaliveInfo) message.obj;
250 NetworkAgentInfo nai = ki.getNai();
251 int slot = findFirstFreeSlot(nai);
252 mKeepalives.get(nai).put(slot, ki);
253 ki.start(slot);
254 }
255
256 public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
257 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
258 if (networkKeepalives != null) {
259 for (KeepaliveInfo ki : networkKeepalives.values()) {
260 ki.stop(reason);
261 }
262 networkKeepalives.clear();
263 mKeepalives.remove(nai);
264 }
265 }
266
267 public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900268 String networkName = (nai == null) ? "(null)" : nai.name();
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900269 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
270 if (networkKeepalives == null) {
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900271 Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900272 return;
273 }
274 KeepaliveInfo ki = networkKeepalives.get(slot);
275 if (ki == null) {
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900276 Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName);
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900277 return;
278 }
279 ki.stop(reason);
280 networkKeepalives.remove(slot);
281 if (networkKeepalives.isEmpty()) {
282 mKeepalives.remove(nai);
283 }
284 }
285
286 public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
287 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
288 if (networkKeepalives != null) {
289 ArrayList<Pair<Integer, Integer>> invalidKeepalives = new ArrayList<>();
290 for (int slot : networkKeepalives.keySet()) {
291 int error = networkKeepalives.get(slot).isValid();
292 if (error != SUCCESS) {
293 invalidKeepalives.add(Pair.create(slot, error));
294 }
295 }
296 for (Pair<Integer, Integer> slotAndError: invalidKeepalives) {
297 handleStopKeepalive(nai, slotAndError.first, slotAndError.second);
298 }
299 }
300 }
301
302 public void handleEventPacketKeepalive(NetworkAgentInfo nai, Message message) {
303 int slot = message.arg1;
304 int reason = message.arg2;
305
306 KeepaliveInfo ki = null;
307 try {
308 ki = mKeepalives.get(nai).get(slot);
309 } catch(NullPointerException e) {}
310 if (ki == null) {
311 Log.e(TAG, "Event for unknown keepalive " + slot + " on " + nai.name());
312 return;
313 }
314
315 if (reason == SUCCESS && !ki.isStarted) {
316 // Keepalive successfully started.
317 if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
318 ki.isStarted = true;
319 ki.notifyMessenger(slot, reason);
320 } else {
321 // Keepalive successfully stopped, or error.
322 ki.isStarted = false;
323 if (reason == SUCCESS) {
324 if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name());
325 } else {
326 if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason);
327 }
328 handleStopKeepalive(nai, slot, reason);
329 }
330 }
331
332 public void startNattKeepalive(NetworkAgentInfo nai, int intervalSeconds, Messenger messenger,
333 IBinder binder, String srcAddrString, int srcPort, String dstAddrString, int dstPort) {
Lorenzo Colitti9acca092015-09-08 13:44:23 +0900334 if (nai == null) {
335 notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK);
336 return;
337 }
338
Lorenzo Colitti8bf977d2015-06-15 14:29:22 +0900339 InetAddress srcAddress, dstAddress;
340 try {
341 srcAddress = NetworkUtils.numericToInetAddress(srcAddrString);
342 dstAddress = NetworkUtils.numericToInetAddress(dstAddrString);
343 } catch (IllegalArgumentException e) {
344 notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_IP_ADDRESS);
345 return;
346 }
347
348 KeepalivePacketData packet;
349 try {
350 packet = KeepalivePacketData.nattKeepalivePacket(
351 srcAddress, srcPort, dstAddress, NATT_PORT);
352 } catch (KeepalivePacketData.InvalidPacketException e) {
353 notifyMessenger(messenger, NO_KEEPALIVE, e.error);
354 return;
355 }
356 KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds);
357 Log.d(TAG, "Created keepalive: " + ki.toString());
358 mConnectivityServiceHandler.obtainMessage(
359 NetworkAgent.CMD_START_PACKET_KEEPALIVE, ki).sendToTarget();
360 }
361
362 public void dump(IndentingPrintWriter pw) {
363 pw.println("Packet keepalives:");
364 pw.increaseIndent();
365 for (NetworkAgentInfo nai : mKeepalives.keySet()) {
366 pw.println(nai.name());
367 pw.increaseIndent();
368 for (int slot : mKeepalives.get(nai).keySet()) {
369 KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
370 pw.println(slot + ": " + ki.toString());
371 }
372 pw.decreaseIndent();
373 }
374 pw.decreaseIndent();
375 }
376}