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 static android.Manifest.permission.BLUETOOTH_CONNECT; 20 import static android.Manifest.permission.TETHER_PRIVILEGED; 21 22 import android.annotation.RequiresPermission; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothPan; 25 import android.bluetooth.BluetoothPan.LocalPanRole; 26 import android.bluetooth.BluetoothPan.RemotePanRole; 27 import android.bluetooth.BluetoothProfile; 28 import android.bluetooth.IBluetoothPan; 29 import android.bluetooth.IBluetoothPanCallback; 30 import android.content.AttributionSource; 31 import android.content.Intent; 32 import android.content.res.Resources.NotFoundException; 33 import android.net.TetheringInterface; 34 import android.net.TetheringManager; 35 import android.os.Handler; 36 import android.os.Looper; 37 import android.os.Message; 38 import android.os.RemoteException; 39 import android.os.UserManager; 40 import android.sysprop.BluetoothProperties; 41 import android.util.Log; 42 43 import com.android.bluetooth.BluetoothMetricsProto; 44 import com.android.bluetooth.Utils; 45 import com.android.bluetooth.btservice.AdapterService; 46 import com.android.bluetooth.btservice.MetricsLogger; 47 import com.android.bluetooth.btservice.ProfileService; 48 import com.android.bluetooth.btservice.storage.DatabaseManager; 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.modules.utils.HandlerExecutor; 51 import com.android.modules.utils.SynchronousResultReceiver; 52 53 import java.util.ArrayList; 54 import java.util.HashMap; 55 import java.util.List; 56 import java.util.Map; 57 import java.util.Objects; 58 59 /** 60 * Provides Bluetooth Pan Device profile, as a service in 61 * the Bluetooth application. 62 * @hide 63 */ 64 public class PanService extends ProfileService { 65 private static final String TAG = "PanService"; 66 private static final boolean DBG = false; 67 private static PanService sPanService; 68 69 private static final String BLUETOOTH_IFACE_ADDR_START = "192.168.44.1"; 70 private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; 71 private static final int BLUETOOTH_PREFIX_LENGTH = 24; 72 73 @VisibleForTesting 74 HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices; 75 private int mMaxPanDevices; 76 private String mPanIfName; 77 @VisibleForTesting 78 boolean mIsTethering = false; 79 private boolean mNativeAvailable; 80 private HashMap<String, IBluetoothPanCallback> mBluetoothTetheringCallbacks; 81 82 private TetheringManager mTetheringManager; 83 private DatabaseManager mDatabaseManager; 84 @VisibleForTesting 85 UserManager mUserManager; 86 87 private static final int MESSAGE_CONNECT = 1; 88 private static final int MESSAGE_DISCONNECT = 2; 89 private static final int MESSAGE_CONNECT_STATE_CHANGED = 11; 90 private boolean mTetherOn = false; 91 92 private BluetoothTetheringNetworkFactory mNetworkFactory; 93 private boolean mStarted = false; 94 95 private AdapterService mAdapterService; 96 97 TetheringManager.TetheringEventCallback mTetheringCallback = 98 new TetheringManager.TetheringEventCallback() { 99 @Override 100 public void onError(TetheringInterface iface, int error) { 101 if (mIsTethering 102 && iface.getType() == TetheringManager.TETHERING_BLUETOOTH) { 103 // tethering is fail because of @TetheringIfaceError error. 104 Log.e(TAG, "Error setting up tether interface: " + error); 105 for (Map.Entry device : mPanDevices.entrySet()) { 106 disconnectPanNative(Utils.getByteAddress( 107 (BluetoothDevice) device.getKey())); 108 } 109 mPanDevices.clear(); 110 mIsTethering = false; 111 } 112 } 113 }; 114 115 static { classInitNative()116 classInitNative(); 117 } 118 isEnabled()119 public static boolean isEnabled() { 120 return BluetoothProperties.isProfilePanNapEnabled().orElse(false) 121 || BluetoothProperties.isProfilePanPanuEnabled().orElse(false); 122 } 123 124 @Override initBinder()125 public IProfileServiceBinder initBinder() { 126 return new BluetoothPanBinder(this); 127 } 128 getPanService()129 public static synchronized PanService getPanService() { 130 if (sPanService == null) { 131 Log.w(TAG, "getPanService(): service is null"); 132 return null; 133 } 134 if (!sPanService.isAvailable()) { 135 Log.w(TAG, "getPanService(): service is not available "); 136 return null; 137 } 138 return sPanService; 139 } 140 setPanService(PanService instance)141 private static synchronized void setPanService(PanService instance) { 142 if (DBG) { 143 Log.d(TAG, "setPanService(): set to: " + instance); 144 } 145 sPanService = instance; 146 } 147 148 @Override start()149 protected boolean start() { 150 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 151 "AdapterService cannot be null when PanService starts"); 152 mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(), 153 "DatabaseManager cannot be null when PanService starts"); 154 155 mBluetoothTetheringCallbacks = new HashMap<>(); 156 mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>(); 157 try { 158 mMaxPanDevices = getResources().getInteger( 159 com.android.bluetooth.R.integer.config_max_pan_devices); 160 } catch (NotFoundException e) { 161 mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; 162 } 163 initializeNative(); 164 mNativeAvailable = true; 165 166 mUserManager = getSystemService(UserManager.class); 167 168 mTetheringManager = getSystemService(TetheringManager.class); 169 mTetheringManager.registerTetheringEventCallback( 170 new HandlerExecutor(new Handler(Looper.getMainLooper())), mTetheringCallback); 171 setPanService(this); 172 mStarted = true; 173 174 return true; 175 } 176 177 @Override stop()178 protected boolean stop() { 179 if (!mStarted) { 180 Log.w(TAG, "stop() called before start()"); 181 return true; 182 } 183 if (mTetheringManager != null) { 184 mTetheringManager.unregisterTetheringEventCallback(mTetheringCallback); 185 mTetheringManager = null; 186 } 187 mHandler.removeCallbacksAndMessages(null); 188 return true; 189 } 190 191 @Override cleanup()192 protected void cleanup() { 193 // TODO(b/72948646): this should be moved to stop() 194 setPanService(null); 195 if (mNativeAvailable) { 196 cleanupNative(); 197 mNativeAvailable = false; 198 } 199 200 mUserManager = null; 201 202 if (mPanDevices != null) { 203 int[] desiredStates = {BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED, 204 BluetoothProfile.STATE_DISCONNECTING}; 205 List<BluetoothDevice> devList = 206 getDevicesMatchingConnectionStates(desiredStates); 207 for (BluetoothDevice device : devList) { 208 BluetoothPanDevice panDevice = mPanDevices.get(device); 209 Log.d(TAG, "panDevice: " + panDevice + " device address: " + device); 210 if (panDevice != null) { 211 handlePanDeviceStateChange(device, mPanIfName, 212 BluetoothProfile.STATE_DISCONNECTED, 213 panDevice.mLocalRole, panDevice.mRemoteRole); 214 } 215 } 216 mPanDevices.clear(); 217 } 218 } 219 220 private final Handler mHandler = new Handler() { 221 @Override 222 public void handleMessage(Message msg) { 223 switch (msg.what) { 224 case MESSAGE_CONNECT: { 225 BluetoothDevice device = (BluetoothDevice) msg.obj; 226 if (!connectPanNative(mAdapterService.getByteIdentityAddress(device), 227 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) { 228 handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING, 229 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); 230 handlePanDeviceStateChange(device, null, 231 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE, 232 BluetoothPan.REMOTE_NAP_ROLE); 233 break; 234 } 235 } 236 break; 237 case MESSAGE_DISCONNECT: { 238 BluetoothDevice device = (BluetoothDevice) msg.obj; 239 if (!disconnectPanNative(mAdapterService.getByteIdentityAddress(device))) { 240 handlePanDeviceStateChange(device, mPanIfName, 241 BluetoothProfile.STATE_DISCONNECTING, BluetoothPan.LOCAL_PANU_ROLE, 242 BluetoothPan.REMOTE_NAP_ROLE); 243 handlePanDeviceStateChange(device, mPanIfName, 244 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE, 245 BluetoothPan.REMOTE_NAP_ROLE); 246 break; 247 } 248 } 249 break; 250 case MESSAGE_CONNECT_STATE_CHANGED: { 251 ConnectState cs = (ConnectState) msg.obj; 252 final BluetoothDevice device = mAdapterService.getDeviceFromByte(cs.addr); 253 // TBD get iface from the msg 254 if (DBG) { 255 Log.d(TAG, 256 "MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state); 257 } 258 // It could be null if the connection up is coming when the Bluetooth is turning 259 // off. 260 if (device == null) { 261 break; 262 } 263 handlePanDeviceStateChange(device, mPanIfName /* iface */, 264 convertHalState(cs.state), cs.local_role, cs.remote_role); 265 } 266 break; 267 } 268 } 269 }; 270 271 /** 272 * Handlers for incoming service calls 273 */ 274 @VisibleForTesting 275 static class BluetoothPanBinder extends IBluetoothPan.Stub 276 implements IProfileServiceBinder { 277 private PanService mService; 278 BluetoothPanBinder(PanService svc)279 BluetoothPanBinder(PanService svc) { 280 mService = svc; 281 } 282 283 @Override cleanup()284 public void cleanup() { 285 mService = null; 286 } 287 288 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)289 private PanService getService(AttributionSource source) { 290 if (Utils.isInstrumentationTestMode()) { 291 return mService; 292 } 293 if (!Utils.checkServiceAvailable(mService, TAG) 294 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 295 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 296 return null; 297 } 298 return mService; 299 } 300 301 @Override connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)302 public void connect(BluetoothDevice device, AttributionSource source, 303 SynchronousResultReceiver receiver) { 304 try { 305 PanService service = getService(source); 306 boolean defaultValue = false; 307 if (service != null) { 308 defaultValue = service.connect(device); 309 } 310 receiver.send(defaultValue); 311 } catch (RuntimeException e) { 312 receiver.propagateException(e); 313 } 314 } 315 316 @Override disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)317 public void disconnect(BluetoothDevice device, AttributionSource source, 318 SynchronousResultReceiver receiver) { 319 try { 320 PanService service = getService(source); 321 boolean defaultValue = false; 322 if (service != null) { 323 defaultValue = service.disconnect(device); 324 } 325 receiver.send(defaultValue); 326 } catch (RuntimeException e) { 327 receiver.propagateException(e); 328 } 329 } 330 331 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver)332 public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, 333 AttributionSource source, SynchronousResultReceiver receiver) { 334 try { 335 PanService service = getService(source); 336 boolean defaultValue = false; 337 if (service != null) { 338 defaultValue = service.setConnectionPolicy(device, connectionPolicy); 339 } 340 receiver.send(defaultValue); 341 } catch (RuntimeException e) { 342 receiver.propagateException(e); 343 } 344 } 345 346 @Override getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)347 public void getConnectionState(BluetoothDevice device, AttributionSource source, 348 SynchronousResultReceiver receiver) { 349 try { 350 PanService service = getService(source); 351 int defaultValue = BluetoothPan.STATE_DISCONNECTED; 352 if (service != null) { 353 defaultValue = service.getConnectionState(device); 354 } 355 receiver.send(defaultValue); 356 } catch (RuntimeException e) { 357 receiver.propagateException(e); 358 } 359 } 360 361 @Override isTetheringOn(AttributionSource source, SynchronousResultReceiver receiver)362 public void isTetheringOn(AttributionSource source, SynchronousResultReceiver receiver) { 363 // TODO(BT) have a variable marking the on/off state 364 try { 365 PanService service = getService(source); 366 boolean defaultValue = false; 367 if (service != null) { 368 defaultValue = service.isTetheringOn(); 369 } 370 receiver.send(defaultValue); 371 } catch (RuntimeException e) { 372 receiver.propagateException(e); 373 } 374 } 375 376 @Override setBluetoothTethering(IBluetoothPanCallback callback, int id, boolean value, AttributionSource source, SynchronousResultReceiver receiver)377 public void setBluetoothTethering(IBluetoothPanCallback callback, int id, 378 boolean value, AttributionSource source, 379 SynchronousResultReceiver receiver) { 380 try { 381 PanService service = getService(source); 382 if (service != null) { 383 Log.d(TAG, "setBluetoothTethering: " + value 384 + ", pkgName: " + source.getPackageName() 385 + ", mTetherOn: " + service.mTetherOn); 386 service.setBluetoothTethering(callback, id, source.getUid(), value); 387 } 388 receiver.send(null); 389 } catch (RuntimeException e) { 390 receiver.propagateException(e); 391 } 392 } 393 394 @Override getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver)395 public void getConnectedDevices(AttributionSource source, 396 SynchronousResultReceiver receiver) { 397 try { 398 PanService service = getService(source); 399 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0); 400 if (service != null) { 401 defaultValue = service.getConnectedDevices(); 402 } 403 receiver.send(defaultValue); 404 } catch (RuntimeException e) { 405 receiver.propagateException(e); 406 } 407 } 408 409 @Override getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver)410 public void getDevicesMatchingConnectionStates(int[] states, 411 AttributionSource source, SynchronousResultReceiver receiver) { 412 try { 413 PanService service = getService(source); 414 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0); 415 if (service != null) { 416 defaultValue = service.getDevicesMatchingConnectionStates(states); 417 } 418 receiver.send(defaultValue); 419 } catch (RuntimeException e) { 420 receiver.propagateException(e); 421 } 422 } 423 } 424 425 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) connect(BluetoothDevice device)426 public boolean connect(BluetoothDevice device) { 427 if (mUserManager.isGuestUser()) { 428 Log.w(TAG, "Guest user does not have the permission to change the WiFi network"); 429 return false; 430 } 431 if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) { 432 Log.e(TAG, "Pan Device not disconnected: " + device); 433 return false; 434 } 435 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device); 436 mHandler.sendMessage(msg); 437 return true; 438 } 439 disconnect(BluetoothDevice device)440 public boolean disconnect(BluetoothDevice device) { 441 Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT, device); 442 mHandler.sendMessage(msg); 443 return true; 444 } 445 446 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getConnectionState(BluetoothDevice device)447 public int getConnectionState(BluetoothDevice device) { 448 enforceCallingOrSelfPermission( 449 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 450 BluetoothPanDevice panDevice = mPanDevices.get(device); 451 if (panDevice == null) { 452 return BluetoothPan.STATE_DISCONNECTED; 453 } 454 return panDevice.mState; 455 } 456 isTetheringOn()457 public boolean isTetheringOn() { 458 // TODO(BT) have a variable marking the on/off state 459 return mTetherOn; 460 } 461 462 @RequiresPermission(allOf = { 463 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 464 android.Manifest.permission.TETHER_PRIVILEGED, 465 }) setBluetoothTethering(IBluetoothPanCallback callback, int id, int callerUid, boolean value)466 void setBluetoothTethering(IBluetoothPanCallback callback, int id, int callerUid, 467 boolean value) { 468 if (DBG) { 469 Log.d(TAG, "setBluetoothTethering: " + value + ", mTetherOn: " + mTetherOn); 470 } 471 enforceCallingOrSelfPermission( 472 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 473 enforceCallingOrSelfPermission( 474 TETHER_PRIVILEGED, "Need TETHER_PRIVILEGED permission"); 475 476 UserManager um = getSystemService(UserManager.class); 477 if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING) && value) { 478 throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user."); 479 } 480 final String key = id + "/" + callerUid; 481 if (callback != null) { 482 boolean keyExists = mBluetoothTetheringCallbacks.containsKey(key); 483 if (value) { 484 if (!keyExists) { 485 mBluetoothTetheringCallbacks.put(key, callback); 486 } else { 487 Log.e(TAG, "setBluetoothTethering Error: Callback already registered."); 488 return; 489 } 490 } else { 491 if (keyExists) { 492 mBluetoothTetheringCallbacks.remove(key); 493 } else { 494 Log.e(TAG, "setBluetoothTethering Error: Callback not registered."); 495 return; 496 } 497 } 498 } 499 if (mTetherOn != value) { 500 //drop any existing panu or pan-nap connection when changing the tethering state 501 mTetherOn = value; 502 List<BluetoothDevice> devList = getConnectedDevices(); 503 for (BluetoothDevice dev : devList) { 504 disconnect(dev); 505 } 506 Intent intent = new Intent(BluetoothPan.ACTION_TETHERING_STATE_CHANGED); 507 intent.putExtra(BluetoothPan.EXTRA_TETHERING_STATE, 508 mTetherOn ? BluetoothPan.TETHERING_STATE_ON : BluetoothPan.TETHERING_STATE_OFF); 509 Utils.sendBroadcast(this, intent, null, Utils.getTempAllowlistBroadcastOptions()); 510 } 511 } 512 513 /** 514 * Set connection policy of the profile and connects it if connectionPolicy is 515 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is 516 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 517 * 518 * <p> The device should already be paired. 519 * Connection policy can be one of: 520 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 521 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 522 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 523 * 524 * @param device Paired bluetooth device 525 * @param connectionPolicy is the connection policy to set to for this profile 526 * @return true if connectionPolicy is set, false on error 527 */ 528 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) setConnectionPolicy(BluetoothDevice device, int connectionPolicy)529 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 530 enforceCallingOrSelfPermission( 531 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 532 if (DBG) { 533 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 534 } 535 536 if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.PAN, 537 connectionPolicy)) { 538 return false; 539 } 540 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 541 connect(device); 542 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 543 disconnect(device); 544 } 545 return true; 546 } 547 548 /** 549 * Get the connection policy of the profile. 550 * 551 * <p> The connection policy can be any of: 552 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 553 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 554 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 555 * 556 * @param device Bluetooth device 557 * @return connection policy of the device 558 * @hide 559 */ getConnectionPolicy(BluetoothDevice device)560 public int getConnectionPolicy(BluetoothDevice device) { 561 return mDatabaseManager 562 .getProfileConnectionPolicy(device, BluetoothProfile.PAN); 563 } 564 565 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getConnectedDevices()566 public List<BluetoothDevice> getConnectedDevices() { 567 enforceCallingOrSelfPermission( 568 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 569 List<BluetoothDevice> devices = 570 getDevicesMatchingConnectionStates(new int[]{BluetoothProfile.STATE_CONNECTED}); 571 return devices; 572 } 573 574 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getDevicesMatchingConnectionStates(int[] states)575 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 576 List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>(); 577 578 for (BluetoothDevice device : mPanDevices.keySet()) { 579 int panDeviceState = getConnectionState(device); 580 for (int state : states) { 581 if (state == panDeviceState) { 582 panDevices.add(device); 583 break; 584 } 585 } 586 } 587 return panDevices; 588 } 589 590 protected static class ConnectState { ConnectState(byte[] address, int state, int error, int localRole, int remoteRole)591 public ConnectState(byte[] address, int state, int error, int localRole, int remoteRole) { 592 this.addr = address; 593 this.state = state; 594 this.error = error; 595 this.local_role = localRole; 596 this.remote_role = remoteRole; 597 } 598 599 public byte[] addr; 600 public int state; 601 public int error; 602 public int local_role; 603 public int remote_role; 604 } 605 606 @VisibleForTesting onConnectStateChanged(byte[] address, int state, int error, int localRole, int remoteRole)607 void onConnectStateChanged(byte[] address, int state, int error, int localRole, 608 int remoteRole) { 609 if (DBG) { 610 Log.d(TAG, "onConnectStateChanged: " + state + ", local role:" + localRole 611 + ", remoteRole: " + remoteRole); 612 } 613 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED); 614 msg.obj = new ConnectState(address, state, error, localRole, remoteRole); 615 mHandler.sendMessage(msg); 616 } 617 618 @VisibleForTesting onControlStateChanged(int localRole, int state, int error, String ifname)619 void onControlStateChanged(int localRole, int state, int error, String ifname) { 620 if (DBG) { 621 Log.d(TAG, "onControlStateChanged: " + state + ", error: " + error + ", ifname: " 622 + ifname); 623 } 624 if (error == 0) { 625 mPanIfName = ifname; 626 } 627 } 628 629 @VisibleForTesting convertHalState(int halState)630 static int convertHalState(int halState) { 631 switch (halState) { 632 case CONN_STATE_CONNECTED: 633 return BluetoothProfile.STATE_CONNECTED; 634 case CONN_STATE_CONNECTING: 635 return BluetoothProfile.STATE_CONNECTING; 636 case CONN_STATE_DISCONNECTED: 637 return BluetoothProfile.STATE_DISCONNECTED; 638 case CONN_STATE_DISCONNECTING: 639 return BluetoothProfile.STATE_DISCONNECTING; 640 default: 641 Log.e(TAG, "bad pan connection state: " + halState); 642 return BluetoothProfile.STATE_DISCONNECTED; 643 } 644 } 645 handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, @LocalPanRole int localRole, @RemotePanRole int remoteRole)646 void handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, 647 @LocalPanRole int localRole, @RemotePanRole int remoteRole) { 648 if (DBG) { 649 Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface 650 + ", state: " + state + ", localRole:" + localRole + ", remoteRole:" 651 + remoteRole); 652 } 653 int prevState; 654 655 BluetoothPanDevice panDevice = mPanDevices.get(device); 656 if (panDevice == null) { 657 Log.i(TAG, "state " + state + " Num of connected pan devices: " + mPanDevices.size()); 658 prevState = BluetoothProfile.STATE_DISCONNECTED; 659 panDevice = new BluetoothPanDevice(state, iface, localRole, remoteRole); 660 mPanDevices.put(device, panDevice); 661 } else { 662 prevState = panDevice.mState; 663 panDevice.mState = state; 664 panDevice.mLocalRole = localRole; 665 panDevice.mRemoteRole = remoteRole; 666 panDevice.mIface = iface; 667 } 668 669 // Avoid race condition that gets this class stuck in STATE_DISCONNECTING. While we 670 // are in STATE_CONNECTING, if a BluetoothPan#disconnect call comes in, the original 671 // connect call will put us in STATE_DISCONNECTED. Then, the disconnect completes and 672 // changes the state to STATE_DISCONNECTING. All future calls to BluetoothPan#connect 673 // will fail until the caller explicitly calls BluetoothPan#disconnect. 674 if (prevState == BluetoothProfile.STATE_DISCONNECTED 675 && state == BluetoothProfile.STATE_DISCONNECTING) { 676 Log.d(TAG, "Ignoring state change from " + prevState + " to " + state); 677 mPanDevices.remove(device); 678 return; 679 } 680 681 Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state); 682 if (prevState == state) { 683 return; 684 } 685 if (remoteRole == BluetoothPan.LOCAL_PANU_ROLE) { 686 if (state == BluetoothProfile.STATE_CONNECTED) { 687 if ((!mTetherOn) || (localRole == BluetoothPan.LOCAL_PANU_ROLE)) { 688 Log.d(TAG, "handlePanDeviceStateChange BT tethering is off/Local role" 689 + " is PANU drop the connection"); 690 mPanDevices.remove(device); 691 disconnectPanNative(Utils.getByteAddress(device)); 692 return; 693 } 694 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE"); 695 if (!mIsTethering) { 696 mIsTethering = true; 697 try { 698 for (IBluetoothPanCallback cb : mBluetoothTetheringCallbacks.values()) { 699 cb.onAvailable(iface); 700 } 701 } catch (RemoteException e) { 702 throw e.rethrowFromSystemServer(); 703 } 704 } 705 } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 706 mPanDevices.remove(device); 707 Log.i(TAG, "remote(PANU) is disconnected, Remaining connected PANU devices: " 708 + mPanDevices.size()); 709 if (mIsTethering && mPanDevices.size() == 0) { 710 try { 711 for (IBluetoothPanCallback cb : mBluetoothTetheringCallbacks.values()) { 712 cb.onUnavailable(); 713 } 714 } catch (RemoteException e) { 715 throw e.rethrowFromSystemServer(); 716 } 717 mIsTethering = false; 718 } 719 } 720 } else if (mStarted) { 721 // PANU Role = reverse Tether 722 723 Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE state = " + state 724 + ", prevState = " + prevState); 725 if (state == BluetoothProfile.STATE_CONNECTED) { 726 mNetworkFactory = new BluetoothTetheringNetworkFactory( 727 getBaseContext(), getMainLooper(), this); 728 mNetworkFactory.startReverseTether(iface); 729 } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 730 if (mNetworkFactory != null) { 731 mNetworkFactory.stopReverseTether(); 732 mNetworkFactory = null; 733 } 734 mPanDevices.remove(device); 735 } 736 } 737 738 if (state == BluetoothProfile.STATE_CONNECTED) { 739 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.PAN); 740 } 741 /* Notifying the connection state change of the profile before sending the intent for 742 connection state change, as it was causing a race condition, with the UI not being 743 updated with the correct connection state. */ 744 Log.d(TAG, "Pan Device state : device: " + device + " State:" + prevState + "->" + state); 745 Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); 746 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 747 intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState); 748 intent.putExtra(BluetoothPan.EXTRA_STATE, state); 749 intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, localRole); 750 sendBroadcast(intent, BLUETOOTH_CONNECT); 751 } 752 753 @Override dump(StringBuilder sb)754 public void dump(StringBuilder sb) { 755 super.dump(sb); 756 println(sb, "mMaxPanDevices: " + mMaxPanDevices); 757 println(sb, "mPanIfName: " + mPanIfName); 758 println(sb, "mTetherOn: " + mTetherOn); 759 println(sb, "mPanDevices:"); 760 for (BluetoothDevice device : mPanDevices.keySet()) { 761 println(sb, " " + device + " : " + mPanDevices.get(device)); 762 } 763 } 764 765 @VisibleForTesting 766 static class BluetoothPanDevice { 767 private int mState; 768 private String mIface; 769 private int mLocalRole; // Which local role is this PAN device bound to 770 private int mRemoteRole; // Which remote role is this PAN device bound to 771 BluetoothPanDevice(int state, String iface, int localRole, int remoteRole)772 BluetoothPanDevice(int state, String iface, int localRole, int remoteRole) { 773 mState = state; 774 mIface = iface; 775 mLocalRole = localRole; 776 mRemoteRole = remoteRole; 777 } 778 } 779 780 // Constants matching Hal header file bt_hh.h 781 // bthh_connection_state_t 782 @VisibleForTesting 783 static final int CONN_STATE_CONNECTED = 0; 784 @VisibleForTesting 785 static final int CONN_STATE_CONNECTING = 1; 786 @VisibleForTesting 787 static final int CONN_STATE_DISCONNECTED = 2; 788 @VisibleForTesting 789 static final int CONN_STATE_DISCONNECTING = 3; 790 classInitNative()791 private static native void classInitNative(); 792 initializeNative()793 private native void initializeNative(); 794 cleanupNative()795 private native void cleanupNative(); 796 connectPanNative(byte[] btAddress, int localRole, int remoteRole)797 private native boolean connectPanNative(byte[] btAddress, int localRole, int remoteRole); 798 disconnectPanNative(byte[] btAddress)799 private native boolean disconnectPanNative(byte[] btAddress); 800 801 } 802