LE Audio: Expose more system APIs
* Expose two intentss:
* ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED
* ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED
* Settings and Telecom are interested in the group node status
information, therefore lets expose the API as a SystemApi
* Reduce scope of HAP connection state API to system API
* Expose more system APIs for HAP client
* To select different hearing presets
* To get notified about changes in hearing presets
* To get all available hearing presets
* Expose more system APIs for CSIP
* To make error codes more visible for the API caller
* Expose GROUP_ID_INVALID to public API for getGroupId()
Bug: 150670922
Test: atest BluetoothInstrumentationTests
Tag: #feature
Sponsor: siyuanh@
Ignore-AOSP-First: earlier change merged on internal
Change-Id: If87e0863f23b989632603ac1253e4a77bfff028d
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index 2e3614a..dec6db2 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -18,7 +18,6 @@
package com.android.bluetooth.csip;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -26,6 +25,7 @@
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothCsipSetCoordinator;
import android.bluetooth.IBluetoothCsipSetCoordinatorCallback;
@@ -488,7 +488,8 @@
if (mLocks.containsKey(groupId)) {
try {
callback.onGroupLockSet(groupId,
- BluetoothCsipSetCoordinator.GROUP_LOCK_FAILED_LOCKED_BY_OTHER, true);
+ BluetoothStatusCodes.ERROR_CSIP_GROUP_LOCKED_BY_OTHER,
+ true);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -615,6 +616,25 @@
}
}
+ int getApiStatusCode(int nativeResult) {
+ switch (nativeResult) {
+ case IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_SUCCESS:
+ return BluetoothStatusCodes.SUCCESS;
+ case IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_FAILED_INVALID_GROUP:
+ return BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID;
+ case IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED:
+ return BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED;
+ case IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_FAILED_LOCKED_BY_OTHER:
+ return BluetoothStatusCodes.ERROR_CSIP_GROUP_LOCKED_BY_OTHER;
+ case IBluetoothCsipSetCoordinator.CSIS_LOCKED_GROUP_MEMBER_LOST:
+ return BluetoothStatusCodes.ERROR_CSIP_LOCKED_GROUP_MEMBER_LOST;
+ case IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_FAILED_OTHER_REASON:
+ default:
+ Log.e(TAG, " Unknown status code: " + nativeResult);
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
+ }
+ }
+
void handleGroupLockChanged(int groupId, int status, boolean isLocked) {
synchronized (mLocks) {
if (!mLocks.containsKey(groupId)) {
@@ -623,7 +643,7 @@
IBluetoothCsipSetCoordinatorLockCallback cb = mLocks.get(groupId).second;
try {
- cb.onGroupLockSet(groupId, status, isLocked);
+ cb.onGroupLockSet(groupId, getApiStatusCode(status), isLocked);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
index dd34055..5adc336 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
@@ -260,11 +260,12 @@
.when(mCsipSetCoordinatorNativeInterface)
.onGroupLockChanged(anyInt(), anyBoolean(), anyInt());
mCsipSetCoordinatorNativeInterface.onGroupLockChanged(
- group, true, BluetoothCsipSetCoordinator.GROUP_LOCK_SUCCESS);
+ group, true, IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_SUCCESS);
try {
verify(mCsipSetCoordinatorLockCallback, times(1))
- .onGroupLockSet(group, BluetoothCsipSetCoordinator.GROUP_LOCK_SUCCESS, true);
+ .onGroupLockSet(group, BluetoothStatusCodes.SUCCESS,
+ true);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -273,11 +274,12 @@
verify(mCsipSetCoordinatorNativeInterface, times(1)).groupLockSet(eq(group), eq(false));
mCsipSetCoordinatorNativeInterface.onGroupLockChanged(
- group, false, BluetoothCsipSetCoordinator.GROUP_LOCK_SUCCESS);
+ group, false, IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_SUCCESS);
try {
verify(mCsipSetCoordinatorLockCallback, times(1))
- .onGroupLockSet(group, BluetoothCsipSetCoordinator.GROUP_LOCK_SUCCESS, false);
+ .onGroupLockSet(group, BluetoothStatusCodes.SUCCESS,
+ false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -305,7 +307,7 @@
try {
verify(mCsipSetCoordinatorLockCallback, times(1))
.onGroupLockSet(group,
- BluetoothCsipSetCoordinator.GROUP_LOCK_FAILED_LOCKED_BY_OTHER, true);
+ BluetoothStatusCodes.ERROR_CSIP_GROUP_LOCKED_BY_OTHER, true);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 94d9698..d06f75e 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -756,15 +756,6 @@
field protected java.util.List<android.bluetooth.BluetoothGattService> mIncludedServices;
}
- public final class BluetoothHapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
- method public void close();
- method protected void finalize();
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
- field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_HAP_CONNECTION_STATE_CHANGED = "android.bluetooth.action.HAP_CONNECTION_STATE_CHANGED";
- }
-
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(android.bluetooth.BluetoothDevice);
@@ -923,6 +914,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getGroupId(@NonNull android.bluetooth.BluetoothDevice);
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
+ field public static final int GROUP_ID_INVALID = -1; // 0xffffffff
}
public final class BluetoothLeAudioCodecConfig implements android.os.Parcelable {
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 5188e2a..f9df162 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -157,6 +157,32 @@
field public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22; // 0x16
}
+ public final class BluetoothHapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothHapPresetInfo getActivePresetInfo(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothHapPresetInfo> getAllPresetInfo(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothHapClient.Callback);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void selectPreset(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void selectPresetForGroup(int, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setPresetName(@NonNull android.bluetooth.BluetoothDevice, int, @NonNull String);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setPresetNameForGroup(int, int, @NonNull String);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void unregisterCallback(@NonNull android.bluetooth.BluetoothHapClient.Callback);
+ field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_HAP_CONNECTION_STATE_CHANGED = "android.bluetooth.action.HAP_CONNECTION_STATE_CHANGED";
+ }
+
+ public static interface BluetoothHapClient.Callback {
+ method public void onActivePresetChanged(@NonNull android.bluetooth.BluetoothDevice, int);
+ method public void onPresetInfoChanged(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.List<android.bluetooth.BluetoothHapPresetInfo>, int);
+ method public void onSelectActivePresetFailed(@NonNull android.bluetooth.BluetoothDevice, int);
+ method public void onSelectActivePresetForGroupFailed(int, int);
+ method public void onSetPresetNameFailed(@NonNull android.bluetooth.BluetoothDevice, int);
+ method public void onSetPresetNameForGroupFailed(int, int);
+ }
+
public final class BluetoothHapPresetInfo implements android.os.Parcelable {
method public int getIndex();
method @NonNull public String getName();
@@ -219,6 +245,12 @@
public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioLocation(@NonNull android.bluetooth.BluetoothDevice);
+ field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED";
+ field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED = "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED";
+ field public static final String EXTRA_LE_AUDIO_GROUP_ID = "android.bluetooth.extra.LE_AUDIO_GROUP_ID";
+ field public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS = "android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS";
+ field public static final int GROUP_NODE_ADDED = 1; // 0x1
+ field public static final int GROUP_NODE_REMOVED = 2; // 0x2
}
public final class BluetoothLeAudioCodecConfigMetadata implements android.os.Parcelable {
@@ -477,6 +509,11 @@
field public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; // 0x45e
field public static final int ERROR_BAD_PARAMETERS = 21; // 0x15
field public static final int ERROR_CALL_ACTIVE = 1119; // 0x45f
+ field public static final int ERROR_CSIP_GROUP_LOCKED_BY_OTHER = 1208; // 0x4b8
+ field public static final int ERROR_CSIP_INVALID_GROUP_ID = 1207; // 0x4b7
+ field public static final int ERROR_CSIP_LOCKED_GROUP_MEMBER_LOST = 1209; // 0x4b9
+ field public static final int ERROR_HAP_INVALID_PRESET_INDEX = 1211; // 0x4bb
+ field public static final int ERROR_HAP_PRESET_NAME_TOO_LONG = 1210; // 0x4ba
field public static final int ERROR_HARDWARE_GENERIC = 20; // 0x14
field public static final int ERROR_LE_BROADCAST_ASSISTANT_DUPLICATE_ADDITION = 1203; // 0x4b3
field public static final int ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID = 1202; // 0x4b2
@@ -491,6 +528,7 @@
field public static final int ERROR_PROFILE_NOT_CONNECTED = 14; // 0xe
field public static final int ERROR_REMOTE_LINK_ERROR = 25; // 0x19
field public static final int ERROR_REMOTE_NOT_ENOUGH_RESOURCES = 23; // 0x17
+ field public static final int ERROR_REMOTE_OPERATION_NOT_SUPPORTED = 24; // 0x18
field public static final int ERROR_REMOTE_OPERATION_REJECTED = 24; // 0x18
field public static final int ERROR_TIMEOUT = 15; // 0xf
field public static final int NOT_ALLOWED = 401; // 0x191
diff --git a/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java
index ba57ec4..955920c 100644
--- a/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java
+++ b/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java
@@ -21,6 +21,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -37,6 +38,8 @@
import com.android.modules.utils.SynchronousResultReceiver;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -49,7 +52,7 @@
/**
* This class provides the public APIs to control the Bluetooth CSIP set coordinator.
*
- * <p>BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC
+ * <p>BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth CSIP set
* Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
* the BluetoothCsipSetCoordinator proxy object.
*
@@ -66,10 +69,29 @@
*/
@SystemApi
public interface ClientLockCallback {
+ /** @hide */
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
+ BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID,
+ BluetoothStatusCodes.ERROR_CSIP_GROUP_LOCKED_BY_OTHER,
+ BluetoothStatusCodes.ERROR_CSIP_LOCKED_GROUP_MEMBER_LOST,
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Status {}
+
/**
+ * Callback is invoken as a result on {@link #groupLock()}.
+ *
+ * @param groupId group identifier
+ * @param opStatus status of lock operation
+ * @param isLocked inidcates if group is locked
+ *
* @hide
*/
- @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked);
+ @SystemApi
+ void onGroupLockSet(int groupId, @Status int opStatus, boolean isLocked);
}
private static class BluetoothCsipSetCoordinatorLockCallbackDelegate
@@ -177,55 +199,6 @@
*/
public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID;
- /**
- * Indicating that group was locked with success.
- *
- * @hide
- */
- public static final int GROUP_LOCK_SUCCESS = 0;
-
- /**
- * Indicating that group locked failed due to invalid group ID.
- *
- * @hide
- */
- public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1;
-
- /**
- * Indicating that group locked failed due to empty group.
- *
- * @hide
- */
- public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2;
-
- /**
- * Indicating that group locked failed due to group members being disconnected.
- *
- * @hide
- */
- public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3;
-
- /**
- * Indicating that group locked failed due to group member being already locked.
- *
- * @hide
- */
- public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4;
-
- /**
- * Indicating that group locked failed due to other reason.
- *
- * @hide
- */
- public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5;
-
- /**
- * Indicating that group member in locked state was lost.
- *
- * @hide
- */
- public static final int LOCKED_GROUP_MEMBER_LOST = 6;
-
private final BluetoothAdapter mAdapter;
private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothCsipSetCoordinator> mProfileConnector =
diff --git a/framework/java/android/bluetooth/BluetoothHapClient.java b/framework/java/android/bluetooth/BluetoothHapClient.java
index 992e906..2b245bc 100644
--- a/framework/java/android/bluetooth/BluetoothHapClient.java
+++ b/framework/java/android/bluetooth/BluetoothHapClient.java
@@ -19,10 +19,13 @@
import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.AttributionSource;
import android.content.Context;
@@ -33,8 +36,11 @@
import com.android.modules.utils.SynchronousResultReceiver;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
@@ -44,7 +50,9 @@
* <p>BluetoothHapClient is a proxy object for controlling the Bluetooth HAP
* Service client via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
* BluetoothHapClient proxy object.
+ * @hide
*/
+@SystemApi
public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable {
private static final String TAG = "BluetoothHapClient";
private static final boolean DBG = false;
@@ -53,6 +61,126 @@
private CloseGuard mCloseGuard;
/**
+ * This class provides callbacks mechanism for the BluetoothHapClient profile.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface Callback {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST,
+ BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST,
+ BluetoothStatusCodes.REASON_REMOTE_REQUEST,
+ BluetoothStatusCodes.REASON_SYSTEM_POLICY,
+ BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED,
+ BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED,
+ BluetoothStatusCodes.ERROR_HAP_PRESET_NAME_TOO_LONG,
+ BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX,
+ BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID,
+ })
+ @interface Status {}
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ PRESET_INFO_REASON_ALL_PRESET_INFO,
+ PRESET_INFO_REASON_PRESET_INFO_UPDATE,
+ PRESET_INFO_REASON_PRESET_DELETED,
+ PRESET_INFO_REASON_PRESET_AVAILABILITY_CHANGED,
+ PRESET_INFO_REASON_PRESET_INFO_REQUEST_RESPONSE,
+ })
+ @interface PresetInfoReason {}
+
+ /**
+ * Invoked to inform about HA device's currently active preset.
+ *
+ * @param device remote device,
+ * @param presetIndex the currently active preset index.
+ *
+ * @hide
+ */
+ @SystemApi
+ void onActivePresetChanged(@NonNull BluetoothDevice device, int presetIndex);
+
+ /**
+ * Invoked inform about the result of a failed preset change attempt.
+ *
+ * @param device remote device,
+ * @param statusCode failure reason.
+ *
+ * @hide
+ */
+ @SystemApi
+ void onSelectActivePresetFailed(@NonNull BluetoothDevice device, @Status int statusCode);
+
+ /**
+ * Invoked to inform about the result of a failed preset change attempt.
+ *
+ * The implementation will try to restore the state for every device back to original
+ *
+ * @param hapGroupId valid HAP group ID,
+ * @param statusCode failure reason.
+ *
+ * @hide
+ */
+ @SystemApi
+ void onSelectActivePresetForGroupFailed(int hapGroupId, @Status int statusCode);
+
+ /**
+ * Invoked to inform about the preset list changes.
+ *
+ * @param device remote device,
+ * @param presetInfoList a list of all preset information on the target device
+ * @param statusCode reason for the preset list change
+ *
+ * @hide
+ */
+ @SystemApi
+ void onPresetInfoChanged(@NonNull BluetoothDevice device,
+ @NonNull List<BluetoothHapPresetInfo> presetInfoList,
+ @Status int statusCode);
+
+ /**
+ * Invoked to inform about HA device's feature set.
+ *
+ * @param device remote device
+ * @param hapFeatures the feature set integer with these possible bit numbers
+ * set: {@link #FEATURE_BIT_NUM_TYPE_MONAURAL}, {@link #FEATURE_BIT_NUM_TYPE_BANDED},
+ * {@link #FEATURE_BIT_NUM_SYNCHRONIZATED_PRESETS},
+ * {@link #FEATURE_BIT_NUM_INDEPENDENT_PRESETS}, {@link #FEATURE_BIT_NUM_DYNAMIC_PRESETS},
+ * {@link #FEATURE_BIT_NUM_WRITABLE_PRESETS}.
+ *
+ * @hide
+ */
+ void onHapFeaturesAvailable(@NonNull BluetoothDevice device, int hapFeatures);
+
+ /**
+ * Invoked to inform about the failed preset rename attempt.
+ *
+ * @param device remote device
+ * @param status Failure reason code.
+ * @hide
+ */
+ @SystemApi
+ void onSetPresetNameFailed(@NonNull BluetoothDevice device, @Status int status);
+
+ /**
+ * Invoked to inform about the failed preset rename attempt.
+ *
+ * The implementation will try to restore the state for every device back to original
+ *
+ * @param hapGroupId valid HAP group ID,
+ * @param status Failure reason code.
+ * @hide
+ */
+ @SystemApi
+ void onSetPresetNameForGroupFailed(int hapGroupId, @Status int status);
+ }
+
+ /**
* Intent used to broadcast the change in connection state of the Hearing Access Profile Client
* service. Please note that in the binaural case, there will be two different LE devices for
* the left and right side and each device will have their own connection state changes.
@@ -67,9 +195,14 @@
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ * @hide
*/
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_HAP_CONNECTION_STATE_CHANGED =
"android.bluetooth.action.HAP_CONNECTION_STATE_CHANGED";
@@ -84,9 +217,9 @@
* <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
* <li> {@link #EXTRA_HAP_FEATURES} - Supported features map. </li>
* </ul>
- *
* @hide
*/
+ @RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
@@ -461,6 +594,63 @@
}
/**
+ * Register a {@link Callback} that will be invoked during the
+ * operation of this profile.
+ *
+ * Repeated registration of the same <var>callback</var> object will have no effect after
+ * the first call to this method, even when the <var>executor</var> is different. API caller
+ * would have to call {@link #unregisterCallback(Callback)} with
+ * the same callback object before registering it again.
+ *
+ * @param executor an {@link Executor} to execute given callback
+ * @param callback user implementation of the {@link Callback}
+ * @throws IllegalArgumentException if a null executor, sink, or callback is given
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor cannot be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ if (DBG) log("registerCallback");
+ throw new UnsupportedOperationException("Not Implemented");
+ }
+
+ /**
+ * Unregister the specified {@link Callback}.
+ * <p>The same {@link Callback} object used when calling
+ * {@link #registerCallback(Executor, Callback)} must be used.
+ *
+ * <p>Callbacks are automatically unregistered when application process goes away
+ *
+ * @param callback user implementation of the {@link Callback}
+ * @throws IllegalArgumentException when callback is null or when no callback is registered
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void unregisterCallback(@NonNull Callback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ if (DBG) log("unregisterCallback");
+ throw new UnsupportedOperationException("Not Implemented");
+ }
+
+ /**
* Set connection policy of the profile
*
* <p> The device should already be paired.
@@ -472,6 +662,7 @@
* @return true if connectionPolicy is set, false on error
* @hide
*/
+ @SystemApi
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {
android.Manifest.permission.BLUETOOTH_CONNECT,
@@ -510,8 +701,12 @@
* @return connection policy of the device
* @hide
*/
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHapClient service = getService();
@@ -533,10 +728,15 @@
/**
* {@inheritDoc}
+ * @hide
*/
- @Override
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @Override
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) Log.d(TAG, "getConnectedDevices()");
final IBluetoothHapClient service = getService();
@@ -558,10 +758,15 @@
/**
* {@inheritDoc}
+ * @hide
*/
- @Override
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @Override
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) Log.d(TAG, "getDevicesMatchingConnectionStates()");
@@ -584,10 +789,15 @@
/**
* {@inheritDoc}
+ * @hide
*/
- @Override
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @Override
public @BluetoothProfile.BtProfileState int getConnectionState(
@NonNull BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getConnectionState(" + device + ")");
@@ -627,7 +837,10 @@
* @hide
*/
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getHapGroup(@NonNull BluetoothDevice device) {
final IBluetoothHapClient service = getService();
final int defaultValue = HAP_GROUP_UNAVAILABLE;
@@ -647,44 +860,73 @@
}
/**
- * Gets the currently active preset for a HA device
+ * Gets the currently active preset for a HA device.
*
* @param device is the device for which we want to set the active preset
* @return active preset index
* @hide
*/
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public boolean getActivePresetIndex(@NonNull BluetoothDevice device) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public int getActivePresetIndex(@NonNull BluetoothDevice device) {
final IBluetoothHapClient service = getService();
- final boolean defaultValue = false;
+ final int defaultValue = PRESET_INDEX_UNAVAILABLE;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled() && isValidDevice(device)) {
- try {
- final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
- service.getActivePresetIndex(device, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
- Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
- }
+ // TODO(b/216639668)
+ // try {
+ // final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ // service.getActivePresetIndex(device, mAttributionSource, recv);
+ // return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ // } catch (RemoteException | TimeoutException e) {
+ // Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ // }
}
return defaultValue;
}
/**
+ * Get the currently active preset info for a remote device.
+ *
+ * @param device is the device for which we want to get the preset name
+ * @return currently active preset info if selected, null if preset info is not available
+ * for the remote device
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public @Nullable BluetoothHapPresetInfo getActivePresetInfo(@NonNull BluetoothDevice device) {
+ // TODO(b/216639668)
+ return null;
+ }
+
+ /**
* Selects the currently active preset for a HA device
*
+ * On success, {@link Callback#onActivePresetChanged(BluetoothDevice, int)} will be called with
+ * reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}
+ * On failure, {@link Callback#onSelectActivePresetFailed(BluetoothDevice, int)} will be called.
+ *
* @param device is the device for which we want to set the active preset
* @param presetIndex is an index of one of the available presets
- * @return true if valid request was sent, false otherwise
* @hide
*/
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean selectActivePreset(@NonNull BluetoothDevice device, int presetIndex) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public void selectPreset(@NonNull BluetoothDevice device, int presetIndex) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
if (service == null) {
@@ -692,31 +934,39 @@
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled() && isValidDevice(device)) {
try {
+ // TODO(b/216639668)
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
service.selectActivePreset(device, presetIndex, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
}
/**
- * Selects the currently active preset for a HA device group.
+ * Selects the currently active preset for a Hearing Aid device group.
*
* <p> This group call may replace multiple device calls if those are part of the
* valid HAS group. Note that binaural HA devices may or may not support group.
*
+ * On success, {@link Callback#onActivePresetChanged(BluetoothDevice, int)} will be called
+ * for each device within the group with reason code
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}
+ * On failure, {@link Callback#onSelectActivePresetFailed(BluetoothDevice, int)} will be called
+ * for each device within the group.
+ *
* @param groupId is the device group identifier for which want to set the active preset
* @param presetIndex is an index of one of the available presets
- * @return true if valid group request was sent, false otherwise
* @hide
*/
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean groupSelectActivePreset(int groupId, int presetIndex) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public void selectPresetForGroup(int groupId, int presetIndex) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
if (service == null) {
@@ -724,14 +974,14 @@
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled()) {
try {
+ // TODO(b/216639668)
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
service.groupSelectActivePreset(groupId, presetIndex, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
}
/**
@@ -741,13 +991,14 @@
* does not necessarily mean a higher preset index.
*
* @param device is the device for which we want to set the active preset
- * @return true if valid request was sent, false otherwise
* @hide
*/
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean nextActivePreset(@NonNull BluetoothDevice device) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public void switchToNextPreset(@NonNull BluetoothDevice device) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
if (service == null) {
@@ -755,14 +1006,14 @@
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled() && isValidDevice(device)) {
try {
+ // TODO(b/216639668)
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
service.nextActivePreset(device, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
}
/**
@@ -774,13 +1025,14 @@
* valid HAS group. Note that binaural HA devices may or may not support group.
*
* @param groupId is the device group identifier for which want to set the active preset
- * @return true if valid group request was sent, false otherwise
* @hide
*/
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean groupNextActivePreset(int groupId) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public void switchToNextPresetForGroup(int groupId) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
if (service == null) {
@@ -788,14 +1040,14 @@
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled()) {
try {
+ // TODO(b/216639668)
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
service.groupNextActivePreset(groupId, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
}
/**
@@ -805,13 +1057,14 @@
* does not necessarily mean a lower preset index.
*
* @param device is the device for which we want to set the active preset
- * @return true if valid request was sent, false otherwise
* @hide
*/
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean previousActivePreset(@NonNull BluetoothDevice device) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public void switchToPreviousPreset(@NonNull BluetoothDevice device) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
if (service == null) {
@@ -819,14 +1072,14 @@
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled() && isValidDevice(device)) {
try {
+ // TODO(b/216639668)
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
service.previousActivePreset(device, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
}
/**
@@ -838,13 +1091,14 @@
* valid HAS group. Note that binaural HA devices may or may not support group.
*
* @param groupId is the device group identifier for which want to set the active preset
- * @return true if valid group request was sent, false otherwise
* @hide
*/
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean groupPreviousActivePreset(int groupId) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public void switchToPreviousPresetForGroup(int groupId) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
if (service == null) {
@@ -852,14 +1106,14 @@
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled()) {
try {
+ // TODO(b/216639668)
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
service.groupPreviousActivePreset(groupId, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
}
/**
@@ -867,54 +1121,62 @@
*
* @param device is the device for which we want to get the preset name
* @param presetIndex is an index of one of the available presets
- * @return true if valid request was sent, false otherwise
+ * @return preset info
* @hide
*/
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean getPresetInfo(@NonNull BluetoothDevice device, int presetIndex) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public @NonNull BluetoothHapPresetInfo getPresetInfo(@NonNull BluetoothDevice device,
+ int presetIndex) {
final IBluetoothHapClient service = getService();
- final boolean defaultValue = false;
+ final BluetoothHapPresetInfo.Builder builder = new BluetoothHapPresetInfo.Builder();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled() && isValidDevice(device)) {
- try {
- final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
- service.getPresetInfo(device, presetIndex, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
- Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
- }
+ // TODO(b/216639668)
+ // try {
+ // final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ // service.getPresetInfo(device, presetIndex, mAttributionSource, recv);
+ // return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(builder);
+ // } catch (RemoteException | TimeoutException e) {
+ // Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ // }
}
- return defaultValue;
+ return builder.build();
}
/**
- * Requests all presets info
+ * Get all preset info for a particular device
*
* @param device is the device for which we want to get all presets info
- * @return true if request was processed, false otherwise
+ * @return a list of all known preset info
* @hide
*/
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean getAllPresetsInfo(@NonNull BluetoothDevice device) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public @NonNull List<BluetoothHapPresetInfo> getAllPresetInfo(@NonNull BluetoothDevice device) {
final IBluetoothHapClient service = getService();
- final boolean defaultValue = false;
+ final List<BluetoothHapPresetInfo> defaultValue = new ArrayList<BluetoothHapPresetInfo>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled() && isValidDevice(device)) {
- try {
- final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
- service.getAllPresetsInfo(device, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
- Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
- }
+ // TODO(b/216639668)
+ // try {
+ // final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ // service.getAllPresetsInfo(device, mAttributionSource, recv);
+ // return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ // } catch (RemoteException | TimeoutException e) {
+ // Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ // }
}
return defaultValue;
}
@@ -927,8 +1189,10 @@
* @hide
*/
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
public boolean getFeatures(@NonNull BluetoothDevice device) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
@@ -948,21 +1212,28 @@
}
/**
- * Sets the preset name
+ * Sets the preset name for a particular device
*
- * <p> Note that the name length is restricted to 30 characters.
+ * <p> Note that the name length is restricted to 40 characters.
+ *
+ * On success, {@link Callback#onPresetInfoChanged(BluetoothDevice, List, int)}
+ * with a new name will be called and reason code
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}
+ * On failure, {@link Callback#onSetPresetNameFailed(BluetoothDevice, int)} will be called.
*
* @param device is the device for which we want to get the preset name
* @param presetIndex is an index of one of the available presets
- * @param name is a new name for a preset
- * @return true if valid request was sent, false otherwise
+ * @param name is a new name for a preset, maximum length is 40 characters
* @hide
*/
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean setPresetName(@NonNull BluetoothDevice device, int presetIndex,
- @NonNull String name) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public void setPresetName(@NonNull BluetoothDevice device, int presetIndex,
+ @NonNull String name) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
if (service == null) {
@@ -970,31 +1241,38 @@
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled() && isValidDevice(device)) {
try {
+ // TODO(b/216639668)
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
service.setPresetName(device, presetIndex, name, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
}
/**
- * Sets the preset name
+ * Sets the name for a hearing aid preset.
*
- * <p> Note that the name length is restricted to 30 characters.
+ * <p> Note that the name length is restricted to 40 characters.
+ *
+ * On success, {@link Callback#onPresetInfoChanged(BluetoothDevice, List, int)}
+ * with a new name will be called for each device within the group with reason code
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}
+ * On failure, {@link Callback#onSetPresetNameForGroupFailed(int, int)} will be invoked
*
* @param groupId is the device group identifier
* @param presetIndex is an index of one of the available presets
- * @param name is a new name for a preset
- * @return true if valid request was sent, false otherwise
+ * @param name is a new name for a preset, maximum length is 40 characters
* @hide
*/
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
- public boolean groupSetPresetName(int groupId, int presetIndex, @NonNull String name) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ public void setPresetNameForGroup(int groupId, int presetIndex, @NonNull String name) {
final IBluetoothHapClient service = getService();
final boolean defaultValue = false;
if (service == null) {
@@ -1002,14 +1280,14 @@
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled()) {
try {
+ // TODO(b/216639668)
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
service.groupSetPresetName(groupId, presetIndex, name, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
}
private boolean isEnabled() {
diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java
index cb543bd..aa34d0d 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudio.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudio.java
@@ -92,9 +92,13 @@
*
* @hide
*/
+ @SystemApi
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED =
"android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED";
@@ -112,12 +116,15 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @SystemApi
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED =
"android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED";
-
/**
* Intent used to broadcast group status information.
*
@@ -256,8 +263,6 @@
/**
* This represents an invalid group ID.
- *
- * @hide
*/
public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
@@ -272,6 +277,7 @@
* Contains group id.
* @hide
*/
+ @SystemApi
public static final String EXTRA_LE_AUDIO_GROUP_ID =
"android.bluetooth.extra.LE_AUDIO_GROUP_ID";
@@ -285,6 +291,7 @@
* <p>
* @hide
*/
+ @SystemApi
public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS =
"android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS";
@@ -348,12 +355,14 @@
* Indicating that node has been added to the group.
* @hide
*/
+ @SystemApi
public static final int GROUP_NODE_ADDED = IBluetoothLeAudio.GROUP_NODE_ADDED;
/**
* Indicating that node has been removed from the group.
* @hide
*/
+ @SystemApi
public static final int GROUP_NODE_REMOVED = IBluetoothLeAudio.GROUP_NODE_REMOVED;
private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector =
@@ -675,7 +684,8 @@
* Get device group id. Devices with same group id belong to same group (i.e left and right
* earbud)
* @param device LE Audio capable device
- * @return group id that this device currently belongs to
+ * @return group id that this device currently belongs to, {@link #GROUP_ID_INVALID} when this
+ * device does not belong to any group
*/
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
index 81f5d18..870ab0f 100644
--- a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
@@ -344,8 +344,7 @@
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
@Override
- @NonNull
- public List<BluetoothDevice> getConnectedDevices() {
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
return Collections.emptyList();
}
diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java
index 01e30db..8825c3e 100644
--- a/framework/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java
@@ -221,6 +221,15 @@
public static final int ERROR_ALREADY_IN_TARGET_STATE = 26;
/**
+ * Indicates that the requested operation is not supported by the remote device
+ *
+ * Caller should stop trying this operation
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_REMOTE_OPERATION_NOT_SUPPORTED = 24;
+
+ /**
* A GATT writeCharacteristic request is not permitted on the remote device.
*/
public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200;
@@ -454,6 +463,48 @@
public static final int ERROR_LE_CONTENT_METADATA_INVALID_OTHER = 1206;
/**
+ * Indicates that provided group ID is invalid for the coordinated set
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_CSIP_INVALID_GROUP_ID = 1207;
+
+ /**
+ * Indicating that CSIP group locked failed due to group member being already locked.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_CSIP_GROUP_LOCKED_BY_OTHER = 1208;
+
+ /**
+ * Indicating that CSIP device has been lost while being locked.
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_CSIP_LOCKED_GROUP_MEMBER_LOST = 1209;
+
+ /**
+ * Indicates that the set preset name is too long.
+ * <p>
+ * Example solution: Try using shorter name.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_HAP_PRESET_NAME_TOO_LONG = 1210;
+
+ /**
+ * Indicates that provided preset index parameters is invalid
+ * <p>
+ * Example solution: Use preset index of a known existing preset.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_HAP_INVALID_PRESET_INDEX = 1211;
+
+ /**
* Indicates that the RFCOMM listener could not be started due to the requested UUID already
* being in use.
*
diff --git a/system/binder/android/bluetooth/IBluetoothCsipSetCoordinator.aidl b/system/binder/android/bluetooth/IBluetoothCsipSetCoordinator.aidl
index e001edc..91b40e7 100644
--- a/system/binder/android/bluetooth/IBluetoothCsipSetCoordinator.aidl
+++ b/system/binder/android/bluetooth/IBluetoothCsipSetCoordinator.aidl
@@ -83,4 +83,12 @@
const int CSIS_GROUP_ID_INVALID = -1;
const int CSIS_GROUP_SIZE_UNKNOWN = 1;
+
+ const int CSIS_GROUP_LOCK_SUCCESS = 0;
+ const int CSIS_GROUP_LOCK_FAILED_INVALID_GROUP = 1;
+ const int CSIS_GROUP_LOCK_FAILED_GROUP_EMPTY = 2;
+ const int CSIS_GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3;
+ const int CSIS_GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4;
+ const int CSIS_GROUP_LOCK_FAILED_OTHER_REASON = 5;
+ const int CSIS_LOCKED_GROUP_MEMBER_LOST = 6;
}