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