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