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