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