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 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"; 63 private static final boolean DBG = false; 64 65 public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD; 66 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 } 210 // 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? 212 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 237 // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two 238 // separate chipset implementations independently came up with. 239 int slot; 240 for (slot = 1; slot <= networkKeepalives.size(); slot++) { 241 if (networkKeepalives.get(slot) == null) { 242 return slot; 243 } 244 } 245 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) { 268 String networkName = (nai == null) ? "(null)" : nai.name(); 269 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 270 if (networkKeepalives == null) { 271 Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName); 272 return; 273 } 274 KeepaliveInfo ki = networkKeepalives.get(slot); 275 if (ki == null) { 276 Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName); 277 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) { 334 if (nai == null) { 335 notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK); 336 return; 337 } 338 339 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} 377