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