From a63a2505e74e6955d54629c488699c7f9c52cf18 Mon Sep 17 00:00:00 2001 From: AnubhavGupta Date: Tue, 30 Jun 2015 12:40:39 +0530 Subject: Bluetooth: A2DP Sink support for Settings App - add support for A2DP Sink in Settings App. This will enable connection initiation and updation on Settings App - add framework Apis to support A2DP Sink. Any third party Apps can access A2DP Sink priority of device and playing state of device - add support for key to set priority. This manages priority of device for A2DP Sink profile Change-Id: I9920957e0f20583e1e2d57ca76c2c0bd9dfa0bcf --- .../settingslib/bluetooth/A2dpSinkProfile.java | 223 +++++++++++++++++++++ .../bluetooth/BluetoothDeviceFilter.java | 4 + .../bluetooth/CachedBluetoothDevice.java | 3 +- .../bluetooth/LocalBluetoothAdapter.java | 4 + .../bluetooth/LocalBluetoothProfileManager.java | 34 +++- 5 files changed, 265 insertions(+), 3 deletions(-) create mode 100755 packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java mode change 100644 => 100755 packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java mode change 100644 => 100755 packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java mode change 100644 => 100755 packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java (limited to 'packages/SettingsLib/src') diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java new file mode 100755 index 0000000..fd76d81 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -0,0 +1,223 @@ +/* + * 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.BluetoothA2dpSink; +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; + +final class A2dpSinkProfile implements LocalBluetoothProfile { + private static final String TAG = "A2dpSinkProfile"; + private static boolean V = true; + + private BluetoothA2dpSink mService; + private boolean mIsProfileReady; + + private final LocalBluetoothAdapter mLocalAdapter; + private final CachedBluetoothDeviceManager mDeviceManager; + + static final ParcelUuid[] SRC_UUIDS = { + BluetoothUuid.AudioSource, + BluetoothUuid.AdvAudioDist, + }; + + static final String NAME = "A2DPSink"; + private final LocalBluetoothProfileManager mProfileManager; + + // Order of this profile in device profiles list + private static final int ORDINAL = 5; + + // These callbacks run on the main thread. + private final class A2dpSinkServiceListener + implements BluetoothProfile.ServiceListener { + + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (V) Log.d(TAG,"Bluetooth service connected"); + mService = (BluetoothA2dpSink) proxy; + // We just bound to the service, so refresh the UI for any connected A2DP devices. + List 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, "A2dpSinkProfile found new device: " + nextDevice); + device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice); + } + device.onProfileStateChanged(A2dpSinkProfile.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; + } + + A2dpSinkProfile(Context context, LocalBluetoothAdapter adapter, + CachedBluetoothDeviceManager deviceManager, + LocalBluetoothProfileManager profileManager) { + mLocalAdapter = adapter; + mDeviceManager = deviceManager; + mProfileManager = profileManager; + mLocalAdapter.getProfileProxy(context, new A2dpSinkServiceListener(), + BluetoothProfile.A2DP_SINK); + } + + public boolean isConnectable() { + return true; + } + + public boolean isAutoConnectable() { + return true; + } + + public List getConnectedDevices() { + if (mService == null) return new ArrayList(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 srcs = getConnectedDevices(); + if (srcs != null) { + for (BluetoothDevice src : srcs) { + if (src.equals(device)) { + // Connect to same device, Ignore it + Log.d(TAG,"Ignoring Connect"); + return true; + } + } + for (BluetoothDevice src : srcs) { + mService.disconnect(src); + } + } + 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 srcs = mService.getConnectedDevices(); + if (!srcs.isEmpty()) { + if (mService.isA2dpPlaying(srcs.get(0))) { + return true; + } + } + return false; + } + + public String toString() { + return NAME; + } + + public int getOrdinal() { + return ORDINAL; + } + + public int getNameResource(BluetoothDevice device) { + // we need to have same string in UI for even SINK Media Audio. + 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_SINK, + 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/BluetoothDeviceFilter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java old mode 100644 new mode 100755 index 8dec86a..0bd8dbd --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java @@ -112,11 +112,15 @@ public final class BluetoothDeviceFilter { if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS)) { return true; } + if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_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_A2DP_SINK) || btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index b0429ef..ac5a4ab 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -846,7 +846,8 @@ public final class CachedBluetoothDevice implements Comparable