Merge "settingslib/bluetooth: Add LeAudio support"
This commit is contained in:
31
packages/SettingsLib/res/drawable/ic_bt_le_audio.xml
Normal file
31
packages/SettingsLib/res/drawable/ic_bt_le_audio.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<!--
|
||||||
|
Copyright 2021 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.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:tint="?android:attr/colorControlNormal" >
|
||||||
|
<path
|
||||||
|
android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
|
||||||
|
android:fillColor="#FFFFFFFF"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
|
||||||
|
android:fillColor="#FFFFFFFF"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
|
||||||
|
android:fillColor="#FFFFFFFF"/>
|
||||||
|
</vector>
|
||||||
@@ -257,8 +257,12 @@
|
|||||||
|
|
||||||
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the Hearing Aid profile. -->
|
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the Hearing Aid profile. -->
|
||||||
<string name="bluetooth_profile_hearing_aid">Hearing Aids</string>
|
<string name="bluetooth_profile_hearing_aid">Hearing Aids</string>
|
||||||
|
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the LE_AUDIO profile. -->
|
||||||
|
<string name="bluetooth_profile_le_audio">LE_AUDIO</string>
|
||||||
<!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference when Hearing Aid is connected. -->
|
<!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference when Hearing Aid is connected. -->
|
||||||
<string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aids</string>
|
<string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aids</string>
|
||||||
|
<!-- Bluetooth settings. Connection options screen. The summary for the LE_AUDIO checkbox preference when LE_AUDIO is connected. -->
|
||||||
|
<string name="bluetooth_le_audio_profile_summary_connected">Connected to LE_AUDIO</string>
|
||||||
|
|
||||||
<!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference when A2DP is connected. -->
|
<!-- 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>
|
<string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
|
||||||
@@ -299,6 +303,8 @@
|
|||||||
<string name="bluetooth_hid_profile_summary_use_for">Use for input</string>
|
<string name="bluetooth_hid_profile_summary_use_for">Use for input</string>
|
||||||
<!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference that describes how checking it will set the Hearing Aid profile as preferred. -->
|
<!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference that describes how checking it will set the Hearing Aid profile as preferred. -->
|
||||||
<string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aids</string>
|
<string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aids</string>
|
||||||
|
<!-- Bluetooth settings. Connection options screen. The summary for the LE_AUDIO checkbox preference that describes how checking it will set the LE_AUDIO profile as preferred. -->
|
||||||
|
<string name="bluetooth_le_audio_profile_summary_use_for">Use for LE_AUDIO</string>
|
||||||
|
|
||||||
<!-- Button text for accepting an incoming pairing request. [CHAR LIMIT=20] -->
|
<!-- Button text for accepting an incoming pairing request. [CHAR LIMIT=20] -->
|
||||||
<string name="bluetooth_pairing_accept">Pair</string>
|
<string name="bluetooth_pairing_accept">Pair</string>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.bluetooth.BluetoothCsipSetCoordinator;
|
|||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothHeadset;
|
import android.bluetooth.BluetoothHeadset;
|
||||||
import android.bluetooth.BluetoothHearingAid;
|
import android.bluetooth.BluetoothHearingAid;
|
||||||
|
import android.bluetooth.BluetoothLeAudio;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -116,6 +117,8 @@ public class BluetoothEventManager {
|
|||||||
addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED, new ActiveDeviceChangedHandler());
|
addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED, new ActiveDeviceChangedHandler());
|
||||||
addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
|
addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
|
||||||
new ActiveDeviceChangedHandler());
|
new ActiveDeviceChangedHandler());
|
||||||
|
addHandler(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED,
|
||||||
|
new ActiveDeviceChangedHandler());
|
||||||
|
|
||||||
// Headset state changed broadcasts
|
// Headset state changed broadcasts
|
||||||
addHandler(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
|
addHandler(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
|
||||||
@@ -452,6 +455,9 @@ public class BluetoothEventManager {
|
|||||||
bluetoothProfile = BluetoothProfile.HEADSET;
|
bluetoothProfile = BluetoothProfile.HEADSET;
|
||||||
} else if (Objects.equals(action, BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED)) {
|
} else if (Objects.equals(action, BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED)) {
|
||||||
bluetoothProfile = BluetoothProfile.HEARING_AID;
|
bluetoothProfile = BluetoothProfile.HEARING_AID;
|
||||||
|
} else if (Objects.equals(action,
|
||||||
|
BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED)) {
|
||||||
|
bluetoothProfile = BluetoothProfile.LE_AUDIO;
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
|
Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.bluetooth.BluetoothClass;
|
|||||||
import android.bluetooth.BluetoothCsipSetCoordinator;
|
import android.bluetooth.BluetoothCsipSetCoordinator;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothHearingAid;
|
import android.bluetooth.BluetoothHearingAid;
|
||||||
|
import android.bluetooth.BluetoothLeAudio;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.bluetooth.BluetoothUuid;
|
import android.bluetooth.BluetoothUuid;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -109,10 +110,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
private boolean mIsActiveDeviceA2dp = false;
|
private boolean mIsActiveDeviceA2dp = false;
|
||||||
private boolean mIsActiveDeviceHeadset = false;
|
private boolean mIsActiveDeviceHeadset = false;
|
||||||
private boolean mIsActiveDeviceHearingAid = false;
|
private boolean mIsActiveDeviceHearingAid = false;
|
||||||
|
private boolean mIsActiveDeviceLeAudio = false;
|
||||||
// Media profile connect state
|
// Media profile connect state
|
||||||
private boolean mIsA2dpProfileConnectedFail = false;
|
private boolean mIsA2dpProfileConnectedFail = false;
|
||||||
private boolean mIsHeadsetProfileConnectedFail = false;
|
private boolean mIsHeadsetProfileConnectedFail = false;
|
||||||
private boolean mIsHearingAidProfileConnectedFail = false;
|
private boolean mIsHearingAidProfileConnectedFail = false;
|
||||||
|
private boolean mIsLeAudioProfileConnectedFail = false;
|
||||||
// Group second device for Hearing Aid
|
// Group second device for Hearing Aid
|
||||||
private CachedBluetoothDevice mSubDevice;
|
private CachedBluetoothDevice mSubDevice;
|
||||||
// Group member devices for the coordinated set
|
// Group member devices for the coordinated set
|
||||||
@@ -133,6 +136,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
case BluetoothProfile.HEARING_AID:
|
case BluetoothProfile.HEARING_AID:
|
||||||
mIsHearingAidProfileConnectedFail = true;
|
mIsHearingAidProfileConnectedFail = true;
|
||||||
break;
|
break;
|
||||||
|
case BluetoothProfile.LE_AUDIO:
|
||||||
|
mIsLeAudioProfileConnectedFail = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Log.w(TAG, "handleMessage(): unknown message : " + msg.what);
|
Log.w(TAG, "handleMessage(): unknown message : " + msg.what);
|
||||||
break;
|
break;
|
||||||
@@ -266,6 +272,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
case BluetoothProfile.HEARING_AID:
|
case BluetoothProfile.HEARING_AID:
|
||||||
mIsHearingAidProfileConnectedFail = isFailed;
|
mIsHearingAidProfileConnectedFail = isFailed;
|
||||||
break;
|
break;
|
||||||
|
case BluetoothProfile.LE_AUDIO:
|
||||||
|
mIsLeAudioProfileConnectedFail = isFailed;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Log.w(TAG, "setProfileConnectedStatus(): unknown profile id : " + profileId);
|
Log.w(TAG, "setProfileConnectedStatus(): unknown profile id : " + profileId);
|
||||||
break;
|
break;
|
||||||
@@ -541,6 +550,13 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LeAudioProfile leAudioProfile = mProfileManager.getLeAudioProfile();
|
||||||
|
if ((leAudioProfile != null) && isConnectedProfile(leAudioProfile)) {
|
||||||
|
if (leAudioProfile.setActiveDevice(getDevice())) {
|
||||||
|
Log.i(TAG, "OnPreferenceClickListener: LeAudio active device=" + this);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,6 +635,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
changed = (mIsActiveDeviceHearingAid != isActive);
|
changed = (mIsActiveDeviceHearingAid != isActive);
|
||||||
mIsActiveDeviceHearingAid = isActive;
|
mIsActiveDeviceHearingAid = isActive;
|
||||||
break;
|
break;
|
||||||
|
case BluetoothProfile.LE_AUDIO:
|
||||||
|
changed = (mIsActiveDeviceLeAudio != isActive);
|
||||||
|
mIsActiveDeviceLeAudio = isActive;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
|
Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
|
||||||
" isActive " + isActive);
|
" isActive " + isActive);
|
||||||
@@ -650,6 +670,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
return mIsActiveDeviceHeadset;
|
return mIsActiveDeviceHeadset;
|
||||||
case BluetoothProfile.HEARING_AID:
|
case BluetoothProfile.HEARING_AID:
|
||||||
return mIsActiveDeviceHearingAid;
|
return mIsActiveDeviceHearingAid;
|
||||||
|
case BluetoothProfile.LE_AUDIO:
|
||||||
|
return mIsActiveDeviceLeAudio;
|
||||||
default:
|
default:
|
||||||
Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
|
Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
|
||||||
break;
|
break;
|
||||||
@@ -744,6 +766,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
if (hearingAidProfile != null) {
|
if (hearingAidProfile != null) {
|
||||||
mIsActiveDeviceHearingAid = hearingAidProfile.getActiveDevices().contains(mDevice);
|
mIsActiveDeviceHearingAid = hearingAidProfile.getActiveDevices().contains(mDevice);
|
||||||
}
|
}
|
||||||
|
LeAudioProfile leAudio = mProfileManager.getLeAudioProfile();
|
||||||
|
if (leAudio != null) {
|
||||||
|
mIsActiveDeviceLeAudio = leAudio.getActiveDevices().contains(mDevice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -984,6 +1010,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
boolean a2dpConnected = true; // A2DP is connected
|
boolean a2dpConnected = true; // A2DP is connected
|
||||||
boolean hfpConnected = true; // HFP is connected
|
boolean hfpConnected = true; // HFP is connected
|
||||||
boolean hearingAidConnected = true; // Hearing Aid is connected
|
boolean hearingAidConnected = true; // Hearing Aid is connected
|
||||||
|
boolean leAudioConnected = true; // LeAudio is connected
|
||||||
int leftBattery = -1;
|
int leftBattery = -1;
|
||||||
int rightBattery = -1;
|
int rightBattery = -1;
|
||||||
|
|
||||||
@@ -1015,6 +1042,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
hfpConnected = false;
|
hfpConnected = false;
|
||||||
} else if (profile instanceof HearingAidProfile) {
|
} else if (profile instanceof HearingAidProfile) {
|
||||||
hearingAidConnected = false;
|
hearingAidConnected = false;
|
||||||
|
} else if (profile instanceof LeAudioProfile) {
|
||||||
|
leAudioConnected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1057,7 +1086,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
// 1. Hearing Aid device active.
|
// 1. Hearing Aid device active.
|
||||||
// 2. Headset device active with in-calling state.
|
// 2. Headset device active with in-calling state.
|
||||||
// 3. A2DP device active without in-calling state.
|
// 3. A2DP device active without in-calling state.
|
||||||
if (a2dpConnected || hfpConnected || hearingAidConnected) {
|
// 4. Le Audio device active
|
||||||
|
if (a2dpConnected || hfpConnected || hearingAidConnected || leAudioConnected) {
|
||||||
final boolean isOnCall = Utils.isAudioModeOngoingCall(mContext);
|
final boolean isOnCall = Utils.isAudioModeOngoingCall(mContext);
|
||||||
if ((mIsActiveDeviceHearingAid)
|
if ((mIsActiveDeviceHearingAid)
|
||||||
|| (mIsActiveDeviceHeadset && isOnCall)
|
|| (mIsActiveDeviceHeadset && isOnCall)
|
||||||
@@ -1092,7 +1122,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
|
|
||||||
private boolean isProfileConnectedFail() {
|
private boolean isProfileConnectedFail() {
|
||||||
return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail
|
return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail
|
||||||
|| (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail);
|
|| (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail)
|
||||||
|
|| mIsLeAudioProfileConnectedFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1103,6 +1134,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
boolean a2dpNotConnected = false; // A2DP is preferred but not connected
|
boolean a2dpNotConnected = false; // A2DP is preferred but not connected
|
||||||
boolean hfpNotConnected = false; // HFP is preferred but not connected
|
boolean hfpNotConnected = false; // HFP is preferred but not connected
|
||||||
boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
|
boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
|
||||||
|
boolean leAudioNotConnected = false; // LeAudio is preferred but not connected
|
||||||
|
|
||||||
synchronized (mProfileLock) {
|
synchronized (mProfileLock) {
|
||||||
for (LocalBluetoothProfile profile : getProfiles()) {
|
for (LocalBluetoothProfile profile : getProfiles()) {
|
||||||
@@ -1128,6 +1160,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
hfpNotConnected = true;
|
hfpNotConnected = true;
|
||||||
} else if (profile instanceof HearingAidProfile) {
|
} else if (profile instanceof HearingAidProfile) {
|
||||||
hearingAidNotConnected = true;
|
hearingAidNotConnected = true;
|
||||||
|
} else if (profile instanceof LeAudioProfile) {
|
||||||
|
leAudioNotConnected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1166,6 +1200,11 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
|
return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!leAudioNotConnected && mIsActiveDeviceLeAudio) {
|
||||||
|
activeDeviceString = activeDeviceStringsArray[1];
|
||||||
|
return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
|
||||||
|
}
|
||||||
|
|
||||||
if (profileConnected) {
|
if (profileConnected) {
|
||||||
if (a2dpNotConnected && hfpNotConnected) {
|
if (a2dpNotConnected && hfpNotConnected) {
|
||||||
if (batteryLevelPercentageString != null) {
|
if (batteryLevelPercentageString != null) {
|
||||||
@@ -1235,6 +1274,15 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
BluetoothProfile.STATE_CONNECTED;
|
BluetoothProfile.STATE_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} if {@code cachedBluetoothDevice} is LeAudio device
|
||||||
|
*/
|
||||||
|
public boolean isConnectedLeAudioDevice() {
|
||||||
|
LeAudioProfile leAudio = mProfileManager.getLeAudioProfile();
|
||||||
|
return leAudio != null && leAudio.getConnectionStatus(mDevice) ==
|
||||||
|
BluetoothProfile.STATE_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isConnectedSapDevice() {
|
private boolean isConnectedSapDevice() {
|
||||||
SapProfile sapProfile = mProfileManager.getSapProfile();
|
SapProfile sapProfile = mProfileManager.getSapProfile();
|
||||||
return sapProfile != null && sapProfile.getConnectionStatus(mDevice)
|
return sapProfile != null && sapProfile.getConnectionStatus(mDevice)
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
/* Copyright 2021 HIMSA II K/S - www.himsa.com. Represented by EHIMA
|
||||||
|
- www.ehima.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 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 static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_ALL;
|
||||||
|
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
|
||||||
|
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothLeAudio;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothCodecConfig;
|
||||||
|
import android.bluetooth.BluetoothCodecStatus;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.bluetooth.BluetoothUuid;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.settingslib.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LeAudioProfile implements LocalBluetoothProfile {
|
||||||
|
private static final String TAG = "LeAudioProfile";
|
||||||
|
private static boolean DEBUG = true;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
private BluetoothLeAudio mService;
|
||||||
|
private boolean mIsProfileReady;
|
||||||
|
|
||||||
|
private final CachedBluetoothDeviceManager mDeviceManager;
|
||||||
|
|
||||||
|
static final String NAME = "LE_AUDIO";
|
||||||
|
private final LocalBluetoothProfileManager mProfileManager;
|
||||||
|
private final BluetoothAdapter mBluetoothAdapter;
|
||||||
|
|
||||||
|
// Order of this profile in device profiles list
|
||||||
|
private static final int ORDINAL = 1;
|
||||||
|
|
||||||
|
// These callbacks run on the main thread.
|
||||||
|
private final class LeAudioServiceListener
|
||||||
|
implements BluetoothProfile.ServiceListener {
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.S)
|
||||||
|
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG,"Bluetooth service connected");
|
||||||
|
}
|
||||||
|
mService = (BluetoothLeAudio) proxy;
|
||||||
|
// We just bound to the service, so refresh the UI for any connected LeAudio 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) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "LeAudioProfile found new device: " + nextDevice);
|
||||||
|
}
|
||||||
|
device = mDeviceManager.addDevice(nextDevice);
|
||||||
|
}
|
||||||
|
device.onProfileStateChanged(LeAudioProfile.this,
|
||||||
|
BluetoothProfile.STATE_CONNECTED);
|
||||||
|
device.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
mProfileManager.callServiceConnectedListeners();
|
||||||
|
mIsProfileReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(int profile) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG,"Bluetooth service disconnected");
|
||||||
|
}
|
||||||
|
mProfileManager.callServiceDisconnectedListeners();
|
||||||
|
mIsProfileReady = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return mIsProfileReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProfileId() {
|
||||||
|
return BluetoothProfile.LE_AUDIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LeAudioProfile(Context context, CachedBluetoothDeviceManager deviceManager,
|
||||||
|
LocalBluetoothProfileManager profileManager) {
|
||||||
|
mContext = context;
|
||||||
|
mDeviceManager = deviceManager;
|
||||||
|
mProfileManager = profileManager;
|
||||||
|
|
||||||
|
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
mBluetoothAdapter.getProfileProxy(
|
||||||
|
context, new LeAudioServiceListener(),
|
||||||
|
BluetoothProfile.LE_AUDIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean accessProfileEnabled() {
|
||||||
|
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});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean connect(BluetoothDevice device) {
|
||||||
|
if (mService == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return mService.connect(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
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 setActiveDevice(BluetoothDevice device) {
|
||||||
|
if (mBluetoothAdapter == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return device == null
|
||||||
|
? mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_ALL)
|
||||||
|
: mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BluetoothDevice> getActiveDevices() {
|
||||||
|
if (mService == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return mService.getActiveDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(BluetoothDevice device) {
|
||||||
|
if (mService == null || device == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getConnectionPolicy(BluetoothDevice device) {
|
||||||
|
if (mService == null || device == null) {
|
||||||
|
return CONNECTION_POLICY_FORBIDDEN;
|
||||||
|
}
|
||||||
|
return mService.getConnectionPolicy(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
|
||||||
|
boolean isEnabled = false;
|
||||||
|
if (mService == null || device == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (enabled) {
|
||||||
|
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
|
||||||
|
isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrdinal() {
|
||||||
|
return ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameResource(BluetoothDevice device) {
|
||||||
|
return R.string.bluetooth_profile_le_audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSummaryResourceForDevice(BluetoothDevice device) {
|
||||||
|
int state = getConnectionStatus(device);
|
||||||
|
switch (state) {
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
return R.string.bluetooth_le_audio_profile_summary_use_for;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
return R.string.bluetooth_le_audio_profile_summary_connected;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return BluetoothUtils.getConnectionStateSummary(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDrawableResource(BluetoothClass btClass) {
|
||||||
|
return R.drawable.ic_bt_le_audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.S)
|
||||||
|
protected void finalize() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "finalize()");
|
||||||
|
}
|
||||||
|
if (mService != null) {
|
||||||
|
try {
|
||||||
|
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.LE_AUDIO,
|
||||||
|
mService);
|
||||||
|
mService = null;
|
||||||
|
}catch (Throwable t) {
|
||||||
|
Log.w(TAG, "Error cleaning up LeAudio proxy", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import android.bluetooth.BluetoothDevice;
|
|||||||
import android.bluetooth.BluetoothHeadset;
|
import android.bluetooth.BluetoothHeadset;
|
||||||
import android.bluetooth.BluetoothHeadsetClient;
|
import android.bluetooth.BluetoothHeadsetClient;
|
||||||
import android.bluetooth.BluetoothHearingAid;
|
import android.bluetooth.BluetoothHearingAid;
|
||||||
|
import android.bluetooth.BluetoothLeAudio;
|
||||||
import android.bluetooth.BluetoothHidDevice;
|
import android.bluetooth.BluetoothHidDevice;
|
||||||
import android.bluetooth.BluetoothHidHost;
|
import android.bluetooth.BluetoothHidHost;
|
||||||
import android.bluetooth.BluetoothMap;
|
import android.bluetooth.BluetoothMap;
|
||||||
@@ -102,6 +103,7 @@ public class LocalBluetoothProfileManager {
|
|||||||
private PbapServerProfile mPbapProfile;
|
private PbapServerProfile mPbapProfile;
|
||||||
private HearingAidProfile mHearingAidProfile;
|
private HearingAidProfile mHearingAidProfile;
|
||||||
private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
|
private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
|
||||||
|
private LeAudioProfile mLeAudioProfile;
|
||||||
private SapProfile mSapProfile;
|
private SapProfile mSapProfile;
|
||||||
private VolumeControlProfile mVolumeControlProfile;
|
private VolumeControlProfile mVolumeControlProfile;
|
||||||
|
|
||||||
@@ -232,6 +234,14 @@ public class LocalBluetoothProfileManager {
|
|||||||
// Note: no event handler for VCP, only for being connectable.
|
// Note: no event handler for VCP, only for being connectable.
|
||||||
mProfileNameMap.put(VolumeControlProfile.NAME, mVolumeControlProfile);
|
mProfileNameMap.put(VolumeControlProfile.NAME, mVolumeControlProfile);
|
||||||
}
|
}
|
||||||
|
if (mLeAudioProfile == null && supportedList.contains(BluetoothProfile.LE_AUDIO)) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Adding local LE_AUDIO profile");
|
||||||
|
}
|
||||||
|
mLeAudioProfile = new LeAudioProfile(mContext, mDeviceManager, this);
|
||||||
|
addProfile(mLeAudioProfile, LeAudioProfile.NAME,
|
||||||
|
BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
|
||||||
|
}
|
||||||
if (mCsipSetCoordinatorProfile == null
|
if (mCsipSetCoordinatorProfile == null
|
||||||
&& supportedList.contains(BluetoothProfile.CSIP_SET_COORDINATOR)) {
|
&& supportedList.contains(BluetoothProfile.CSIP_SET_COORDINATOR)) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -487,6 +497,10 @@ public class LocalBluetoothProfileManager {
|
|||||||
return mHearingAidProfile;
|
return mHearingAidProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LeAudioProfile getLeAudioProfile() {
|
||||||
|
return mLeAudioProfile;
|
||||||
|
}
|
||||||
|
|
||||||
SapProfile getSapProfile() {
|
SapProfile getSapProfile() {
|
||||||
return mSapProfile;
|
return mSapProfile;
|
||||||
}
|
}
|
||||||
@@ -614,6 +628,11 @@ public class LocalBluetoothProfileManager {
|
|||||||
removedProfiles.remove(mHearingAidProfile);
|
removedProfiles.remove(mHearingAidProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ArrayUtils.contains(uuids, BluetoothUuid.LE_AUDIO) && mLeAudioProfile != null) {
|
||||||
|
profiles.add(mLeAudioProfile);
|
||||||
|
removedProfiles.remove(mLeAudioProfile);
|
||||||
|
}
|
||||||
|
|
||||||
if (mSapProfile != null && ArrayUtils.contains(uuids, BluetoothUuid.SAP)) {
|
if (mSapProfile != null && ArrayUtils.contains(uuids, BluetoothUuid.SAP)) {
|
||||||
profiles.add(mSapProfile);
|
profiles.add(mSapProfile);
|
||||||
removedProfiles.remove(mSapProfile);
|
removedProfiles.remove(mSapProfile);
|
||||||
|
|||||||
Reference in New Issue
Block a user