summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorJason Monk <jmonk@google.com>2015-02-24 13:23:29 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-02-24 13:23:31 +0000
commit08238bb6247a2c1d93aefd52a31f9d1ad5bc7ff0 (patch)
tree1a78184efe0e4dcb6dd49cdd5e1e04483c89c9bc /packages
parentfb66b8ff48491ce7f537955aedc143e9fb69cca1 (diff)
parent7ce96b9e610de2782ec5f2af806e7bc0f90c8578 (diff)
downloadframeworks_base-08238bb6247a2c1d93aefd52a31f9d1ad5bc7ff0.zip
frameworks_base-08238bb6247a2c1d93aefd52a31f9d1ad5bc7ff0.tar.gz
frameworks_base-08238bb6247a2c1d93aefd52a31f9d1ad5bc7ff0.tar.bz2
Merge "Move non-ui bt settings code to SettingsLib"
Diffstat (limited to 'packages')
-rw-r--r--packages/SettingsLib/res/drawable-hdpi/ic_bt_cellphone.pngbin0 -> 653 bytes
-rw-r--r--packages/SettingsLib/res/drawable-hdpi/ic_bt_headphones_a2dp.pngbin0 -> 631 bytes
-rw-r--r--packages/SettingsLib/res/drawable-hdpi/ic_bt_headset_hfp.pngbin0 -> 649 bytes
-rw-r--r--packages/SettingsLib/res/drawable-hdpi/ic_bt_misc_hid.pngbin0 -> 342 bytes
-rw-r--r--packages/SettingsLib/res/drawable-hdpi/ic_bt_network_pan.pngbin0 -> 528 bytes
-rw-r--r--packages/SettingsLib/res/drawable-hdpi/ic_bt_pointing_hid.pngbin0 -> 600 bytes
-rw-r--r--packages/SettingsLib/res/drawable-hdpi/ic_lockscreen_ime.pngbin0 -> 285 bytes
-rw-r--r--packages/SettingsLib/res/drawable-ldrtl-hdpi/ic_bt_cellphone.pngbin0 -> 4349 bytes
-rw-r--r--packages/SettingsLib/res/drawable-ldrtl-mdpi/ic_bt_cellphone.pngbin0 -> 3872 bytes
-rw-r--r--packages/SettingsLib/res/drawable-ldrtl-xhdpi/ic_bt_cellphone.pngbin0 -> 4781 bytes
-rw-r--r--packages/SettingsLib/res/drawable-mdpi/ic_bt_cellphone.pngbin0 -> 450 bytes
-rw-r--r--packages/SettingsLib/res/drawable-mdpi/ic_bt_headphones_a2dp.pngbin0 -> 444 bytes
-rw-r--r--packages/SettingsLib/res/drawable-mdpi/ic_bt_headset_hfp.pngbin0 -> 448 bytes
-rw-r--r--packages/SettingsLib/res/drawable-mdpi/ic_bt_misc_hid.pngbin0 -> 246 bytes
-rw-r--r--packages/SettingsLib/res/drawable-mdpi/ic_bt_network_pan.pngbin0 -> 418 bytes
-rw-r--r--packages/SettingsLib/res/drawable-mdpi/ic_bt_pointing_hid.pngbin0 -> 370 bytes
-rw-r--r--packages/SettingsLib/res/drawable-mdpi/ic_lockscreen_ime.pngbin0 -> 175 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xhdpi/ic_bt_cellphone.pngbin0 -> 792 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xhdpi/ic_bt_headphones_a2dp.pngbin0 -> 764 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xhdpi/ic_bt_headset_hfp.pngbin0 -> 771 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xhdpi/ic_bt_misc_hid.pngbin0 -> 220 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xhdpi/ic_bt_network_pan.pngbin0 -> 717 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xhdpi/ic_bt_pointing_hid.pngbin0 -> 713 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xhdpi/ic_lockscreen_ime.pngbin0 -> 254 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxhdpi/ic_bt_cellphone.pngbin0 -> 1091 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headphones_a2dp.pngbin0 -> 1031 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headset_hfp.pngbin0 -> 1045 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxhdpi/ic_bt_misc_hid.pngbin0 -> 367 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxhdpi/ic_bt_network_pan.pngbin0 -> 993 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxhdpi/ic_bt_pointing_hid.pngbin0 -> 947 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxhdpi/ic_lockscreen_ime.pngbin0 -> 327 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_cellphone.pngbin0 -> 1337 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headphones_a2dp.pngbin0 -> 1353 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headset_hfp.pngbin0 -> 1362 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_misc_hid.pngbin0 -> 305 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_network_pan.pngbin0 -> 1284 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_pointing_hid.pngbin0 -> 1178 bytes
-rw-r--r--packages/SettingsLib/res/drawable-xxxhdpi/ic_lockscreen_ime.pngbin0 -> 412 bytes
-rw-r--r--packages/SettingsLib/res/values/strings.xml83
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java214
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java30
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java169
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java86
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java362
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java787
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java172
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java227
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java201
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java216
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java122
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java71
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java383
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java213
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java89
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java183
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java152
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java43
57 files changed, 3803 insertions, 0 deletions
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_cellphone.png
new file mode 100644
index 0000000..6e29d23
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-hdpi/ic_bt_cellphone.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_headphones_a2dp.png
new file mode 100644
index 0000000..6110e9e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-hdpi/ic_bt_headphones_a2dp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_headset_hfp.png
new file mode 100644
index 0000000..6cca225
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-hdpi/ic_bt_headset_hfp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_misc_hid.png
new file mode 100644
index 0000000..6445f2a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-hdpi/ic_bt_misc_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_network_pan.png
new file mode 100644
index 0000000..78c0294
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-hdpi/ic_bt_network_pan.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-hdpi/ic_bt_pointing_hid.png
new file mode 100644
index 0000000..2fcc3b0
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-hdpi/ic_bt_pointing_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-hdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-hdpi/ic_lockscreen_ime.png
new file mode 100644
index 0000000..70d35bf
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-hdpi/ic_lockscreen_ime.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-ldrtl-hdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-ldrtl-hdpi/ic_bt_cellphone.png
new file mode 100644
index 0000000..2d9b75e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-ldrtl-hdpi/ic_bt_cellphone.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-ldrtl-mdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-ldrtl-mdpi/ic_bt_cellphone.png
new file mode 100644
index 0000000..b6ebe34
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-ldrtl-mdpi/ic_bt_cellphone.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-ldrtl-xhdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-ldrtl-xhdpi/ic_bt_cellphone.png
new file mode 100644
index 0000000..8b67b91
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-ldrtl-xhdpi/ic_bt_cellphone.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_cellphone.png
new file mode 100644
index 0000000..1fa0a3d
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mdpi/ic_bt_cellphone.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_headphones_a2dp.png
new file mode 100644
index 0000000..175bd78
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mdpi/ic_bt_headphones_a2dp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_headset_hfp.png
new file mode 100644
index 0000000..05b27e8
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mdpi/ic_bt_headset_hfp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_misc_hid.png
new file mode 100644
index 0000000..6e9f8ae
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mdpi/ic_bt_misc_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_network_pan.png
new file mode 100644
index 0000000..5f3371f
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mdpi/ic_bt_network_pan.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-mdpi/ic_bt_pointing_hid.png
new file mode 100644
index 0000000..539d77f
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mdpi/ic_bt_pointing_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-mdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-mdpi/ic_lockscreen_ime.png
new file mode 100644
index 0000000..3216776
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mdpi/ic_lockscreen_ime.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_cellphone.png
new file mode 100644
index 0000000..4f381ba
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_cellphone.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headphones_a2dp.png
new file mode 100644
index 0000000..c67127d
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headphones_a2dp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headset_hfp.png
new file mode 100644
index 0000000..d3b356b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_headset_hfp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_misc_hid.png
new file mode 100644
index 0000000..2d38129
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_misc_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_network_pan.png
new file mode 100644
index 0000000..fb76575
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_network_pan.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_pointing_hid.png
new file mode 100644
index 0000000..d8b68eb
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xhdpi/ic_bt_pointing_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xhdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-xhdpi/ic_lockscreen_ime.png
new file mode 100644
index 0000000..02cc3af
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xhdpi/ic_lockscreen_ime.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_cellphone.png
new file mode 100644
index 0000000..7805b7a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_cellphone.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headphones_a2dp.png
new file mode 100644
index 0000000..8127774
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headphones_a2dp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headset_hfp.png
new file mode 100644
index 0000000..84b8085
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_headset_hfp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_misc_hid.png
new file mode 100644
index 0000000..289d6ac
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_misc_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_network_pan.png
new file mode 100644
index 0000000..72bc804
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_network_pan.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_pointing_hid.png
new file mode 100644
index 0000000..e31ce2b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxhdpi/ic_bt_pointing_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxhdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-xxhdpi/ic_lockscreen_ime.png
new file mode 100644
index 0000000..f23b0e7
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxhdpi/ic_lockscreen_ime.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_cellphone.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_cellphone.png
new file mode 100644
index 0000000..1e12f96
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_cellphone.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headphones_a2dp.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headphones_a2dp.png
new file mode 100644
index 0000000..8b547d9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headphones_a2dp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headset_hfp.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headset_hfp.png
new file mode 100644
index 0000000..03c5033
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_headset_hfp.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_misc_hid.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_misc_hid.png
new file mode 100644
index 0000000..b9a9923
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_misc_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_network_pan.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_network_pan.png
new file mode 100644
index 0000000..989e1ab
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_network_pan.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_pointing_hid.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_pointing_hid.png
new file mode 100644
index 0000000..de8c389
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxxhdpi/ic_bt_pointing_hid.png
Binary files differ
diff --git a/packages/SettingsLib/res/drawable-xxxhdpi/ic_lockscreen_ime.png b/packages/SettingsLib/res/drawable-xxxhdpi/ic_lockscreen_ime.png
new file mode 100644
index 0000000..2eb8a92
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-xxxhdpi/ic_lockscreen_ime.png
Binary files differ
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f055a2c..870afeb 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -67,4 +67,87 @@
<!-- Status message of Wi-Fi when it is connected by a Wi-Fi assistant application. [CHAR LIMIT=NONE] -->
<string name="connected_via_wfa">Connected via Wi\u2011Fi assistant</string>
+
+ <!-- Bluetooth settings. Message when a device is disconnected -->
+ <string name="bluetooth_disconnected">Disconnected</string>
+ <!-- Bluetooth settings. Message when disconnecting from a device -->
+ <string name="bluetooth_disconnecting">Disconnecting\u2026</string>
+ <!-- Bluetooth settings. Message when connecting to a device -->
+ <string name="bluetooth_connecting">Connecting\u2026</string>
+ <!-- Bluetooth settings. Message when connected to a device. [CHAR LIMIT=40] -->
+ <string name="bluetooth_connected">Connected</string>
+
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the A2DP profile. -->
+ <string name="bluetooth_profile_a2dp">Media audio</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the headset or handsfree profile. -->
+ <string name="bluetooth_profile_headset">Phone audio</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the OPP profile. -->
+ <string name="bluetooth_profile_opp">File transfer</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the HID profile. -->
+ <string name="bluetooth_profile_hid">Input device</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (accessing Internet through remote device). [CHAR LIMIT=40] -->
+ <string name="bluetooth_profile_pan">Internet access</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PBAP profile. [CHAR LIMIT=40] -->
+ <string name="bluetooth_profile_pbap">Contact sharing</string>
+ <!-- Bluetooth settings. The user-visible summary string that is used whenever referring to the PBAP profile (sharing contacts). [CHAR LIMIT=60] -->
+ <string name="bluetooth_profile_pbap_summary">Use for contact sharing</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (sharing this device's Internet connection). [CHAR LIMIT=40] -->
+ <string name="bluetooth_profile_pan_nap">Internet connection sharing</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. -->
+ <string name="bluetooth_profile_map">Message Access</string>
+
+ <!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference when A2DP is connected. -->
+ <string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the headset checkbox preference when headset is connected. -->
+ <string name="bluetooth_headset_profile_summary_connected">Connected to phone audio</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the OPP checkbox preference when OPP is connected. -->
+ <string name="bluetooth_opp_profile_summary_connected">Connected to file transfer server</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the map checkbox preference when map is connected. -->
+ <string name="bluetooth_map_profile_summary_connected">Connected to map</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the OPP checkbox preference when OPP is not connected. -->
+ <string name="bluetooth_opp_profile_summary_not_connected">Not connected to file transfer server</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. -->
+ <string name="bluetooth_hid_profile_summary_connected">Connected to input device</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (user role). [CHAR LIMIT=25]-->
+ <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for Internet access</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (NAP role). [CHAR LIMIT=25]-->
+ <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local Internet connection with device</string>
+
+ <!-- Bluetooth settings. Connection options screen. The summary
+ for the PAN checkbox preference that describes how checking it
+ will set the PAN profile as preferred. -->
+ <string name="bluetooth_pan_profile_summary_use_for">Use for Internet access</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the map checkbox preference that describes how checking it will set the map profile as preferred. -->
+ <string name="bluetooth_map_profile_summary_use_for">Use for map</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference that describes how checking it will set the A2DP profile as preferred. -->
+ <string name="bluetooth_a2dp_profile_summary_use_for">Use for media audio</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the headset checkbox preference that describes how checking it will set the headset profile as preferred. -->
+ <string name="bluetooth_headset_profile_summary_use_for">Use for phone audio</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the OPP checkbox preference that describes how checking it will set the OPP profile as preferred. -->
+ <string name="bluetooth_opp_profile_summary_use_for">Use for file transfer</string>
+ <!-- Bluetooth settings. Connection options screen. The summary
+ for the HID checkbox preference that describes how checking it
+ will set the HID profile as preferred. -->
+ <string name="bluetooth_hid_profile_summary_use_for">Use for input</string>
+
+ <!-- Button text for accepting an incoming pairing request. [CHAR LIMIT=20] -->
+ <string name="bluetooth_pairing_accept">Pair</string>
+ <!-- Button text for accepting an incoming pairing request in all caps. [CHAR LIMIT=20] -->
+ <string name="bluetooth_pairing_accept_all_caps">PAIR</string>
+ <!-- Button text for declining an incoming pairing request. [CHAR LIMIT=20] -->
+ <string name="bluetooth_pairing_decline">Cancel</string>
+
+ <!-- Message in pairing dialogs. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_pairing_will_share_phonebook">Pairing grants access to your contacts and call history when connected.</string>
+ <!-- Message for the error dialog when BT pairing fails generically. -->
+ <string name="bluetooth_pairing_error_message">Couldn\'t pair with <xliff:g id="device_name">%1$s</xliff:g>.</string>
+
+ <!-- Message for the error dialog when BT pairing fails because the PIN /
+ Passkey entered is incorrect. -->
+ <string name="bluetooth_pairing_pin_error_message">Couldn\'t pair with <xliff:g id="device_name">%1$s</xliff:g> because of an incorrect PIN or passkey.</string>
+ <!-- Message for the error dialog when BT pairing fails because the other device is down. -->
+ <string name="bluetooth_pairing_device_down_error_message">Can\'t communicate with <xliff:g id="device_name">%1$s</xliff:g>.</string>
+ <!-- Message for the error dialog when BT pairing fails because the other device rejected the pairing. -->
+ <string name="bluetooth_pairing_rejected_error_message">Pairing rejected by <xliff:g id="device_name">%1$s</xliff:g>.</string>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
new file mode 100755
index 0000000..9608daa
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class A2dpProfile implements LocalBluetoothProfile {
+ private static final String TAG = "A2dpProfile";
+ private static boolean V = false;
+
+ private BluetoothA2dp mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+
+ static final ParcelUuid[] SINK_UUIDS = {
+ BluetoothUuid.AudioSink,
+ BluetoothUuid.AdvAudioDist,
+ };
+
+ static final String NAME = "A2DP";
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 1;
+
+ // These callbacks run on the main thread.
+ private final class A2dpServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothA2dp) proxy;
+ // We just bound to the service, so refresh the UI for any connected A2DP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "A2dpProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(A2dpProfile.this, BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ A2dpProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new A2dpServiceListener(),
+ BluetoothProfile.A2DP);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> sinks = getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ mService.disconnect(sink);
+ }
+ }
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ // Downgrade priority as user is disconnecting the headset.
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+ boolean isA2dpPlaying() {
+ if (mService == null) return false;
+ List<BluetoothDevice> sinks = mService.getConnectedDevices();
+ if (!sinks.isEmpty()) {
+ if (mService.isA2dpPlaying(sinks.get(0))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_a2dp;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_a2dp_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_a2dp_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_headphones_a2dp;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up A2DP proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
new file mode 100644
index 0000000..b802f58
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+
+/**
+ * BluetoothCallback provides a callback interface for the settings
+ * UI to receive events from {@link BluetoothEventManager}.
+ */
+public interface BluetoothCallback {
+ void onBluetoothStateChanged(int bluetoothState);
+ void onScanningStateChanged(boolean started);
+ void onDeviceAdded(CachedBluetoothDevice cachedDevice);
+ void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
+ void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
new file mode 100644
index 0000000..8dec86a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+/**
+ * BluetoothDeviceFilter contains a static method that returns a
+ * Filter object that returns whether or not the BluetoothDevice
+ * passed to it matches the specified filter type constant from
+ * {@link android.bluetooth.BluetoothDevicePicker}.
+ */
+public final class BluetoothDeviceFilter {
+ private static final String TAG = "BluetoothDeviceFilter";
+
+ /** The filter interface to external classes. */
+ public interface Filter {
+ boolean matches(BluetoothDevice device);
+ }
+
+ /** All filter singleton (referenced directly). */
+ public static final Filter ALL_FILTER = new AllFilter();
+
+ /** Bonded devices only filter (referenced directly). */
+ public static final Filter BONDED_DEVICE_FILTER = new BondedDeviceFilter();
+
+ /** Unbonded devices only filter (referenced directly). */
+ public static final Filter UNBONDED_DEVICE_FILTER = new UnbondedDeviceFilter();
+
+ /** Table of singleton filter objects. */
+ private static final Filter[] FILTERS = {
+ ALL_FILTER, // FILTER_TYPE_ALL
+ new AudioFilter(), // FILTER_TYPE_AUDIO
+ new TransferFilter(), // FILTER_TYPE_TRANSFER
+ new PanuFilter(), // FILTER_TYPE_PANU
+ new NapFilter() // FILTER_TYPE_NAP
+ };
+
+ /** Private constructor. */
+ private BluetoothDeviceFilter() {
+ }
+
+ /**
+ * Returns the singleton {@link Filter} object for the specified type,
+ * or {@link #ALL_FILTER} if the type value is out of range.
+ *
+ * @param filterType a constant from BluetoothDevicePicker
+ * @return a singleton object implementing the {@link Filter} interface.
+ */
+ public static Filter getFilter(int filterType) {
+ if (filterType >= 0 && filterType < FILTERS.length) {
+ return FILTERS[filterType];
+ } else {
+ Log.w(TAG, "Invalid filter type " + filterType + " for device picker");
+ return ALL_FILTER;
+ }
+ }
+
+ /** Filter that matches all devices. */
+ private static final class AllFilter implements Filter {
+ public boolean matches(BluetoothDevice device) {
+ return true;
+ }
+ }
+
+ /** Filter that matches only bonded devices. */
+ private static final class BondedDeviceFilter implements Filter {
+ public boolean matches(BluetoothDevice device) {
+ return device.getBondState() == BluetoothDevice.BOND_BONDED;
+ }
+ }
+
+ /** Filter that matches only unbonded devices. */
+ private static final class UnbondedDeviceFilter implements Filter {
+ public boolean matches(BluetoothDevice device) {
+ return device.getBondState() != BluetoothDevice.BOND_BONDED;
+ }
+ }
+
+ /** Parent class of filters based on UUID and/or Bluetooth class. */
+ private abstract static class ClassUuidFilter implements Filter {
+ abstract boolean matches(ParcelUuid[] uuids, BluetoothClass btClass);
+
+ public boolean matches(BluetoothDevice device) {
+ return matches(device.getUuids(), device.getBluetoothClass());
+ }
+ }
+
+ /** Filter that matches devices that support AUDIO profiles. */
+ private static final class AudioFilter extends ClassUuidFilter {
+ @Override
+ boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
+ if (uuids != null) {
+ if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS)) {
+ return true;
+ }
+ if (BluetoothUuid.containsAnyUuid(uuids, HeadsetProfile.UUIDS)) {
+ return true;
+ }
+ } else if (btClass != null) {
+ if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP) ||
+ btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /** Filter that matches devices that support Object Transfer. */
+ private static final class TransferFilter extends ClassUuidFilter {
+ @Override
+ boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
+ if (uuids != null) {
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
+ return true;
+ }
+ }
+ return btClass != null
+ && btClass.doesClassMatch(BluetoothClass.PROFILE_OPP);
+ }
+ }
+
+ /** Filter that matches devices that support PAN User (PANU) profile. */
+ private static final class PanuFilter extends ClassUuidFilter {
+ @Override
+ boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
+ if (uuids != null) {
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.PANU)) {
+ return true;
+ }
+ }
+ return btClass != null
+ && btClass.doesClassMatch(BluetoothClass.PROFILE_PANU);
+ }
+ }
+
+ /** Filter that matches devices that support NAP profile. */
+ private static final class NapFilter extends ClassUuidFilter {
+ @Override
+ boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
+ if (uuids != null) {
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP)) {
+ return true;
+ }
+ }
+ return btClass != null
+ && btClass.doesClassMatch(BluetoothClass.PROFILE_NAP);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java
new file mode 100644
index 0000000..acb7e7a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+/* Required to handle timeout notification when phone is suspended */
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+
+public class BluetoothDiscoverableTimeoutReceiver extends BroadcastReceiver {
+ private static final String TAG = "BluetoothDiscoverableTimeoutReceiver";
+
+ private static final String INTENT_DISCOVERABLE_TIMEOUT =
+ "android.bluetooth.intent.DISCOVERABLE_TIMEOUT";
+
+ public static void setDiscoverableAlarm(Context context, long alarmTime) {
+ Log.d(TAG, "setDiscoverableAlarm(): alarmTime = " + alarmTime);
+
+ Intent intent = new Intent(INTENT_DISCOVERABLE_TIMEOUT);
+ intent.setClass(context, BluetoothDiscoverableTimeoutReceiver.class);
+ PendingIntent pending = PendingIntent.getBroadcast(
+ context, 0, intent, 0);
+ AlarmManager alarmManager =
+ (AlarmManager) context.getSystemService (Context.ALARM_SERVICE);
+
+ if (pending != null) {
+ // Cancel any previous alarms that do the same thing.
+ alarmManager.cancel(pending);
+ Log.d(TAG, "setDiscoverableAlarm(): cancel prev alarm");
+ }
+ pending = PendingIntent.getBroadcast(
+ context, 0, intent, 0);
+
+ alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, pending);
+ }
+
+ public static void cancelDiscoverableAlarm(Context context) {
+ Log.d(TAG, "cancelDiscoverableAlarm(): Enter");
+
+ Intent intent = new Intent(INTENT_DISCOVERABLE_TIMEOUT);
+ intent.setClass(context, BluetoothDiscoverableTimeoutReceiver.class);
+ PendingIntent pending = PendingIntent.getBroadcast(
+ context, 0, intent, PendingIntent.FLAG_NO_CREATE);
+ if (pending != null) {
+ // Cancel any previous alarms that do the same thing.
+ AlarmManager alarmManager =
+ (AlarmManager) context.getSystemService (Context.ALARM_SERVICE);
+
+ alarmManager.cancel(pending);
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ LocalBluetoothAdapter localBluetoothAdapter = LocalBluetoothAdapter.getInstance();
+
+ if(localBluetoothAdapter != null &&
+ localBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
+ Log.d(TAG, "Disable discoverable...");
+
+ localBluetoothAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ } else {
+ Log.e(TAG, "localBluetoothAdapter is NULL!!");
+ }
+ }
+};
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
new file mode 100755
index 0000000..7c92368
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * BluetoothEventManager receives broadcasts and callbacks from the Bluetooth
+ * API and dispatches the event on the UI thread to the right class in the
+ * Settings.
+ */
+public final class BluetoothEventManager {
+ private static final String TAG = "BluetoothEventManager";
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private LocalBluetoothProfileManager mProfileManager;
+ private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter;
+ private final Map<String, Handler> mHandlerMap;
+ private Context mContext;
+
+ private final Collection<BluetoothCallback> mCallbacks =
+ new ArrayList<BluetoothCallback>();
+
+ interface Handler {
+ void onReceive(Context context, Intent intent, BluetoothDevice device);
+ }
+
+ void addHandler(String action, Handler handler) {
+ mHandlerMap.put(action, handler);
+ mAdapterIntentFilter.addAction(action);
+ }
+
+ void addProfileHandler(String action, Handler handler) {
+ mHandlerMap.put(action, handler);
+ mProfileIntentFilter.addAction(action);
+ }
+
+ // Set profile manager after construction due to circular dependency
+ void setProfileManager(LocalBluetoothProfileManager manager) {
+ mProfileManager = manager;
+ }
+
+ BluetoothEventManager(LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager, Context context) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mAdapterIntentFilter = new IntentFilter();
+ mProfileIntentFilter = new IntentFilter();
+ mHandlerMap = new HashMap<String, Handler>();
+ mContext = context;
+
+ // Bluetooth on/off broadcasts
+ addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
+
+ // Discovery broadcasts
+ addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
+ addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
+ addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
+ addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
+ addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
+
+ // Pairing broadcasts
+ addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
+ addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, new PairingCancelHandler());
+
+ // Fine-grained state broadcasts
+ addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
+ addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
+
+ // Dock event broadcasts
+ addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
+
+ mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter);
+ }
+
+ void registerProfileIntentReceiver() {
+ mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter);
+ }
+
+ /** Register to start receiving callbacks for Bluetooth events. */
+ public void registerCallback(BluetoothCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ /** Unregister to stop receiving callbacks for Bluetooth events. */
+ public void unregisterCallback(BluetoothCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ BluetoothDevice device = intent
+ .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ Handler handler = mHandlerMap.get(action);
+ if (handler != null) {
+ handler.onReceive(context, intent, device);
+ }
+ }
+ };
+
+ private class AdapterStateChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ // update local profiles and get paired devices
+ mLocalAdapter.setBluetoothStateInt(state);
+ // send callback to update UI and possibly start scanning
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onBluetoothStateChanged(state);
+ }
+ }
+ // Inform CachedDeviceManager that the adapter state has changed
+ mDeviceManager.onBluetoothStateChanged(state);
+ }
+ }
+
+ private class ScanningStateChangedHandler implements Handler {
+ private final boolean mStarted;
+
+ ScanningStateChangedHandler(boolean started) {
+ mStarted = started;
+ }
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onScanningStateChanged(mStarted);
+ }
+ }
+ mDeviceManager.onScanningStateChanged(mStarted);
+ }
+ }
+
+ private class DeviceFoundHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
+ BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
+ String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
+ // TODO Pick up UUID. They should be available for 2.1 devices.
+ // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
+ Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
+ + cachedDevice);
+ // callback to UI to create Preference for new device
+ dispatchDeviceAdded(cachedDevice);
+ }
+ cachedDevice.setRssi(rssi);
+ cachedDevice.setBtClass(btClass);
+ cachedDevice.setNewName(name);
+ cachedDevice.setVisible(true);
+ }
+ }
+
+ private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceAdded(cachedDevice);
+ }
+ }
+ }
+
+ private class DeviceDisappearedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.w(TAG, "received ACTION_DISAPPEARED for an unknown device: " + device);
+ return;
+ }
+ if (CachedBluetoothDeviceManager.onDeviceDisappeared(cachedDevice)) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceDeleted(cachedDevice);
+ }
+ }
+ }
+ }
+ }
+
+ private class NameChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ mDeviceManager.onDeviceNameUpdated(device);
+ }
+ }
+
+ private class BondStateChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ if (device == null) {
+ Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
+ return;
+ }
+ int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.ERROR);
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.w(TAG, "CachedBluetoothDevice for device " + device +
+ " not found, calling readPairedDevices().");
+ if (!readPairedDevices()) {
+ Log.e(TAG, "Got bonding state changed for " + device +
+ ", but we have no record of that device.");
+ return;
+ }
+ cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.e(TAG, "Got bonding state changed for " + device +
+ ", but device not added in cache.");
+ return;
+ }
+ }
+
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceBondStateChanged(cachedDevice, bondState);
+ }
+ }
+ cachedDevice.onBondingStateChanged(bondState);
+
+ if (bondState == BluetoothDevice.BOND_NONE) {
+ int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
+ BluetoothDevice.ERROR);
+
+ showUnbondMessage(context, cachedDevice.getName(), reason);
+ }
+ }
+
+ /**
+ * Called when we have reached the unbonded state.
+ *
+ * @param reason one of the error reasons from
+ * BluetoothDevice.UNBOND_REASON_*
+ */
+ private void showUnbondMessage(Context context, String name, int reason) {
+ int errorMsg;
+
+ switch(reason) {
+ case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
+ errorMsg = R.string.bluetooth_pairing_pin_error_message;
+ break;
+ case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
+ errorMsg = R.string.bluetooth_pairing_rejected_error_message;
+ break;
+ case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
+ errorMsg = R.string.bluetooth_pairing_device_down_error_message;
+ break;
+ case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
+ case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
+ case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
+ case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
+ errorMsg = R.string.bluetooth_pairing_error_message;
+ break;
+ default:
+ Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
+ return;
+ }
+ Utils.showError(context, name, errorMsg);
+ }
+ }
+
+ private class ClassChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ mDeviceManager.onBtClassChanged(device);
+ }
+ }
+
+ private class UuidChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ mDeviceManager.onUuidChanged(device);
+ }
+ }
+
+ private class PairingCancelHandler implements Handler {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ if (device == null) {
+ Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
+ return;
+ }
+ int errorMsg = R.string.bluetooth_pairing_error_message;
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ Utils.showError(context, cachedDevice.getName(), errorMsg);
+ }
+ }
+
+ private class DockEventHandler implements Handler {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ // Remove if unpair device upon undocking
+ int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
+ int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
+ if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice != null) {
+ cachedDevice.setVisible(false);
+ }
+ }
+ }
+ }
+ }
+ boolean readPairedDevices() {
+ Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
+ if (bondedDevices == null) {
+ return false;
+ }
+
+ boolean deviceAdded = false;
+ for (BluetoothDevice device : bondedDevices) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
+ dispatchDeviceAdded(cachedDevice);
+ deviceAdded = true;
+ }
+ }
+
+ return deviceAdded;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
new file mode 100755
index 0000000..ddcc49f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.ParcelUuid;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+import android.bluetooth.BluetoothAdapter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * CachedBluetoothDevice represents a remote Bluetooth device. It contains
+ * attributes of the device (such as the address, name, RSSI, etc.) and
+ * functionality that can be performed on the device (connect, pair, disconnect,
+ * etc.).
+ */
+public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
+ private static final String TAG = "CachedBluetoothDevice";
+ private static final boolean DEBUG = Utils.V;
+
+ private final Context mContext;
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final LocalBluetoothProfileManager mProfileManager;
+ private final BluetoothDevice mDevice;
+ private String mName;
+ private short mRssi;
+ private BluetoothClass mBtClass;
+ private HashMap<LocalBluetoothProfile, Integer> mProfileConnectionState;
+
+ private final List<LocalBluetoothProfile> mProfiles =
+ new ArrayList<LocalBluetoothProfile>();
+
+ // List of profiles that were previously in mProfiles, but have been removed
+ private final List<LocalBluetoothProfile> mRemovedProfiles =
+ new ArrayList<LocalBluetoothProfile>();
+
+ // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
+ private boolean mLocalNapRoleConnected;
+
+ private boolean mVisible;
+
+ private int mPhonebookPermissionChoice;
+
+ private int mMessagePermissionChoice;
+
+ private int mMessageRejectionCount;
+
+ private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
+
+ // Following constants indicate the user's choices of Phone book/message access settings
+ // User hasn't made any choice or settings app has wiped out the memory
+ public final static int ACCESS_UNKNOWN = 0;
+ // User has accepted the connection and let Settings app remember the decision
+ public final static int ACCESS_ALLOWED = 1;
+ // User has rejected the connection and let Settings app remember the decision
+ public final static int ACCESS_REJECTED = 2;
+
+ // How many times user should reject the connection to make the choice persist.
+ private final static int MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST = 2;
+
+ private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject";
+
+ /**
+ * When we connect to multiple profiles, we only want to display a single
+ * error even if they all fail. This tracks that state.
+ */
+ private boolean mIsConnectingErrorPossible;
+
+ /**
+ * Last time a bt profile auto-connect was attempted.
+ * If an ACTION_UUID intent comes in within
+ * MAX_UUID_DELAY_FOR_AUTO_CONNECT milliseconds, we will try auto-connect
+ * again with the new UUIDs
+ */
+ private long mConnectAttempted;
+
+ // See mConnectAttempted
+ private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
+
+ /** Auto-connect after pairing only if locally initiated. */
+ private boolean mConnectAfterPairing;
+
+ /**
+ * Describes the current device and profile for logging.
+ *
+ * @param profile Profile to describe
+ * @return Description of the device and profile
+ */
+ private String describe(LocalBluetoothProfile profile) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Address:").append(mDevice);
+ if (profile != null) {
+ sb.append(" Profile:").append(profile);
+ }
+
+ return sb.toString();
+ }
+
+ void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
+ if (Utils.D) {
+ Log.d(TAG, "onProfileStateChanged: profile " + profile +
+ " newProfileState " + newProfileState);
+ }
+ if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF)
+ {
+ if (Utils.D) Log.d(TAG, " BT Turninig Off...Profile conn state change ignored...");
+ return;
+ }
+ mProfileConnectionState.put(profile, newProfileState);
+ if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
+ if (profile instanceof MapProfile) {
+ profile.setPreferred(mDevice, true);
+ } else if (!mProfiles.contains(profile)) {
+ mRemovedProfiles.remove(profile);
+ mProfiles.add(profile);
+ if (profile instanceof PanProfile &&
+ ((PanProfile) profile).isLocalRoleNap(mDevice)) {
+ // Device doesn't support NAP, so remove PanProfile on disconnect
+ mLocalNapRoleConnected = true;
+ }
+ }
+ } else if (profile instanceof MapProfile &&
+ newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+ profile.setPreferred(mDevice, false);
+ } else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
+ ((PanProfile) profile).isLocalRoleNap(mDevice) &&
+ newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+ Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
+ mProfiles.remove(profile);
+ mRemovedProfiles.add(profile);
+ mLocalNapRoleConnected = false;
+ }
+ }
+
+ CachedBluetoothDevice(Context context,
+ LocalBluetoothAdapter adapter,
+ LocalBluetoothProfileManager profileManager,
+ BluetoothDevice device) {
+ mContext = context;
+ mLocalAdapter = adapter;
+ mProfileManager = profileManager;
+ mDevice = device;
+ mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>();
+ fillData();
+ }
+
+ public void disconnect() {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ disconnect(profile);
+ }
+ // Disconnect PBAP server in case its connected
+ // This is to ensure all the profiles are disconnected as some CK/Hs do not
+ // disconnect PBAP connection when HF connection is brought down
+ PbapServerProfile PbapProfile = mProfileManager.getPbapProfile();
+ if (PbapProfile.getConnectionStatus(mDevice) == BluetoothProfile.STATE_CONNECTED)
+ {
+ PbapProfile.disconnect(mDevice);
+ }
+ }
+
+ public void disconnect(LocalBluetoothProfile profile) {
+ if (profile.disconnect(mDevice)) {
+ if (Utils.D) {
+ Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile));
+ }
+ }
+ }
+
+ public void connect(boolean connectAllProfiles) {
+ if (!ensurePaired()) {
+ return;
+ }
+
+ mConnectAttempted = SystemClock.elapsedRealtime();
+ connectWithoutResettingTimer(connectAllProfiles);
+ }
+
+ void onBondingDockConnect() {
+ // Attempt to connect if UUIDs are available. Otherwise,
+ // we will connect when the ACTION_UUID intent arrives.
+ connect(false);
+ }
+
+ private void connectWithoutResettingTimer(boolean connectAllProfiles) {
+ // Try to initialize the profiles if they were not.
+ if (mProfiles.isEmpty()) {
+ // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
+ // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
+ // from bluetooth stack but ACTION.uuid is not sent yet.
+ // Eventually ACTION.uuid will be received which shall trigger the connection of the
+ // various profiles
+ // If UUIDs are not available yet, connect will be happen
+ // upon arrival of the ACTION_UUID intent.
+ Log.d(TAG, "No profiles. Maybe we will connect later");
+ return;
+ }
+
+ // Reset the only-show-one-error-dialog tracking variable
+ mIsConnectingErrorPossible = true;
+
+ int preferredProfiles = 0;
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
+ if (profile.isPreferred(mDevice)) {
+ ++preferredProfiles;
+ connectInt(profile);
+ }
+ }
+ }
+ if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
+
+ if (preferredProfiles == 0) {
+ connectAutoConnectableProfiles();
+ }
+ }
+
+ private void connectAutoConnectableProfiles() {
+ if (!ensurePaired()) {
+ return;
+ }
+ // Reset the only-show-one-error-dialog tracking variable
+ mIsConnectingErrorPossible = true;
+
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (profile.isAutoConnectable()) {
+ profile.setPreferred(mDevice, true);
+ connectInt(profile);
+ }
+ }
+ }
+
+ /**
+ * Connect this device to the specified profile.
+ *
+ * @param profile the profile to use with the remote device
+ */
+ public void connectProfile(LocalBluetoothProfile profile) {
+ mConnectAttempted = SystemClock.elapsedRealtime();
+ // Reset the only-show-one-error-dialog tracking variable
+ mIsConnectingErrorPossible = true;
+ connectInt(profile);
+ // Refresh the UI based on profile.connect() call
+ refresh();
+ }
+
+ synchronized void connectInt(LocalBluetoothProfile profile) {
+ if (!ensurePaired()) {
+ return;
+ }
+ if (profile.connect(mDevice)) {
+ if (Utils.D) {
+ Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
+ }
+ return;
+ }
+ Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
+ }
+
+ private boolean ensurePaired() {
+ if (getBondState() == BluetoothDevice.BOND_NONE) {
+ startPairing();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public boolean startPairing() {
+ // Pairing is unreliable while scanning, so cancel discovery
+ if (mLocalAdapter.isDiscovering()) {
+ mLocalAdapter.cancelDiscovery();
+ }
+
+ if (!mDevice.createBond()) {
+ return false;
+ }
+
+ mConnectAfterPairing = true; // auto-connect after pairing
+ return true;
+ }
+
+ /**
+ * Return true if user initiated pairing on this device. The message text is
+ * slightly different for local vs. remote initiated pairing dialogs.
+ */
+ boolean isUserInitiatedPairing() {
+ return mConnectAfterPairing;
+ }
+
+ public void unpair() {
+ int state = getBondState();
+
+ if (state == BluetoothDevice.BOND_BONDING) {
+ mDevice.cancelBondProcess();
+ }
+
+ if (state != BluetoothDevice.BOND_NONE) {
+ final BluetoothDevice dev = mDevice;
+ if (dev != null) {
+ final boolean successful = dev.removeBond();
+ if (successful) {
+ if (Utils.D) {
+ Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));
+ }
+ } else if (Utils.V) {
+ Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +
+ describe(null));
+ }
+ }
+ }
+ }
+
+ public int getProfileConnectionState(LocalBluetoothProfile profile) {
+ if (mProfileConnectionState == null ||
+ mProfileConnectionState.get(profile) == null) {
+ // If cache is empty make the binder call to get the state
+ int state = profile.getConnectionStatus(mDevice);
+ mProfileConnectionState.put(profile, state);
+ }
+ return mProfileConnectionState.get(profile);
+ }
+
+ public void clearProfileConnectionState ()
+ {
+ if (Utils.D) {
+ Log.d(TAG," Clearing all connection state for dev:" + mDevice.getName());
+ }
+ for (LocalBluetoothProfile profile :getProfiles()) {
+ mProfileConnectionState.put(profile, BluetoothProfile.STATE_DISCONNECTED);
+ }
+ }
+
+ // TODO: do any of these need to run async on a background thread?
+ private void fillData() {
+ fetchName();
+ fetchBtClass();
+ updateProfiles();
+ migratePhonebookPermissionChoice();
+ migrateMessagePermissionChoice();
+ fetchMessageRejectionCount();
+
+ mVisible = false;
+ dispatchAttributesChanged();
+ }
+
+ public BluetoothDevice getDevice() {
+ return mDevice;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Populate name from BluetoothDevice.ACTION_FOUND intent
+ */
+ void setNewName(String name) {
+ if (mName == null) {
+ mName = name;
+ if (mName == null || TextUtils.isEmpty(mName)) {
+ mName = mDevice.getAddress();
+ }
+ dispatchAttributesChanged();
+ }
+ }
+
+ /**
+ * user changes the device name
+ */
+ public void setName(String name) {
+ if (!mName.equals(name)) {
+ mName = name;
+ mDevice.setAlias(name);
+ dispatchAttributesChanged();
+ }
+ }
+
+ void refreshName() {
+ fetchName();
+ dispatchAttributesChanged();
+ }
+
+ private void fetchName() {
+ mName = mDevice.getAliasName();
+
+ if (TextUtils.isEmpty(mName)) {
+ mName = mDevice.getAddress();
+ if (DEBUG) Log.d(TAG, "Device has no name (yet), use address: " + mName);
+ }
+ }
+
+ void refresh() {
+ dispatchAttributesChanged();
+ }
+
+ public boolean isVisible() {
+ return mVisible;
+ }
+
+ public void setVisible(boolean visible) {
+ if (mVisible != visible) {
+ mVisible = visible;
+ dispatchAttributesChanged();
+ }
+ }
+
+ public int getBondState() {
+ return mDevice.getBondState();
+ }
+
+ void setRssi(short rssi) {
+ if (mRssi != rssi) {
+ mRssi = rssi;
+ dispatchAttributesChanged();
+ }
+ }
+
+ /**
+ * Checks whether we are connected to this device (any profile counts).
+ *
+ * @return Whether it is connected.
+ */
+ public boolean isConnected() {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ int status = getProfileConnectionState(profile);
+ if (status == BluetoothProfile.STATE_CONNECTED) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean isConnectedProfile(LocalBluetoothProfile profile) {
+ int status = getProfileConnectionState(profile);
+ return status == BluetoothProfile.STATE_CONNECTED;
+
+ }
+
+ public boolean isBusy() {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ int status = getProfileConnectionState(profile);
+ if (status == BluetoothProfile.STATE_CONNECTING
+ || status == BluetoothProfile.STATE_DISCONNECTING) {
+ return true;
+ }
+ }
+ return getBondState() == BluetoothDevice.BOND_BONDING;
+ }
+
+ /**
+ * Fetches a new value for the cached BT class.
+ */
+ private void fetchBtClass() {
+ mBtClass = mDevice.getBluetoothClass();
+ }
+
+ private boolean updateProfiles() {
+ ParcelUuid[] uuids = mDevice.getUuids();
+ if (uuids == null) return false;
+
+ ParcelUuid[] localUuids = mLocalAdapter.getUuids();
+ if (localUuids == null) return false;
+
+ /**
+ * Now we know if the device supports PBAP, update permissions...
+ */
+ processPhonebookAccess();
+
+ mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
+ mLocalNapRoleConnected, mDevice);
+
+ if (DEBUG) {
+ Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
+ BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
+
+ if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
+ Log.v(TAG, "UUID:");
+ for (ParcelUuid uuid : uuids) {
+ Log.v(TAG, " " + uuid);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Refreshes the UI for the BT class, including fetching the latest value
+ * for the class.
+ */
+ void refreshBtClass() {
+ fetchBtClass();
+ dispatchAttributesChanged();
+ }
+
+ /**
+ * Refreshes the UI when framework alerts us of a UUID change.
+ */
+ void onUuidChanged() {
+ updateProfiles();
+
+ if (DEBUG) {
+ Log.e(TAG, "onUuidChanged: Time since last connect"
+ + (SystemClock.elapsedRealtime() - mConnectAttempted));
+ }
+
+ /*
+ * If a connect was attempted earlier without any UUID, we will do the
+ * connect now.
+ */
+ if (!mProfiles.isEmpty()
+ && (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
+ .elapsedRealtime()) {
+ connectWithoutResettingTimer(false);
+ }
+ dispatchAttributesChanged();
+ }
+
+ void onBondingStateChanged(int bondState) {
+ if (bondState == BluetoothDevice.BOND_NONE) {
+ mProfiles.clear();
+ mConnectAfterPairing = false; // cancel auto-connect
+ setPhonebookPermissionChoice(ACCESS_UNKNOWN);
+ setMessagePermissionChoice(ACCESS_UNKNOWN);
+ mMessageRejectionCount = 0;
+ saveMessageRejectionCount();
+ }
+
+ refresh();
+
+ if (bondState == BluetoothDevice.BOND_BONDED) {
+ if (mDevice.isBluetoothDock()) {
+ onBondingDockConnect();
+ } else if (mConnectAfterPairing) {
+ connect(false);
+ }
+ mConnectAfterPairing = false;
+ }
+ }
+
+ void setBtClass(BluetoothClass btClass) {
+ if (btClass != null && mBtClass != btClass) {
+ mBtClass = btClass;
+ dispatchAttributesChanged();
+ }
+ }
+
+ public BluetoothClass getBtClass() {
+ return mBtClass;
+ }
+
+ public List<LocalBluetoothProfile> getProfiles() {
+ return Collections.unmodifiableList(mProfiles);
+ }
+
+ public List<LocalBluetoothProfile> getConnectableProfiles() {
+ List<LocalBluetoothProfile> connectableProfiles =
+ new ArrayList<LocalBluetoothProfile>();
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (profile.isConnectable()) {
+ connectableProfiles.add(profile);
+ }
+ }
+ return connectableProfiles;
+ }
+
+ public List<LocalBluetoothProfile> getRemovedProfiles() {
+ return mRemovedProfiles;
+ }
+
+ public void registerCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ public void unregisterCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ private void dispatchAttributesChanged() {
+ synchronized (mCallbacks) {
+ for (Callback callback : mCallbacks) {
+ callback.onDeviceAttributesChanged();
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return mDevice.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
+ return false;
+ }
+ return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
+ }
+
+ @Override
+ public int hashCode() {
+ return mDevice.getAddress().hashCode();
+ }
+
+ // This comparison uses non-final fields so the sort order may change
+ // when device attributes change (such as bonding state). Settings
+ // will completely refresh the device list when this happens.
+ public int compareTo(CachedBluetoothDevice another) {
+ // Connected above not connected
+ int comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
+ if (comparison != 0) return comparison;
+
+ // Paired above not paired
+ comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
+ (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
+ if (comparison != 0) return comparison;
+
+ // Visible above not visible
+ comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
+ if (comparison != 0) return comparison;
+
+ // Stronger signal above weaker signal
+ comparison = another.mRssi - mRssi;
+ if (comparison != 0) return comparison;
+
+ // Fallback on name
+ return mName.compareTo(another.mName);
+ }
+
+ public interface Callback {
+ void onDeviceAttributesChanged();
+ }
+
+ public int getPhonebookPermissionChoice() {
+ int permission = mDevice.getPhonebookAccessPermission();
+ if (permission == BluetoothDevice.ACCESS_ALLOWED) {
+ return ACCESS_ALLOWED;
+ } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
+ return ACCESS_REJECTED;
+ }
+ return ACCESS_UNKNOWN;
+ }
+
+ public void setPhonebookPermissionChoice(int permissionChoice) {
+ int permission = BluetoothDevice.ACCESS_UNKNOWN;
+ if (permissionChoice == ACCESS_ALLOWED) {
+ permission = BluetoothDevice.ACCESS_ALLOWED;
+ } else if (permissionChoice == ACCESS_REJECTED) {
+ permission = BluetoothDevice.ACCESS_REJECTED;
+ }
+ mDevice.setPhonebookAccessPermission(permission);
+ }
+
+ // Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
+ // app's shared preferences).
+ private void migratePhonebookPermissionChoice() {
+ SharedPreferences preferences = mContext.getSharedPreferences(
+ "bluetooth_phonebook_permission", Context.MODE_PRIVATE);
+ if (!preferences.contains(mDevice.getAddress())) {
+ return;
+ }
+
+ if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
+ int oldPermission = preferences.getInt(mDevice.getAddress(), ACCESS_UNKNOWN);
+ if (oldPermission == ACCESS_ALLOWED) {
+ mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+ } else if (oldPermission == ACCESS_REJECTED) {
+ mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
+ }
+ }
+
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.remove(mDevice.getAddress());
+ editor.commit();
+ }
+
+ public int getMessagePermissionChoice() {
+ int permission = mDevice.getMessageAccessPermission();
+ if (permission == BluetoothDevice.ACCESS_ALLOWED) {
+ return ACCESS_ALLOWED;
+ } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
+ return ACCESS_REJECTED;
+ }
+ return ACCESS_UNKNOWN;
+ }
+
+ public void setMessagePermissionChoice(int permissionChoice) {
+ int permission = BluetoothDevice.ACCESS_UNKNOWN;
+ if (permissionChoice == ACCESS_ALLOWED) {
+ permission = BluetoothDevice.ACCESS_ALLOWED;
+ } else if (permissionChoice == ACCESS_REJECTED) {
+ permission = BluetoothDevice.ACCESS_REJECTED;
+ }
+ mDevice.setMessageAccessPermission(permission);
+ }
+
+ // Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
+ // app's shared preferences).
+ private void migrateMessagePermissionChoice() {
+ SharedPreferences preferences = mContext.getSharedPreferences(
+ "bluetooth_message_permission", Context.MODE_PRIVATE);
+ if (!preferences.contains(mDevice.getAddress())) {
+ return;
+ }
+
+ if (mDevice.getMessageAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
+ int oldPermission = preferences.getInt(mDevice.getAddress(), ACCESS_UNKNOWN);
+ if (oldPermission == ACCESS_ALLOWED) {
+ mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+ } else if (oldPermission == ACCESS_REJECTED) {
+ mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
+ }
+ }
+
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.remove(mDevice.getAddress());
+ editor.commit();
+ }
+
+ /**
+ * @return Whether this rejection should persist.
+ */
+ public boolean checkAndIncreaseMessageRejectionCount() {
+ if (mMessageRejectionCount < MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST) {
+ mMessageRejectionCount++;
+ saveMessageRejectionCount();
+ }
+ return mMessageRejectionCount >= MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST;
+ }
+
+ private void fetchMessageRejectionCount() {
+ SharedPreferences preference = mContext.getSharedPreferences(
+ MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE);
+ mMessageRejectionCount = preference.getInt(mDevice.getAddress(), 0);
+ }
+
+ private void saveMessageRejectionCount() {
+ SharedPreferences.Editor editor = mContext.getSharedPreferences(
+ MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE).edit();
+ if (mMessageRejectionCount == 0) {
+ editor.remove(mDevice.getAddress());
+ } else {
+ editor.putInt(mDevice.getAddress(), mMessageRejectionCount);
+ }
+ editor.commit();
+ }
+
+ private void processPhonebookAccess() {
+ if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) return;
+
+ ParcelUuid[] uuids = mDevice.getUuids();
+ if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) {
+ // The pairing dialog now warns of phone-book access for paired devices.
+ // No separate prompt is displayed after pairing.
+ setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
new file mode 100755
index 0000000..65db95f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
+ */
+public final class CachedBluetoothDeviceManager {
+ private static final String TAG = "CachedBluetoothDeviceManager";
+ private static final boolean DEBUG = Utils.D;
+
+ private Context mContext;
+ private final List<CachedBluetoothDevice> mCachedDevices =
+ new ArrayList<CachedBluetoothDevice>();
+
+ CachedBluetoothDeviceManager(Context context) {
+ mContext = context;
+ }
+
+ public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
+ return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
+ }
+
+ public static boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) {
+ cachedDevice.setVisible(false);
+ return cachedDevice.getBondState() == BluetoothDevice.BOND_NONE;
+ }
+
+ public void onDeviceNameUpdated(BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = findDevice(device);
+ if (cachedDevice != null) {
+ cachedDevice.refreshName();
+ }
+ }
+
+ /**
+ * Search for existing {@link CachedBluetoothDevice} or return null
+ * if this device isn't in the cache. Use {@link #addDevice}
+ * to create and return a new {@link CachedBluetoothDevice} for
+ * a newly discovered {@link BluetoothDevice}.
+ *
+ * @param device the address of the Bluetooth device
+ * @return the cached device object for this device, or null if it has
+ * not been previously seen
+ */
+ public CachedBluetoothDevice findDevice(BluetoothDevice device) {
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ if (cachedDevice.getDevice().equals(device)) {
+ return cachedDevice;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Create and return a new {@link CachedBluetoothDevice}. This assumes
+ * that {@link #findDevice} has already been called and returned null.
+ * @param device the address of the new Bluetooth device
+ * @return the newly created CachedBluetoothDevice object
+ */
+ public CachedBluetoothDevice addDevice(LocalBluetoothAdapter adapter,
+ LocalBluetoothProfileManager profileManager,
+ BluetoothDevice device) {
+ CachedBluetoothDevice newDevice = new CachedBluetoothDevice(mContext, adapter,
+ profileManager, device);
+ synchronized (mCachedDevices) {
+ mCachedDevices.add(newDevice);
+ }
+ return newDevice;
+ }
+
+ /**
+ * Attempts to get the name of a remote device, otherwise returns the address.
+ *
+ * @param device The remote device.
+ * @return The name, or if unavailable, the address.
+ */
+ public String getName(BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = findDevice(device);
+ if (cachedDevice != null) {
+ return cachedDevice.getName();
+ }
+
+ String name = device.getAliasName();
+ if (name != null) {
+ return name;
+ }
+
+ return device.getAddress();
+ }
+
+ public synchronized void clearNonBondedDevices() {
+ for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+ CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+ if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
+ mCachedDevices.remove(i);
+ }
+ }
+ }
+
+ public synchronized void onScanningStateChanged(boolean started) {
+ if (!started) return;
+
+ // If starting a new scan, clear old visibility
+ // Iterate in reverse order since devices may be removed.
+ for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+ CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+ cachedDevice.setVisible(false);
+ }
+ }
+
+ public synchronized void onBtClassChanged(BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = findDevice(device);
+ if (cachedDevice != null) {
+ cachedDevice.refreshBtClass();
+ }
+ }
+
+ public synchronized void onUuidChanged(BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = findDevice(device);
+ if (cachedDevice != null) {
+ cachedDevice.onUuidChanged();
+ }
+ }
+
+ public synchronized void onBluetoothStateChanged(int bluetoothState) {
+ // When Bluetooth is turning off, we need to clear the non-bonded devices
+ // Otherwise, they end up showing up on the next BT enable
+ if (bluetoothState == BluetoothAdapter.STATE_TURNING_OFF) {
+ for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+ CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+ if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
+ cachedDevice.setVisible(false);
+ mCachedDevices.remove(i);
+ } else {
+ // For bonded devices, we need to clear the connection status so that
+ // when BT is enabled next time, device connection status shall be retrieved
+ // by making a binder call.
+ cachedDevice.clearProfileConnectionState();
+ }
+ }
+ }
+ }
+ private void log(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, msg);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
new file mode 100755
index 0000000..5529866
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * HeadsetProfile handles Bluetooth HFP and Headset profiles.
+ */
+public final class HeadsetProfile implements LocalBluetoothProfile {
+ private static final String TAG = "HeadsetProfile";
+ private static boolean V = true;
+
+ private BluetoothHeadset mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ static final ParcelUuid[] UUIDS = {
+ BluetoothUuid.HSP,
+ BluetoothUuid.Handsfree,
+ };
+
+ static final String NAME = "HEADSET";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 0;
+
+ // These callbacks run on the main thread.
+ private final class HeadsetServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothHeadset) proxy;
+ // We just bound to the service, so refresh the UI for any connected HFP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "HeadsetProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(HeadsetProfile.this,
+ BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mProfileManager.callServiceDisconnectedListeners();
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ HeadsetProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new HeadsetServiceListener(),
+ BluetoothProfile.HEADSET);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> sinks = mService.getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ Log.d(TAG,"Not disconnecting device = " + sink);
+ }
+ }
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ if (!deviceList.isEmpty()) {
+ for (BluetoothDevice dev : deviceList) {
+ if (dev.equals(device)) {
+ if (V) Log.d(TAG,"Downgrade priority as user" +
+ "is disconnecting the headset");
+ // Downgrade priority as user is disconnecting the headset.
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ }
+ }
+ }
+ return false;
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ if (!deviceList.isEmpty()){
+ for (BluetoothDevice dev : deviceList) {
+ if (dev.equals(device)) {
+ return mService.getConnectionState(device);
+ }
+ }
+ }
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_headset;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_headset_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_headset_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_headset_hfp;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEADSET,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up HID proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
new file mode 100755
index 0000000..a9e8db5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.List;
+
+/**
+ * HidProfile handles Bluetooth HID profile.
+ */
+public final class HidProfile implements LocalBluetoothProfile {
+ private static final String TAG = "HidProfile";
+ private static boolean V = true;
+
+ private BluetoothInputDevice mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ static final String NAME = "HID";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 3;
+
+ // These callbacks run on the main thread.
+ private final class InputDeviceServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothInputDevice) proxy;
+ // We just bound to the service, so refresh the UI for any connected HID devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "HidProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(HidProfile.this, BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ HidProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ adapter.getProfileProxy(context, new InputDeviceServiceListener(),
+ BluetoothProfile.INPUT_DEVICE);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+
+ return !deviceList.isEmpty() && deviceList.get(0).equals(device)
+ ? mService.getConnectionState(device)
+ : BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ // TODO: distinguish between keyboard and mouse?
+ return R.string.bluetooth_profile_hid;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_hid_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_hid_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ if (btClass == null) {
+ return R.drawable.ic_lockscreen_ime;
+ }
+ return getHidClassDrawable(btClass);
+ }
+
+ public static int getHidClassDrawable(BluetoothClass btClass) {
+ switch (btClass.getDeviceClass()) {
+ case BluetoothClass.Device.PERIPHERAL_KEYBOARD:
+ case BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING:
+ return R.drawable.ic_lockscreen_ime;
+ case BluetoothClass.Device.PERIPHERAL_POINTING:
+ return R.drawable.ic_bt_pointing_hid;
+ default:
+ return R.drawable.ic_bt_misc_hid;
+ }
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.INPUT_DEVICE,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up HID proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
new file mode 100644
index 0000000..0c1adec
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import java.util.Set;
+
+/**
+ * LocalBluetoothAdapter provides an interface between the Settings app
+ * and the functionality of the local {@link BluetoothAdapter}, specifically
+ * those related to state transitions of the adapter itself.
+ *
+ * <p>Connection and bonding state changes affecting specific devices
+ * are handled by {@link CachedBluetoothDeviceManager},
+ * {@link BluetoothEventManager}, and {@link LocalBluetoothProfileManager}.
+ */
+public final class LocalBluetoothAdapter {
+ private static final String TAG = "LocalBluetoothAdapter";
+
+ /** This class does not allow direct access to the BluetoothAdapter. */
+ private final BluetoothAdapter mAdapter;
+
+ private LocalBluetoothProfileManager mProfileManager;
+
+ private static LocalBluetoothAdapter sInstance;
+
+ private int mState = BluetoothAdapter.ERROR;
+
+ private static final int SCAN_EXPIRATION_MS = 5 * 60 * 1000; // 5 mins
+
+ private long mLastScan;
+
+ private LocalBluetoothAdapter(BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ void setProfileManager(LocalBluetoothProfileManager manager) {
+ mProfileManager = manager;
+ }
+
+ /**
+ * Get the singleton instance of the LocalBluetoothAdapter. If this device
+ * doesn't support Bluetooth, then null will be returned. Callers must be
+ * prepared to handle a null return value.
+ * @return the LocalBluetoothAdapter object, or null if not supported
+ */
+ static synchronized LocalBluetoothAdapter getInstance() {
+ if (sInstance == null) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ sInstance = new LocalBluetoothAdapter(adapter);
+ }
+ }
+
+ return sInstance;
+ }
+
+ // Pass-through BluetoothAdapter methods that we can intercept if necessary
+
+ public void cancelDiscovery() {
+ mAdapter.cancelDiscovery();
+ }
+
+ public boolean enable() {
+ return mAdapter.enable();
+ }
+
+ public boolean disable() {
+ return mAdapter.disable();
+ }
+
+ void getProfileProxy(Context context,
+ BluetoothProfile.ServiceListener listener, int profile) {
+ mAdapter.getProfileProxy(context, listener, profile);
+ }
+
+ public Set<BluetoothDevice> getBondedDevices() {
+ return mAdapter.getBondedDevices();
+ }
+
+ public String getName() {
+ return mAdapter.getName();
+ }
+
+ public int getScanMode() {
+ return mAdapter.getScanMode();
+ }
+
+ public int getState() {
+ return mAdapter.getState();
+ }
+
+ public ParcelUuid[] getUuids() {
+ return mAdapter.getUuids();
+ }
+
+ public boolean isDiscovering() {
+ return mAdapter.isDiscovering();
+ }
+
+ public boolean isEnabled() {
+ return mAdapter.isEnabled();
+ }
+
+ public void setDiscoverableTimeout(int timeout) {
+ mAdapter.setDiscoverableTimeout(timeout);
+ }
+
+ public void setName(String name) {
+ mAdapter.setName(name);
+ }
+
+ public void setScanMode(int mode) {
+ mAdapter.setScanMode(mode);
+ }
+
+ public boolean setScanMode(int mode, int duration) {
+ return mAdapter.setScanMode(mode, duration);
+ }
+
+ public void startScanning(boolean force) {
+ // Only start if we're not already scanning
+ if (!mAdapter.isDiscovering()) {
+ if (!force) {
+ // Don't scan more than frequently than SCAN_EXPIRATION_MS,
+ // unless forced
+ if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
+ return;
+ }
+
+ // If we are playing music, don't scan unless forced.
+ A2dpProfile a2dp = mProfileManager.getA2dpProfile();
+ if (a2dp != null && a2dp.isA2dpPlaying()) {
+ return;
+ }
+ }
+
+ if (mAdapter.startDiscovery()) {
+ mLastScan = System.currentTimeMillis();
+ }
+ }
+ }
+
+ public void stopScanning() {
+ if (mAdapter.isDiscovering()) {
+ mAdapter.cancelDiscovery();
+ }
+ }
+
+ public synchronized int getBluetoothState() {
+ // Always sync state, in case it changed while paused
+ syncBluetoothState();
+ return mState;
+ }
+
+ synchronized void setBluetoothStateInt(int state) {
+ mState = state;
+
+ if (state == BluetoothAdapter.STATE_ON) {
+ // if mProfileManager hasn't been constructed yet, it will
+ // get the adapter UUIDs in its constructor when it is.
+ if (mProfileManager != null) {
+ mProfileManager.setBluetoothStateOn();
+ }
+ }
+ }
+
+ // Returns true if the state changed; false otherwise.
+ boolean syncBluetoothState() {
+ int currentState = mAdapter.getState();
+ if (currentState != mState) {
+ setBluetoothStateInt(mAdapter.getState());
+ return true;
+ }
+ return false;
+ }
+
+ public void setBluetoothEnabled(boolean enabled) {
+ boolean success = enabled
+ ? mAdapter.enable()
+ : mAdapter.disable();
+
+ if (success) {
+ setBluetoothStateInt(enabled
+ ? BluetoothAdapter.STATE_TURNING_ON
+ : BluetoothAdapter.STATE_TURNING_OFF);
+ } else {
+ if (Utils.V) {
+ Log.v(TAG, "setBluetoothEnabled call, manager didn't return " +
+ "success for enabled: " + enabled);
+ }
+
+ syncBluetoothState();
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
new file mode 100644
index 0000000..4adc62e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * LocalBluetoothManager provides a simplified interface on top of a subset of
+ * the Bluetooth API. Note that {@link #getInstance} will return null
+ * if there is no Bluetooth adapter on this device, and callers must be
+ * prepared to handle this case.
+ */
+public final class LocalBluetoothManager {
+ private static final String TAG = "LocalBluetoothManager";
+
+ /** Singleton instance. */
+ private static LocalBluetoothManager sInstance;
+
+ private final Context mContext;
+
+ /** If a BT-related activity is in the foreground, this will be it. */
+ private Context mForegroundActivity;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+
+ private final CachedBluetoothDeviceManager mCachedDeviceManager;
+
+ /** The Bluetooth profile manager. */
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ /** The broadcast receiver event manager. */
+ private final BluetoothEventManager mEventManager;
+
+ public static synchronized LocalBluetoothManager getInstance(Context context,
+ BluetoothManagerCallback onInitCallback) {
+ if (sInstance == null) {
+ LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
+ if (adapter == null) {
+ return null;
+ }
+ // This will be around as long as this process is
+ Context appContext = context.getApplicationContext();
+ sInstance = new LocalBluetoothManager(adapter, appContext);
+ if (onInitCallback != null) {
+ onInitCallback.onBluetoothManagerInitialized(appContext, sInstance);
+ }
+ }
+
+ return sInstance;
+ }
+
+ private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) {
+ mContext = context;
+ mLocalAdapter = adapter;
+
+ mCachedDeviceManager = new CachedBluetoothDeviceManager(context);
+ mEventManager = new BluetoothEventManager(mLocalAdapter,
+ mCachedDeviceManager, context);
+ mProfileManager = new LocalBluetoothProfileManager(context,
+ mLocalAdapter, mCachedDeviceManager, mEventManager);
+ }
+
+ public LocalBluetoothAdapter getBluetoothAdapter() {
+ return mLocalAdapter;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public Context getForegroundActivity() {
+ return mForegroundActivity;
+ }
+
+ public boolean isForegroundActivity() {
+ return mForegroundActivity != null;
+ }
+
+ public synchronized void setForegroundActivity(Context context) {
+ if (context != null) {
+ Log.d(TAG, "setting foreground activity to non-null context");
+ mForegroundActivity = context;
+ } else {
+ if (mForegroundActivity != null) {
+ Log.d(TAG, "setting foreground activity to null");
+ mForegroundActivity = null;
+ }
+ }
+ }
+
+ public CachedBluetoothDeviceManager getCachedDeviceManager() {
+ return mCachedDeviceManager;
+ }
+
+ public BluetoothEventManager getEventManager() {
+ return mEventManager;
+ }
+
+ public LocalBluetoothProfileManager getProfileManager() {
+ return mProfileManager;
+ }
+
+ public interface BluetoothManagerCallback {
+ void onBluetoothManagerInitialized(Context appContext,
+ LocalBluetoothManager bluetoothManager);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
new file mode 100755
index 0000000..abcb989
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * LocalBluetoothProfile is an interface defining the basic
+ * functionality related to a Bluetooth profile.
+ */
+public interface LocalBluetoothProfile {
+
+ /**
+ * Returns true if the user can initiate a connection, false otherwise.
+ */
+ boolean isConnectable();
+
+ /**
+ * Returns true if the user can enable auto connection for this profile.
+ */
+ boolean isAutoConnectable();
+
+ boolean connect(BluetoothDevice device);
+
+ boolean disconnect(BluetoothDevice device);
+
+ int getConnectionStatus(BluetoothDevice device);
+
+ boolean isPreferred(BluetoothDevice device);
+
+ int getPreferred(BluetoothDevice device);
+
+ void setPreferred(BluetoothDevice device, boolean preferred);
+
+ boolean isProfileReady();
+
+ /** Display order for device profile settings. */
+ int getOrdinal();
+
+ /**
+ * Returns the string resource ID for the localized name for this profile.
+ * @param device the Bluetooth device (to distinguish between PAN roles)
+ */
+ int getNameResource(BluetoothDevice device);
+
+ /**
+ * Returns the string resource ID for the summary text for this profile
+ * for the specified device, e.g. "Use for media audio" or
+ * "Connected to media audio".
+ * @param device the device to query for profile connection status
+ * @return a string resource ID for the profile summary text
+ */
+ int getSummaryResourceForDevice(BluetoothDevice device);
+
+ int getDrawableResource(BluetoothClass btClass);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
new file mode 100644
index 0000000..b0a7b27
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothMap;
+import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothPbap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.content.Intent;
+import android.os.ParcelUuid;
+import android.util.Log;
+import android.os.Handler;
+import android.os.Message;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+
+/**
+ * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile
+ * objects for the available Bluetooth profiles.
+ */
+public final class LocalBluetoothProfileManager {
+ private static final String TAG = "LocalBluetoothProfileManager";
+ private static final boolean DEBUG = Utils.D;
+ /** Singleton instance. */
+ private static LocalBluetoothProfileManager sInstance;
+
+ /**
+ * An interface for notifying BluetoothHeadset IPC clients when they have
+ * been connected to the BluetoothHeadset service.
+ * Only used by com.android.settings.bluetooth.DockService.
+ */
+ public interface ServiceListener {
+ /**
+ * Called to notify the client when this proxy object has been
+ * connected to the BluetoothHeadset service. Clients must wait for
+ * this callback before making IPC calls on the BluetoothHeadset
+ * service.
+ */
+ void onServiceConnected();
+
+ /**
+ * Called to notify the client that this proxy object has been
+ * disconnected from the BluetoothHeadset service. Clients must not
+ * make IPC calls on the BluetoothHeadset service after this callback.
+ * This callback will currently only occur if the application hosting
+ * the BluetoothHeadset service, but may be called more often in future.
+ */
+ void onServiceDisconnected();
+ }
+
+ private final Context mContext;
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final BluetoothEventManager mEventManager;
+
+ private A2dpProfile mA2dpProfile;
+ private HeadsetProfile mHeadsetProfile;
+ private MapProfile mMapProfile;
+ private final HidProfile mHidProfile;
+ private OppProfile mOppProfile;
+ private final PanProfile mPanProfile;
+ private final PbapServerProfile mPbapProfile;
+
+ /**
+ * Mapping from profile name, e.g. "HEADSET" to profile object.
+ */
+ private final Map<String, LocalBluetoothProfile>
+ mProfileNameMap = new HashMap<String, LocalBluetoothProfile>();
+
+ LocalBluetoothProfileManager(Context context,
+ LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ BluetoothEventManager eventManager) {
+ mContext = context;
+
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mEventManager = eventManager;
+ // pass this reference to adapter and event manager (circular dependency)
+ mLocalAdapter.setProfileManager(this);
+ mEventManager.setProfileManager(this);
+
+ ParcelUuid[] uuids = adapter.getUuids();
+
+ // uuids may be null if Bluetooth is turned off
+ if (uuids != null) {
+ updateLocalProfiles(uuids);
+ }
+
+ // Always add HID and PAN profiles
+ mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
+ addProfile(mHidProfile, HidProfile.NAME,
+ BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
+
+ mPanProfile = new PanProfile(context);
+ addPanProfile(mPanProfile, PanProfile.NAME,
+ BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
+
+ if(DEBUG) Log.d(TAG, "Adding local MAP profile");
+ mMapProfile = new MapProfile(mContext, mLocalAdapter,
+ mDeviceManager, this);
+ addProfile(mMapProfile, MapProfile.NAME,
+ BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
+
+ //Create PBAP server profile, but do not add it to list of profiles
+ // as we do not need to monitor the profile as part of profile list
+ mPbapProfile = new PbapServerProfile(context);
+
+ if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
+ }
+
+ /**
+ * Initialize or update the local profile objects. If a UUID was previously
+ * present but has been removed, we print a warning but don't remove the
+ * profile object as it might be referenced elsewhere, or the UUID might
+ * come back and we don't want multiple copies of the profile objects.
+ * @param uuids
+ */
+ void updateLocalProfiles(ParcelUuid[] uuids) {
+ // A2DP
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
+ if (mA2dpProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local A2DP profile");
+ mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mA2dpProfile, A2dpProfile.NAME,
+ BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mA2dpProfile != null) {
+ Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
+ }
+
+ // Headset / Handsfree
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
+ if (mHeadsetProfile == null) {
+ if (DEBUG) Log.d(TAG, "Adding local HEADSET profile");
+ mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
+ mDeviceManager, this);
+ addProfile(mHeadsetProfile, HeadsetProfile.NAME,
+ BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mHeadsetProfile != null) {
+ Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
+ }
+
+ // OPP
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
+ if (mOppProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local OPP profile");
+ mOppProfile = new OppProfile();
+ // Note: no event handler for OPP, only name map.
+ mProfileNameMap.put(OppProfile.NAME, mOppProfile);
+ }
+ } else if (mOppProfile != null) {
+ Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
+ }
+ mEventManager.registerProfileIntentReceiver();
+
+ // There is no local SDP record for HID and Settings app doesn't control PBAP
+ }
+
+ private final Collection<ServiceListener> mServiceListeners =
+ new ArrayList<ServiceListener>();
+
+ private void addProfile(LocalBluetoothProfile profile,
+ String profileName, String stateChangedAction) {
+ mEventManager.addProfileHandler(stateChangedAction, new StateChangedHandler(profile));
+ mProfileNameMap.put(profileName, profile);
+ }
+
+ private void addPanProfile(LocalBluetoothProfile profile,
+ String profileName, String stateChangedAction) {
+ mEventManager.addProfileHandler(stateChangedAction,
+ new PanStateChangedHandler(profile));
+ mProfileNameMap.put(profileName, profile);
+ }
+
+ public LocalBluetoothProfile getProfileByName(String name) {
+ return mProfileNameMap.get(name);
+ }
+
+ // Called from LocalBluetoothAdapter when state changes to ON
+ void setBluetoothStateOn() {
+ ParcelUuid[] uuids = mLocalAdapter.getUuids();
+ if (uuids != null) {
+ updateLocalProfiles(uuids);
+ }
+ mEventManager.readPairedDevices();
+ }
+
+ /**
+ * Generic handler for connection state change events for the specified profile.
+ */
+ private class StateChangedHandler implements BluetoothEventManager.Handler {
+ final LocalBluetoothProfile mProfile;
+
+ StateChangedHandler(LocalBluetoothProfile profile) {
+ mProfile = profile;
+ }
+
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.w(TAG, "StateChangedHandler found new device: " + device);
+ cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
+ LocalBluetoothProfileManager.this, device);
+ }
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+ int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+ if (newState == BluetoothProfile.STATE_DISCONNECTED &&
+ oldState == BluetoothProfile.STATE_CONNECTING) {
+ Log.i(TAG, "Failed to connect " + mProfile + " device");
+ }
+
+ cachedDevice.onProfileStateChanged(mProfile, newState);
+ cachedDevice.refresh();
+ }
+ }
+
+ /** State change handler for NAP and PANU profiles. */
+ private class PanStateChangedHandler extends StateChangedHandler {
+
+ PanStateChangedHandler(LocalBluetoothProfile profile) {
+ super(profile);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ PanProfile panProfile = (PanProfile) mProfile;
+ int role = intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, 0);
+ panProfile.setLocalRole(device, role);
+ super.onReceive(context, intent, device);
+ }
+ }
+
+ // called from DockService
+ public void addServiceListener(ServiceListener l) {
+ mServiceListeners.add(l);
+ }
+
+ // called from DockService
+ public void removeServiceListener(ServiceListener l) {
+ mServiceListeners.remove(l);
+ }
+
+ // not synchronized: use only from UI thread! (TODO: verify)
+ void callServiceConnectedListeners() {
+ for (ServiceListener l : mServiceListeners) {
+ l.onServiceConnected();
+ }
+ }
+
+ // not synchronized: use only from UI thread! (TODO: verify)
+ void callServiceDisconnectedListeners() {
+ for (ServiceListener listener : mServiceListeners) {
+ listener.onServiceDisconnected();
+ }
+ }
+
+ // This is called by DockService, so check Headset and A2DP.
+ public synchronized boolean isManagerReady() {
+ // Getting just the headset profile is fine for now. Will need to deal with A2DP
+ // and others if they aren't always in a ready state.
+ LocalBluetoothProfile profile = mHeadsetProfile;
+ if (profile != null) {
+ return profile.isProfileReady();
+ }
+ profile = mA2dpProfile;
+ if (profile != null) {
+ return profile.isProfileReady();
+ }
+ return false;
+ }
+
+ public A2dpProfile getA2dpProfile() {
+ return mA2dpProfile;
+ }
+
+ public HeadsetProfile getHeadsetProfile() {
+ return mHeadsetProfile;
+ }
+
+ public PbapServerProfile getPbapProfile(){
+ return mPbapProfile;
+ }
+
+ public MapProfile getMapProfile(){
+ return mMapProfile;
+ }
+
+ /**
+ * Fill in a list of LocalBluetoothProfile objects that are supported by
+ * the local device and the remote device.
+ *
+ * @param uuids of the remote device
+ * @param localUuids UUIDs of the local device
+ * @param profiles The list of profiles to fill
+ * @param removedProfiles list of profiles that were removed
+ */
+ synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
+ Collection<LocalBluetoothProfile> profiles,
+ Collection<LocalBluetoothProfile> removedProfiles,
+ boolean isPanNapConnected, BluetoothDevice device) {
+ // Copy previous profile list into removedProfiles
+ removedProfiles.clear();
+ removedProfiles.addAll(profiles);
+ profiles.clear();
+
+ if (uuids == null) {
+ return;
+ }
+
+ if (mHeadsetProfile != null) {
+ if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
+ (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
+ profiles.add(mHeadsetProfile);
+ removedProfiles.remove(mHeadsetProfile);
+ }
+ }
+
+ if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
+ mA2dpProfile != null) {
+ profiles.add(mA2dpProfile);
+ removedProfiles.remove(mA2dpProfile);
+ }
+
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
+ mOppProfile != null) {
+ profiles.add(mOppProfile);
+ removedProfiles.remove(mOppProfile);
+ }
+
+ if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) ||
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) &&
+ mHidProfile != null) {
+ profiles.add(mHidProfile);
+ removedProfiles.remove(mHidProfile);
+ }
+
+ if(isPanNapConnected)
+ if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
+ if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
+ mPanProfile != null) || isPanNapConnected) {
+ profiles.add(mPanProfile);
+ removedProfiles.remove(mPanProfile);
+ }
+
+ if ((mMapProfile != null) &&
+ (mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
+ profiles.add(mMapProfile);
+ removedProfiles.remove(mMapProfile);
+ mMapProfile.setPreferred(device, true);
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
new file mode 100644
index 0000000..e6a152f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MapProfile handles Bluetooth MAP profile.
+ */
+public final class MapProfile implements LocalBluetoothProfile {
+ private static final String TAG = "MapProfile";
+ private static boolean V = true;
+
+ private BluetoothMap mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ static final ParcelUuid[] UUIDS = {
+ BluetoothUuid.MAP,
+ BluetoothUuid.MNS,
+ BluetoothUuid.MAS,
+ };
+
+ static final String NAME = "MAP";
+
+ // Order of this profile in device profiles list
+
+ // These callbacks run on the main thread.
+ private final class MapServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothMap) proxy;
+ // We just bound to the service, so refresh the UI for any connected MAP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "MapProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(MapProfile.this,
+ BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mProfileManager.callServiceDisconnectedListeners();
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+ return mIsProfileReady;
+ }
+
+ MapProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new MapServiceListener(),
+ BluetoothProfile.MAP);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if(V)Log.d(TAG,"connect() - should not get called");
+ return false; // MAP never connects out
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ } else {
+ return false;
+ }
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ if(V) Log.d(TAG,"getConnectionStatus: status is: "+ mService.getConnectionState(device));
+
+ return !deviceList.isEmpty() && deviceList.get(0).equals(device)
+ ? mService.getConnectionState(device)
+ : BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return BluetoothProfile.MAP;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_map;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_map_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_map_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up MAP proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
new file mode 100755
index 0000000..31e675c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import com.android.settingslib.R;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+
+/**
+ * OppProfile handles Bluetooth OPP.
+ */
+final class OppProfile implements LocalBluetoothProfile {
+
+ static final String NAME = "OPP";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 2;
+
+ public boolean isConnectable() {
+ return false;
+ }
+
+ public boolean isAutoConnectable() {
+ return false;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ return false;
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ return false;
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ return BluetoothProfile.STATE_DISCONNECTED; // Settings app doesn't handle OPP
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ return false;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ return BluetoothProfile.PRIORITY_OFF; // Settings app doesn't handle OPP
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ }
+
+ public boolean isProfileReady() {
+ return true;
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_opp;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ return 0; // OPP profile not displayed in UI
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return 0; // no icon for OPP
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
new file mode 100755
index 0000000..3af89e6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * PanProfile handles Bluetooth PAN profile (NAP and PANU).
+ */
+final class PanProfile implements LocalBluetoothProfile {
+ private static final String TAG = "PanProfile";
+ private static boolean V = true;
+
+ private BluetoothPan mService;
+ private boolean mIsProfileReady;
+
+ // Tethering direction for each device
+ private final HashMap<BluetoothDevice, Integer> mDeviceRoleMap =
+ new HashMap<BluetoothDevice, Integer>();
+
+ static final String NAME = "PAN";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 4;
+
+ // These callbacks run on the main thread.
+ private final class PanServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothPan) proxy;
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ PanProfile(Context context) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ adapter.getProfileProxy(context, new PanServiceListener(),
+ BluetoothProfile.PAN);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return false;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> sinks = mService.getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ mService.disconnect(sink);
+ }
+ }
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ // return current connection status so profile checkbox is set correctly
+ return getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ return -1;
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ // ignore: isPreferred is always true for PAN
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ if (isLocalRoleNap(device)) {
+ return R.string.bluetooth_profile_pan_nap;
+ } else {
+ return R.string.bluetooth_profile_pan;
+ }
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_pan_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ if (isLocalRoleNap(device)) {
+ return R.string.bluetooth_pan_nap_profile_summary_connected;
+ } else {
+ return R.string.bluetooth_pan_user_profile_summary_connected;
+ }
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_network_pan;
+ }
+
+ // Tethering direction determines UI strings.
+ void setLocalRole(BluetoothDevice device, int role) {
+ mDeviceRoleMap.put(device, role);
+ }
+
+ boolean isLocalRoleNap(BluetoothDevice device) {
+ if (mDeviceRoleMap.containsKey(device)) {
+ return mDeviceRoleMap.get(device) == BluetoothPan.LOCAL_NAP_ROLE;
+ } else {
+ return false;
+ }
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.PAN, mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up PAN proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
new file mode 100755
index 0000000..a552b24a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPbap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * PBAPServer Profile
+ */
+public final class PbapServerProfile implements LocalBluetoothProfile {
+ private static final String TAG = "PbapServerProfile";
+ private static boolean V = true;
+
+ private BluetoothPbap mService;
+ private boolean mIsProfileReady;
+
+ static final String NAME = "PBAP Server";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 6;
+
+ // The UUIDs indicate that remote device might access pbap server
+ static final ParcelUuid[] PBAB_CLIENT_UUIDS = {
+ BluetoothUuid.HSP,
+ BluetoothUuid.Handsfree,
+ BluetoothUuid.PBAP_PCE
+ };
+
+ // These callbacks run on the main thread.
+ private final class PbapServiceListener
+ implements BluetoothPbap.ServiceListener {
+
+ public void onServiceConnected(BluetoothPbap proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothPbap) proxy;
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected() {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ PbapServerProfile(Context context) {
+ BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener());
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return false;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ /*Can't connect from server */
+ return false;
+
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.disconnect();
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ if (mService.isConnected(device))
+ return BluetoothProfile.STATE_CONNECTED;
+ else
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ return false;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ return -1;
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ // ignore: isPreferred is always true for PBAP
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_pbap;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ return R.string.bluetooth_profile_pbap_summary;
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ mService.close();
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up PBAP proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
new file mode 100644
index 0000000..c919426
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
@@ -0,0 +1,43 @@
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.R;
+
+public class Utils {
+ public static final boolean V = false; // verbose logging
+ public static final boolean D = true; // regular logging
+
+ private static ErrorListener sErrorListener;
+
+ public static int getConnectionStateSummary(int connectionState) {
+ switch (connectionState) {
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_connected;
+ case BluetoothProfile.STATE_CONNECTING:
+ return R.string.bluetooth_connecting;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_disconnected;
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return R.string.bluetooth_disconnecting;
+ default:
+ return 0;
+ }
+ }
+
+ static void showError(Context context, String name, int messageResId) {
+ if (sErrorListener != null) {
+ sErrorListener.onShowError(context, name, messageResId);
+ }
+ }
+
+ public static void setErrorListener(ErrorListener listener) {
+ sErrorListener = listener;
+ }
+
+ public interface ErrorListener {
+ void onShowError(Context context, String name, int messageResId);
+ }
+
+}