LocalBluetoothProfileManager.java revision 8140d9c9166990236e9b15cdefc96d348ba69a53
1/* 2 * Copyright (C) 2008 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.settings.bluetooth; 18 19import com.android.settings.R; 20 21import android.bluetooth.BluetoothA2dp; 22import android.bluetooth.BluetoothAdapter; 23import android.bluetooth.BluetoothDevice; 24import android.bluetooth.BluetoothHeadset; 25import android.bluetooth.BluetoothInputDevice; 26import android.bluetooth.BluetoothPan; 27import android.bluetooth.BluetoothProfile; 28import android.bluetooth.BluetoothUuid; 29import android.os.Handler; 30import android.os.ParcelUuid; 31import android.util.Log; 32 33import java.util.HashMap; 34import java.util.Iterator; 35import java.util.LinkedList; 36import java.util.List; 37import java.util.Map; 38 39/** 40 * LocalBluetoothProfileManager is an abstract class defining the basic 41 * functionality related to a profile. 42 */ 43abstract class LocalBluetoothProfileManager { 44 private static final String TAG = "LocalBluetoothProfileManager"; 45 46 /* package */ static final ParcelUuid[] HEADSET_PROFILE_UUIDS = new ParcelUuid[] { 47 BluetoothUuid.HSP, 48 BluetoothUuid.Handsfree, 49 }; 50 51 /* package */ static final ParcelUuid[] A2DP_SINK_PROFILE_UUIDS = new ParcelUuid[] { 52 BluetoothUuid.AudioSink, 53 BluetoothUuid.AdvAudioDist, 54 }; 55 56 /* package */ static final ParcelUuid[] A2DP_SRC_PROFILE_UUIDS = new ParcelUuid[] { 57 BluetoothUuid.AudioSource 58 }; 59 60 /* package */ static final ParcelUuid[] OPP_PROFILE_UUIDS = new ParcelUuid[] { 61 BluetoothUuid.ObexObjectPush 62 }; 63 64 /* package */ static final ParcelUuid[] HID_PROFILE_UUIDS = new ParcelUuid[] { 65 BluetoothUuid.Hid 66 }; 67 68 /* package */ static final ParcelUuid[] PANU_PROFILE_UUIDS = new ParcelUuid[] { 69 BluetoothUuid.PANU 70 }; 71 72 /* package */ static final ParcelUuid[] NAP_PROFILE_UUIDS = new ParcelUuid[] { 73 BluetoothUuid.NAP 74 }; 75 76 /** 77 * An interface for notifying BluetoothHeadset IPC clients when they have 78 * been connected to the BluetoothHeadset service. 79 */ 80 public interface ServiceListener { 81 /** 82 * Called to notify the client when this proxy object has been 83 * connected to the BluetoothHeadset service. Clients must wait for 84 * this callback before making IPC calls on the BluetoothHeadset 85 * service. 86 */ 87 public void onServiceConnected(); 88 89 /** 90 * Called to notify the client that this proxy object has been 91 * disconnected from the BluetoothHeadset service. Clients must not 92 * make IPC calls on the BluetoothHeadset service after this callback. 93 * This callback will currently only occur if the application hosting 94 * the BluetoothHeadset service, but may be called more often in future. 95 */ 96 public void onServiceDisconnected(); 97 } 98 99 // TODO: close profiles when we're shutting down 100 private static final Map<Profile, LocalBluetoothProfileManager> sProfileMap = 101 new HashMap<Profile, LocalBluetoothProfileManager>(); 102 103 protected final LocalBluetoothManager mLocalManager; 104 105 public static void init(LocalBluetoothManager localManager) { 106 synchronized (sProfileMap) { 107 if (sProfileMap.size() == 0) { 108 LocalBluetoothProfileManager profileManager; 109 110 profileManager = new A2dpProfileManager(localManager); 111 sProfileMap.put(Profile.A2DP, profileManager); 112 113 profileManager = new HeadsetProfileManager(localManager); 114 sProfileMap.put(Profile.HEADSET, profileManager); 115 116 profileManager = new OppProfileManager(localManager); 117 sProfileMap.put(Profile.OPP, profileManager); 118 119 profileManager = new HidProfileManager(localManager); 120 sProfileMap.put(Profile.HID, profileManager); 121 122 profileManager = new PanProfileManager(localManager); 123 sProfileMap.put(Profile.PAN, profileManager); 124 } 125 } 126 } 127 128 // TODO(): Combine the init and updateLocalProfiles codes. 129 // init can get called from various paths, it makes no sense to add and then delete. 130 public static void updateLocalProfiles(LocalBluetoothManager localManager, ParcelUuid[] uuids) { 131 if (!BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) && 132 !BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) { 133 sProfileMap.remove(Profile.HEADSET); 134 } 135 136 if (!BluetoothUuid.containsAnyUuid(uuids, A2DP_SRC_PROFILE_UUIDS)) { 137 sProfileMap.remove(Profile.A2DP); 138 } 139 140 if (!BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS)) { 141 sProfileMap.remove(Profile.OPP); 142 } 143 144 // There is no local SDP record for HID and Settings app doesn't control PBAP 145 } 146 147 private static final LinkedList<ServiceListener> mServiceListeners = 148 new LinkedList<ServiceListener>(); 149 150 public static void addServiceListener(ServiceListener l) { 151 mServiceListeners.add(l); 152 } 153 154 public static void removeServiceListener(ServiceListener l) { 155 mServiceListeners.remove(l); 156 } 157 158 public static boolean isManagerReady() { 159 // Getting just the headset profile is fine for now. Will need to deal with A2DP 160 // and others if they aren't always in a ready state. 161 LocalBluetoothProfileManager profileManager = sProfileMap.get(Profile.HEADSET); 162 if (profileManager == null) { 163 return sProfileMap.size() > 0; 164 } 165 return profileManager.isProfileReady(); 166 } 167 168 public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager, 169 Profile profile) { 170 // Note: This code assumes that "localManager" is same as the 171 // LocalBluetoothManager that was used to initialize the sProfileMap. 172 // If that every changes, we can't just keep one copy of sProfileMap. 173 synchronized (sProfileMap) { 174 LocalBluetoothProfileManager profileManager = sProfileMap.get(profile); 175 if (profileManager == null) { 176 Log.e(TAG, "profileManager can't be found for " + profile.toString()); 177 } 178 return profileManager; 179 } 180 } 181 182 /** 183 * Temporary method to fill profiles based on a device's class. 184 * 185 * NOTE: This list happens to define the connection order. We should put this logic in a more 186 * well known place when this method is no longer temporary. 187 * @param uuids of the remote device 188 * @param localUuids UUIDs of the local device 189 * @param profiles The list of profiles to fill 190 */ 191 public static void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids, 192 List<Profile> profiles) { 193 profiles.clear(); 194 195 if (uuids == null) { 196 return; 197 } 198 199 if (sProfileMap.containsKey(Profile.HEADSET)) { 200 if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) && 201 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) || 202 (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) && 203 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) { 204 profiles.add(Profile.HEADSET); 205 } 206 } 207 208 209 if (BluetoothUuid.containsAnyUuid(uuids, A2DP_SINK_PROFILE_UUIDS) && 210 sProfileMap.containsKey(Profile.A2DP)) { 211 profiles.add(Profile.A2DP); 212 } 213 214 if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS) && 215 sProfileMap.containsKey(Profile.OPP)) { 216 profiles.add(Profile.OPP); 217 } 218 219 if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS) && 220 sProfileMap.containsKey(Profile.HID)) { 221 profiles.add(Profile.HID); 222 } 223 224 if (BluetoothUuid.containsAnyUuid(uuids, NAP_PROFILE_UUIDS) && 225 sProfileMap.containsKey(Profile.PAN)) { 226 profiles.add(Profile.PAN); 227 } 228 } 229 230 protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) { 231 mLocalManager = localManager; 232 } 233 234 public abstract List<BluetoothDevice> getConnectedDevices(); 235 236 public abstract boolean connect(BluetoothDevice device); 237 238 public abstract boolean disconnect(BluetoothDevice device); 239 240 public abstract int getConnectionStatus(BluetoothDevice device); 241 242 public abstract int getSummary(BluetoothDevice device); 243 244 public abstract int convertState(int a2dpState); 245 246 public abstract boolean isPreferred(BluetoothDevice device); 247 248 public abstract int getPreferred(BluetoothDevice device); 249 250 public abstract void setPreferred(BluetoothDevice device, boolean preferred); 251 252 public boolean isConnected(BluetoothDevice device) { 253 return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device)); 254 } 255 256 public abstract boolean isProfileReady(); 257 258 public abstract int getDrawableResource(); 259 260 public static enum Profile { 261 HEADSET(R.string.bluetooth_profile_headset, true, true), 262 A2DP(R.string.bluetooth_profile_a2dp, true, true), 263 OPP(R.string.bluetooth_profile_opp, false, false), 264 HID(R.string.bluetooth_profile_hid, true, true), 265 PAN(R.string.bluetooth_profile_pan, true, false); 266 267 public final int localizedString; 268 private final boolean mConnectable; 269 private final boolean mAutoConnectable; 270 271 private Profile(int localizedString, boolean connectable, 272 boolean autoConnectable) { 273 this.localizedString = localizedString; 274 this.mConnectable = connectable; 275 this.mAutoConnectable = autoConnectable; 276 } 277 278 public boolean isConnectable() { 279 return mConnectable; 280 } 281 282 public boolean isAutoConnectable() { 283 return mAutoConnectable; 284 } 285 } 286 287 /** 288 * A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service. 289 */ 290 private static class A2dpProfileManager extends LocalBluetoothProfileManager 291 implements BluetoothProfile.ServiceListener { 292 private BluetoothA2dp mService; 293 294 // TODO(): The calls must wait for mService. Its not null just 295 // because it runs in the system server. 296 public A2dpProfileManager(LocalBluetoothManager localManager) { 297 super(localManager); 298 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 299 adapter.getProfileProxy(localManager.getContext(), this, BluetoothProfile.A2DP); 300 301 } 302 303 public void onServiceConnected(int profile, BluetoothProfile proxy) { 304 mService = (BluetoothA2dp) proxy; 305 } 306 307 public void onServiceDisconnected(int profile) { 308 mService = null; 309 } 310 311 @Override 312 public List<BluetoothDevice> getConnectedDevices() { 313 return mService.getDevicesMatchingConnectionStates( 314 new int[] {BluetoothProfile.STATE_CONNECTED, 315 BluetoothProfile.STATE_CONNECTING, 316 BluetoothProfile.STATE_DISCONNECTING}); 317 } 318 319 @Override 320 public boolean connect(BluetoothDevice device) { 321 List<BluetoothDevice> sinks = getConnectedDevices(); 322 if (sinks != null) { 323 for (BluetoothDevice sink : sinks) { 324 mService.disconnect(sink); 325 } 326 } 327 return mService.connect(device); 328 } 329 330 @Override 331 public boolean disconnect(BluetoothDevice device) { 332 // Downgrade priority as user is disconnecting the sink. 333 if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { 334 mService.setPriority(device, BluetoothProfile.PRIORITY_ON); 335 } 336 return mService.disconnect(device); 337 } 338 339 @Override 340 public int getConnectionStatus(BluetoothDevice device) { 341 return convertState(mService.getConnectionState(device)); 342 } 343 344 @Override 345 public int getSummary(BluetoothDevice device) { 346 int connectionStatus = getConnectionStatus(device); 347 348 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 349 return R.string.bluetooth_a2dp_profile_summary_connected; 350 } else { 351 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 352 } 353 } 354 355 @Override 356 public boolean isPreferred(BluetoothDevice device) { 357 return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; 358 } 359 360 @Override 361 public int getPreferred(BluetoothDevice device) { 362 return mService.getPriority(device); 363 } 364 365 @Override 366 public void setPreferred(BluetoothDevice device, boolean preferred) { 367 if (preferred) { 368 if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) { 369 mService.setPriority(device, BluetoothProfile.PRIORITY_ON); 370 } 371 } else { 372 mService.setPriority(device, BluetoothProfile.PRIORITY_OFF); 373 } 374 } 375 376 @Override 377 public int convertState(int a2dpState) { 378 switch (a2dpState) { 379 case BluetoothProfile.STATE_CONNECTED: 380 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 381 case BluetoothProfile.STATE_CONNECTING: 382 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 383 case BluetoothProfile.STATE_DISCONNECTED: 384 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 385 case BluetoothProfile.STATE_DISCONNECTING: 386 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING; 387 case BluetoothA2dp.STATE_PLAYING: 388 return SettingsBtStatus.CONNECTION_STATUS_ACTIVE; 389 default: 390 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 391 } 392 } 393 394 @Override 395 public boolean isProfileReady() { 396 return true; 397 } 398 399 @Override 400 public int getDrawableResource() { 401 return R.drawable.ic_bt_headphones_a2dp; 402 } 403 } 404 405 /** 406 * HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service. 407 */ 408 private static class HeadsetProfileManager extends LocalBluetoothProfileManager 409 implements BluetoothProfile.ServiceListener { 410 private BluetoothHeadset mService; 411 private final Handler mUiHandler = new Handler(); 412 private boolean profileReady = false; 413 414 // TODO(): The calls must get queued if mService becomes null. 415 // It can happen when phone app crashes for some reason. 416 // All callers should have service listeners. Dock Service is the only 417 // one right now. 418 public HeadsetProfileManager(LocalBluetoothManager localManager) { 419 super(localManager); 420 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 421 adapter.getProfileProxy(localManager.getContext(), this, BluetoothProfile.HEADSET); 422 } 423 424 public void onServiceConnected(int profile, BluetoothProfile proxy) { 425 mService = (BluetoothHeadset) proxy; 426 profileReady = true; 427 // This could be called on a non-UI thread, funnel to UI thread. 428 mUiHandler.post(new Runnable() { 429 public void run() { 430 /* 431 * We just bound to the service, so refresh the UI of the 432 * headset device. 433 */ 434 List<BluetoothDevice> deviceList = mService.getConnectedDevices(); 435 if (deviceList.size() == 0) return; 436 437 mLocalManager.getCachedDeviceManager() 438 .onProfileStateChanged(deviceList.get(0), Profile.HEADSET, 439 BluetoothProfile.STATE_CONNECTED); 440 } 441 }); 442 443 if (mServiceListeners.size() > 0) { 444 Iterator<ServiceListener> it = mServiceListeners.iterator(); 445 while(it.hasNext()) { 446 it.next().onServiceConnected(); 447 } 448 } 449 } 450 451 public void onServiceDisconnected(int profile) { 452 mService = null; 453 profileReady = false; 454 if (mServiceListeners.size() > 0) { 455 Iterator<ServiceListener> it = mServiceListeners.iterator(); 456 while(it.hasNext()) { 457 it.next().onServiceDisconnected(); 458 } 459 } 460 } 461 462 @Override 463 public boolean isProfileReady() { 464 return profileReady; 465 } 466 467 @Override 468 public List<BluetoothDevice> getConnectedDevices() { 469 return mService.getConnectedDevices(); 470 } 471 472 @Override 473 public boolean connect(BluetoothDevice device) { 474 List<BluetoothDevice> sinks = getConnectedDevices(); 475 if (sinks != null) { 476 for (BluetoothDevice sink : sinks) { 477 mService.disconnect(sink); 478 } 479 } 480 return mService.connect(device); 481 } 482 483 @Override 484 public boolean disconnect(BluetoothDevice device) { 485 List<BluetoothDevice> deviceList = getConnectedDevices(); 486 if (deviceList.size() != 0 && deviceList.get(0).equals(device)) { 487 // Downgrade priority as user is disconnecting the headset. 488 if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { 489 mService.setPriority(device, BluetoothProfile.PRIORITY_ON); 490 } 491 return mService.disconnect(device); 492 } else { 493 return false; 494 } 495 } 496 497 @Override 498 public int getConnectionStatus(BluetoothDevice device) { 499 List<BluetoothDevice> deviceList = getConnectedDevices(); 500 501 return deviceList.size() > 0 && deviceList.get(0).equals(device) 502 ? convertState(mService.getConnectionState(device)) 503 : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 504 } 505 506 @Override 507 public int getSummary(BluetoothDevice device) { 508 int connectionStatus = getConnectionStatus(device); 509 510 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 511 return R.string.bluetooth_headset_profile_summary_connected; 512 } else { 513 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 514 } 515 } 516 517 @Override 518 public boolean isPreferred(BluetoothDevice device) { 519 return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; 520 } 521 522 @Override 523 public int getPreferred(BluetoothDevice device) { 524 return mService.getPriority(device); 525 } 526 527 @Override 528 public void setPreferred(BluetoothDevice device, boolean preferred) { 529 if (preferred) { 530 if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) { 531 mService.setPriority(device, BluetoothProfile.PRIORITY_ON); 532 } 533 } else { 534 mService.setPriority(device, BluetoothProfile.PRIORITY_OFF); 535 } 536 } 537 538 @Override 539 public int convertState(int headsetState) { 540 switch (headsetState) { 541 case BluetoothProfile.STATE_CONNECTED: 542 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 543 case BluetoothProfile.STATE_CONNECTING: 544 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 545 case BluetoothProfile.STATE_DISCONNECTED: 546 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 547 default: 548 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 549 } 550 } 551 552 @Override 553 public int getDrawableResource() { 554 return R.drawable.ic_bt_headset_hfp; 555 } 556 } 557 558 /** 559 * OppProfileManager 560 */ 561 private static class OppProfileManager extends LocalBluetoothProfileManager { 562 563 public OppProfileManager(LocalBluetoothManager localManager) { 564 super(localManager); 565 } 566 567 @Override 568 public List<BluetoothDevice> getConnectedDevices() { 569 return null; 570 } 571 572 @Override 573 public boolean connect(BluetoothDevice device) { 574 return false; 575 } 576 577 @Override 578 public boolean disconnect(BluetoothDevice device) { 579 return false; 580 } 581 582 @Override 583 public int getConnectionStatus(BluetoothDevice device) { 584 return -1; 585 } 586 587 @Override 588 public int getSummary(BluetoothDevice device) { 589 int connectionStatus = getConnectionStatus(device); 590 591 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 592 return R.string.bluetooth_opp_profile_summary_connected; 593 } else { 594 return R.string.bluetooth_opp_profile_summary_not_connected; 595 } 596 } 597 598 @Override 599 public boolean isPreferred(BluetoothDevice device) { 600 return false; 601 } 602 603 @Override 604 public int getPreferred(BluetoothDevice device) { 605 return -1; 606 } 607 608 @Override 609 public void setPreferred(BluetoothDevice device, boolean preferred) { 610 } 611 612 @Override 613 public boolean isProfileReady() { 614 return true; 615 } 616 617 @Override 618 public int convertState(int oppState) { 619 switch (oppState) { 620 case 0: 621 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 622 case 1: 623 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 624 case 2: 625 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 626 default: 627 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 628 } 629 } 630 631 @Override 632 public int getDrawableResource() { 633 return R.drawable.ic_bt_headphones_a2dp; 634 } 635 } 636 637 private static class HidProfileManager extends LocalBluetoothProfileManager { 638 private final BluetoothInputDevice mService; 639 640 public HidProfileManager(LocalBluetoothManager localManager) { 641 super(localManager); 642 mService = new BluetoothInputDevice(localManager.getContext()); 643 } 644 645 @Override 646 public boolean connect(BluetoothDevice device) { 647 return mService.connectInputDevice(device); 648 } 649 650 @Override 651 public int convertState(int hidState) { 652 switch (hidState) { 653 case BluetoothInputDevice.STATE_CONNECTED: 654 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 655 case BluetoothInputDevice.STATE_CONNECTING: 656 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 657 case BluetoothInputDevice.STATE_DISCONNECTED: 658 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 659 case BluetoothInputDevice.STATE_DISCONNECTING: 660 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING; 661 default: 662 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 663 } 664 } 665 666 @Override 667 public boolean disconnect(BluetoothDevice device) { 668 return mService.disconnectInputDevice(device); 669 } 670 671 @Override 672 public List<BluetoothDevice> getConnectedDevices() { 673 return mService.getConnectedInputDevices(); 674 } 675 676 @Override 677 public int getConnectionStatus(BluetoothDevice device) { 678 return convertState(mService.getInputDeviceState(device)); 679 } 680 681 @Override 682 public int getPreferred(BluetoothDevice device) { 683 return mService.getInputDevicePriority(device); 684 } 685 686 @Override 687 public int getSummary(BluetoothDevice device) { 688 final int connectionStatus = getConnectionStatus(device); 689 690 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 691 return R.string.bluetooth_hid_profile_summary_connected; 692 } else { 693 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 694 } 695 } 696 697 @Override 698 public boolean isPreferred(BluetoothDevice device) { 699 return mService.getInputDevicePriority(device) > BluetoothInputDevice.PRIORITY_OFF; 700 } 701 702 @Override 703 public boolean isProfileReady() { 704 return true; 705 } 706 707 @Override 708 public void setPreferred(BluetoothDevice device, boolean preferred) { 709 if (preferred) { 710 if (mService.getInputDevicePriority(device) < BluetoothInputDevice.PRIORITY_ON) { 711 mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON); 712 } 713 } else { 714 mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_OFF); 715 } 716 } 717 718 @Override 719 public int getDrawableResource() { 720 return R.drawable.ic_bt_keyboard_hid; 721 } 722 } 723 724 private static class PanProfileManager extends LocalBluetoothProfileManager { 725 private final BluetoothPan mService; 726 727 public PanProfileManager(LocalBluetoothManager localManager) { 728 super(localManager); 729 mService = new BluetoothPan(localManager.getContext()); 730 } 731 732 @Override 733 public boolean connect(BluetoothDevice device) { 734 List<BluetoothDevice> sinks = getConnectedDevices(); 735 if (sinks != null) { 736 for (BluetoothDevice sink : sinks) { 737 mService.disconnect(sink); 738 } 739 } 740 return mService.connect(device); 741 } 742 743 @Override 744 public int convertState(int panState) { 745 switch (panState) { 746 case BluetoothPan.STATE_CONNECTED: 747 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 748 case BluetoothPan.STATE_CONNECTING: 749 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 750 case BluetoothPan.STATE_DISCONNECTED: 751 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 752 case BluetoothPan.STATE_DISCONNECTING: 753 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING; 754 default: 755 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 756 } 757 } 758 759 @Override 760 public boolean disconnect(BluetoothDevice device) { 761 return mService.disconnect(device); 762 } 763 764 @Override 765 public int getSummary(BluetoothDevice device) { 766 final int connectionStatus = getConnectionStatus(device); 767 768 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 769 return R.string.bluetooth_pan_profile_summary_connected; 770 } else { 771 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 772 } 773 } 774 775 @Override 776 public boolean isProfileReady() { 777 return true; 778 } 779 780 @Override 781 public List<BluetoothDevice> getConnectedDevices() { 782 return mService.getConnectedDevices(); 783 } 784 785 @Override 786 public int getConnectionStatus(BluetoothDevice device) { 787 return convertState(mService.getPanDeviceState(device)); 788 } 789 790 @Override 791 public int getPreferred(BluetoothDevice device) { 792 return -1; 793 } 794 795 @Override 796 public boolean isPreferred(BluetoothDevice device) { 797 return true; 798 } 799 800 @Override 801 public void setPreferred(BluetoothDevice device, boolean preferred) { 802 // ignore: isPreferred is always true for PAN 803 return; 804 } 805 806 @Override 807 public int getDrawableResource() { 808 return R.drawable.ic_bt_network_pan; 809 } 810 } 811} 812