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