PanService.java revision 8120c75b7a8b5a9c0e00df500c1ed072a952bda1
1/* 2 * Copyright (C) 2012 Google Inc. 3 */ 4 5package com.android.bluetooth.pan; 6 7import android.app.Service; 8import android.bluetooth.BluetoothDevice; 9import android.bluetooth.BluetoothPan; 10import android.bluetooth.BluetoothProfile; 11import android.bluetooth.BluetoothTetheringDataTracker; 12import android.bluetooth.IBluetooth; 13import android.bluetooth.IBluetoothPan; 14import android.content.Context; 15import android.content.Intent; 16import android.content.pm.PackageManager; 17import android.content.res.Resources.NotFoundException; 18import android.net.ConnectivityManager; 19import android.net.InterfaceConfiguration; 20import android.net.LinkAddress; 21import android.net.NetworkUtils; 22import android.os.IBinder; 23import android.os.INetworkManagementService; 24import android.os.Handler; 25import android.os.Message; 26import android.os.RemoteException; 27import android.os.ServiceManager; 28import android.provider.Settings; 29import android.util.Log; 30 31import java.net.InetAddress; 32import java.util.ArrayList; 33import java.util.Collections; 34import java.util.HashMap; 35import java.util.List; 36import java.util.Map; 37import com.android.bluetooth.Utils; 38import com.android.bluetooth.btservice.ProfileService; 39 40/** 41 * Provides Bluetooth Pan Device profile, as a service in 42 * the Bluetooth application. 43 * @hide 44 */ 45public class PanService extends ProfileService { 46 private static final String TAG = "PanService"; 47 private static final boolean DBG = true; 48 49 private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1"; 50 private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; 51 private static final int BLUETOOTH_PREFIX_LENGTH = 24; 52 53 private HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices; 54 private ArrayList<String> mBluetoothIfaceAddresses; 55 private int mMaxPanDevices; 56 private String mPanIfName; 57 private boolean mNativeAvailable; 58 59 private static final int MESSAGE_CONNECT = 1; 60 private static final int MESSAGE_DISCONNECT = 2; 61 private static final int MESSAGE_CONNECT_STATE_CHANGED = 11; 62 private boolean mTetherOn = false; 63 64 65 static { 66 classInitNative(); 67 } 68 69 protected String getName() { 70 return TAG; 71 } 72 73 public IProfileServiceBinder initBinder() { 74 return new BluetoothPanBinder(this); 75 } 76 77 protected boolean start() { 78 mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>(); 79 mBluetoothIfaceAddresses = new ArrayList<String>(); 80 try { 81 mMaxPanDevices = getResources().getInteger( 82 com.android.internal.R.integer.config_max_pan_devices); 83 } catch (NotFoundException e) { 84 mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; 85 } 86 initializeNative(); 87 mNativeAvailable=true; 88 return true; 89 } 90 91 protected boolean stop() { 92 mHandler.removeCallbacksAndMessages(null); 93 return true; 94 } 95 96 protected boolean cleanup() { 97 if (mNativeAvailable) { 98 cleanupNative(); 99 mNativeAvailable=false; 100 } 101 if(mPanDevices != null) { 102 List<BluetoothDevice> DevList = getConnectedDevices(); 103 for(BluetoothDevice dev : DevList) { 104 handlePanDeviceStateChange(dev, mPanIfName, BluetoothProfile.STATE_DISCONNECTED, 105 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); 106 } 107 mPanDevices.clear(); 108 mPanDevices = null; 109 } 110 if(mBluetoothIfaceAddresses != null) { 111 mBluetoothIfaceAddresses.clear(); 112 mBluetoothIfaceAddresses = null; 113 } 114 if(mPanIfName != null) 115 mPanIfName = null; 116 return true; 117 } 118 119 private final Handler mHandler = new Handler() { 120 @Override 121 public void handleMessage(Message msg) { 122 switch (msg.what) { 123 case MESSAGE_CONNECT: 124 { 125 BluetoothDevice device = (BluetoothDevice) msg.obj; 126 if (!connectPanNative(Utils.getByteAddress(device), BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) { 127 handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING, 128 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); 129 handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_DISCONNECTED, 130 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); 131 break; 132 } 133 } 134 break; 135 case MESSAGE_DISCONNECT: 136 { 137 BluetoothDevice device = (BluetoothDevice) msg.obj; 138 if (!disconnectPanNative(Utils.getByteAddress(device)) ) { 139 handlePanDeviceStateChange(device, mPanIfName, BluetoothProfile.STATE_DISCONNECTING, 140 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); 141 handlePanDeviceStateChange(device, mPanIfName, BluetoothProfile.STATE_DISCONNECTED, 142 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); 143 break; 144 } 145 } 146 break; 147 case MESSAGE_CONNECT_STATE_CHANGED: 148 { 149 ConnectState cs = (ConnectState)msg.obj; 150 BluetoothDevice device = getDevice(cs.addr); 151 // TBD get iface from the msg 152 if (DBG) log("MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state); 153 handlePanDeviceStateChange(device, mPanIfName /* iface */, convertHalState(cs.state), 154 cs.local_role, cs.remote_role); 155 } 156 break; 157 } 158 } 159 }; 160 161 /** 162 * Handlers for incoming service calls 163 */ 164 private static class BluetoothPanBinder extends IBluetoothPan.Stub implements IProfileServiceBinder { 165 private PanService mService; 166 public BluetoothPanBinder(PanService svc) { 167 mService = svc; 168 } 169 public boolean cleanup() { 170 mService = null; 171 return true; 172 } 173 private PanService getService() { 174 if (mService != null && mService.isAvailable()) { 175 return mService; 176 } 177 return null; 178 } 179 public boolean connect(BluetoothDevice device) { 180 PanService service = getService(); 181 if (service == null) return false; 182 return service.connect(device); 183 } 184 public boolean disconnect(BluetoothDevice device) { 185 PanService service = getService(); 186 if (service == null) return false; 187 return service.disconnect(device); 188 } 189 public int getConnectionState(BluetoothDevice device) { 190 PanService service = getService(); 191 if (service == null) return BluetoothPan.STATE_DISCONNECTED; 192 return service.getConnectionState(device); 193 } 194 private boolean isPanNapOn() { 195 PanService service = getService(); 196 if (service == null) return false; 197 return service.isPanNapOn(); 198 } 199 private boolean isPanUOn() { 200 if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); 201 PanService service = getService(); 202 return service.isPanUOn(); 203 } 204 public boolean isTetheringOn() { 205 // TODO(BT) have a variable marking the on/off state 206 PanService service = getService(); 207 if (service == null) return false; 208 return service.isTetheringOn(); 209 } 210 public void setBluetoothTethering(boolean value) { 211 PanService service = getService(); 212 if (service == null) return; 213 if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + service.mTetherOn); 214 service.setBluetoothTethering(value); 215 } 216 217 public List<BluetoothDevice> getConnectedDevices() { 218 PanService service = getService(); 219 if (service == null) return new ArrayList<BluetoothDevice>(0); 220 return service.getConnectedDevices(); 221 } 222 223 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 224 PanService service = getService(); 225 if (service == null) return new ArrayList<BluetoothDevice>(0); 226 return service.getDevicesMatchingConnectionStates(states); 227 } 228 }; 229 230 boolean connect(BluetoothDevice device) { 231 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 232 if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) { 233 Log.e(TAG, "Pan Device not disconnected: " + device); 234 return false; 235 } 236 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT,device); 237 mHandler.sendMessage(msg); 238 return true; 239 } 240 241 boolean disconnect(BluetoothDevice device) { 242 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 243 Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device); 244 mHandler.sendMessage(msg); 245 return true; 246 } 247 248 int getConnectionState(BluetoothDevice device) { 249 BluetoothPanDevice panDevice = mPanDevices.get(device); 250 if (panDevice == null) { 251 return BluetoothPan.STATE_DISCONNECTED; 252 } 253 return panDevice.mState; 254 } 255 256 boolean isPanNapOn() { 257 if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); 258 return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0; 259 } 260 boolean isPanUOn() { 261 if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); 262 return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0; 263 } 264 boolean isTetheringOn() { 265 // TODO(BT) have a variable marking the on/off state 266 return mTetherOn; 267 } 268 269 void setBluetoothTethering(boolean value) { 270 if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + mTetherOn); 271 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 272 if(mTetherOn != value) { 273 //drop any existing panu or pan-nap connection when changing the tethering state 274 mTetherOn = value; 275 List<BluetoothDevice> DevList = getConnectedDevices(); 276 for(BluetoothDevice dev : DevList) 277 disconnect(dev); 278 } 279 } 280 281 List<BluetoothDevice> getConnectedDevices() { 282 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 283 List<BluetoothDevice> devices = getDevicesMatchingConnectionStates( 284 new int[] {BluetoothProfile.STATE_CONNECTED}); 285 return devices; 286 } 287 288 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 289 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 290 List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>(); 291 292 for (BluetoothDevice device: mPanDevices.keySet()) { 293 int panDeviceState = getConnectionState(device); 294 for (int state : states) { 295 if (state == panDeviceState) { 296 panDevices.add(device); 297 break; 298 } 299 } 300 } 301 return panDevices; 302 } 303 304 static protected class ConnectState { 305 public ConnectState(byte[] address, int state, int error, int local_role, int remote_role) { 306 this.addr = address; 307 this.state = state; 308 this.error = error; 309 this.local_role = local_role; 310 this.remote_role = remote_role; 311 } 312 byte[] addr; 313 int state; 314 int error; 315 int local_role; 316 int remote_role; 317 }; 318 private void onConnectStateChanged(byte[] address, int state, int error, int local_role, int remote_role) { 319 if (DBG) log("onConnectStateChanged: " + state + ", local role:" + local_role + ", remote_role: " + remote_role); 320 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED); 321 msg.obj = new ConnectState(address, state, error, local_role, remote_role); 322 mHandler.sendMessage(msg); 323 } 324 private void onControlStateChanged(int local_role, int state, int error, String ifname) { 325 if (DBG) 326 log("onControlStateChanged: " + state + ", error: " + error + ", ifname: " + ifname); 327 if(error == 0) 328 mPanIfName = ifname; 329 } 330 331 private static int convertHalState(int halState) { 332 switch (halState) { 333 case CONN_STATE_CONNECTED: 334 return BluetoothProfile.STATE_CONNECTED; 335 case CONN_STATE_CONNECTING: 336 return BluetoothProfile.STATE_CONNECTING; 337 case CONN_STATE_DISCONNECTED: 338 return BluetoothProfile.STATE_DISCONNECTED; 339 case CONN_STATE_DISCONNECTING: 340 return BluetoothProfile.STATE_DISCONNECTING; 341 default: 342 Log.e(TAG, "bad pan connection state: " + halState); 343 return BluetoothProfile.STATE_DISCONNECTED; 344 } 345 } 346 347 void handlePanDeviceStateChange(BluetoothDevice device, 348 String iface, int state, int local_role, int remote_role) { 349 if(DBG) Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface + 350 ", state: " + state + ", local_role:" + local_role + ", remote_role:" + remote_role); 351 int prevState; 352 String ifaceAddr = null; 353 BluetoothPanDevice panDevice = mPanDevices.get(device); 354 if (panDevice == null) { 355 prevState = BluetoothProfile.STATE_DISCONNECTED; 356 } else { 357 prevState = panDevice.mState; 358 ifaceAddr = panDevice.mIfaceAddr; 359 } 360 Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state); 361 if (prevState == state) return; 362 if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) { 363 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE"); 364 if (state == BluetoothProfile.STATE_CONNECTED) { 365 if(!mTetherOn) { 366 Log.d(TAG, "handlePanDeviceStateChange bluetooth tethering is off, drop the connection"); 367 disconnectPanNative(Utils.getByteAddress(device)); 368 return; 369 } 370 ifaceAddr = enableTethering(iface); 371 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface"); 372 373 } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 374 if (ifaceAddr != null) { 375 mBluetoothIfaceAddresses.remove(ifaceAddr); 376 ifaceAddr = null; 377 } 378 } 379 } else { 380 // PANU Role = reverse Tether 381 Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE"); 382 if (state == BluetoothProfile.STATE_CONNECTED) { 383 if(DBG) Log.d(TAG, "handlePanDeviceStateChange: panu STATE_CONNECTED, startReverseTether"); 384 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 385 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); 386 Log.d(TAG, "call INetworkManagementService.startReverseTethering()"); 387 try { 388 service.startReverseTethering(iface); 389 } catch (Exception e) { 390 Log.e(TAG, "Cannot start reverse tethering: " + e); 391 return; 392 } 393 } else if (state == BluetoothProfile.STATE_DISCONNECTED && 394 (prevState == BluetoothProfile.STATE_CONNECTED || 395 prevState == BluetoothProfile.STATE_DISCONNECTING)) { 396 if(DBG) Log.d(TAG, "handlePanDeviceStateChange: stopReverseTether, panDevice.mIface: " 397 + panDevice.mIface); 398 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 399 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); 400 try { 401 service.stopReverseTethering(); 402 } catch(Exception e) { 403 Log.e(TAG, "Cannot stop reverse tethering: " + e); 404 return; 405 } 406 } 407 } 408 409 if (panDevice == null) { 410 panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, local_role); 411 mPanDevices.put(device, panDevice); 412 } else { 413 panDevice.mState = state; 414 panDevice.mIfaceAddr = ifaceAddr; 415 panDevice.mLocalRole = local_role; 416 panDevice.mIface = iface; 417 } 418 419 /* Notifying the connection state change of the profile before sending the intent for 420 connection state change, as it was causing a race condition, with the UI not being 421 updated with the correct connection state. */ 422 if (DBG) Log.d(TAG, "Pan Device state : device: " + device + " State:" + 423 prevState + "->" + state); 424 notifyProfileConnectionStateChanged(device, BluetoothProfile.PAN, state, prevState); 425 Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); 426 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 427 intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState); 428 intent.putExtra(BluetoothPan.EXTRA_STATE, state); 429 intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, local_role); 430 sendBroadcast(intent, BLUETOOTH_PERM); 431 } 432 433 // configured when we start tethering 434 private String enableTethering(String iface) { 435 if (DBG) Log.d(TAG, "updateTetherState:" + iface); 436 437 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 438 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); 439 ConnectivityManager cm = 440 (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 441 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); 442 443 // bring toggle the interfaces 444 String[] currentIfaces = new String[0]; 445 try { 446 currentIfaces = service.listInterfaces(); 447 } catch (Exception e) { 448 Log.e(TAG, "Error listing Interfaces :" + e); 449 return null; 450 } 451 452 boolean found = false; 453 for (String currIface: currentIfaces) { 454 if (currIface.equals(iface)) { 455 found = true; 456 break; 457 } 458 } 459 460 if (!found) return null; 461 462 String address = createNewTetheringAddressLocked(); 463 if (address == null) return null; 464 465 InterfaceConfiguration ifcg = null; 466 try { 467 ifcg = service.getInterfaceConfig(iface); 468 if (ifcg != null) { 469 InetAddress addr = null; 470 if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null || 471 addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) || 472 addr.equals(NetworkUtils.numericToInetAddress("::0"))) { 473 addr = NetworkUtils.numericToInetAddress(address); 474 } 475 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); 476 ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH); 477 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); 478 ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); 479 service.setInterfaceConfig(iface, ifcg); 480 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { 481 Log.e(TAG, "Error tethering "+iface); 482 } 483 } 484 } catch (Exception e) { 485 Log.e(TAG, "Error configuring interface " + iface + ", :" + e); 486 return null; 487 } 488 return address; 489 } 490 491 private String createNewTetheringAddressLocked() { 492 if (getConnectedPanDevices().size() == mMaxPanDevices) { 493 if (DBG) Log.d(TAG, "Max PAN device connections reached"); 494 return null; 495 } 496 String address = BLUETOOTH_IFACE_ADDR_START; 497 while (true) { 498 if (mBluetoothIfaceAddresses.contains(address)) { 499 String[] addr = address.split("\\."); 500 Integer newIp = Integer.parseInt(addr[2]) + 1; 501 address = address.replace(addr[2], newIp.toString()); 502 } else { 503 break; 504 } 505 } 506 mBluetoothIfaceAddresses.add(address); 507 return address; 508 } 509 510 private List<BluetoothDevice> getConnectedPanDevices() { 511 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 512 513 for (BluetoothDevice device: mPanDevices.keySet()) { 514 if (getPanDeviceConnectionState(device) == BluetoothProfile.STATE_CONNECTED) { 515 devices.add(device); 516 } 517 } 518 return devices; 519 } 520 521 private int getPanDeviceConnectionState(BluetoothDevice device) { 522 BluetoothPanDevice panDevice = mPanDevices.get(device); 523 if (panDevice == null) { 524 return BluetoothProfile.STATE_DISCONNECTED; 525 } 526 return panDevice.mState; 527 } 528 529 private class BluetoothPanDevice { 530 private int mState; 531 private String mIfaceAddr; 532 private String mIface; 533 private int mLocalRole; // Which local role is this PAN device bound to 534 535 BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) { 536 mState = state; 537 mIfaceAddr = ifaceAddr; 538 mIface = iface; 539 mLocalRole = localRole; 540 } 541 } 542 543 // Constants matching Hal header file bt_hh.h 544 // bthh_connection_state_t 545 private final static int CONN_STATE_CONNECTED = 0; 546 private final static int CONN_STATE_CONNECTING = 1; 547 private final static int CONN_STATE_DISCONNECTED = 2; 548 private final static int CONN_STATE_DISCONNECTING = 3; 549 550 private native static void classInitNative(); 551 private native void initializeNative(); 552 private native void cleanupNative(); 553 private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role); 554 private native boolean disconnectPanNative(byte[] btAddress); 555 private native boolean enablePanNative(int local_role); 556 private native int getPanLocalRoleNative(); 557 558} 559