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