aboutsummaryrefslogtreecommitdiff
path: root/framework/java/android
diff options
context:
space:
mode:
authorRahul Sabnis <rahulsabnis@google.com>2022-02-25 22:34:55 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-02-25 22:34:55 +0000
commit2d2bcc4fbe22997cd324102aa15215b20eae3550 (patch)
treeb096cba1e7ed10ba05c77d634bb63a0150ed0284 /framework/java/android
parent9b5b431290ed707dd040a7f9e60a2a40bbb2cce7 (diff)
parentcea6a45264c1af6189ebd3bd5639a8a47ffe7796 (diff)
Merge changes from topic "hfp-audio-state-api-cleanup"
* changes: Fix crash in AdapterService#disconnectAudio due to ArrayIndexOutOfBoundsException Make BluetoothHeadset APIs used by Telecom into System APIs and update service code to match.
Diffstat (limited to 'framework/java/android')
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java18
-rw-r--r--framework/java/android/bluetooth/BluetoothHeadset.java166
-rw-r--r--framework/java/android/bluetooth/BluetoothStatusCodes.java91
3 files changed, 185 insertions, 90 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index 8bfe6d8236..fdd02f7818 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -2445,17 +2445,17 @@ public final class BluetoothAdapter {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
- BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.FEATURE_SUPPORTED,
BluetoothStatusCodes.ERROR_UNKNOWN,
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
- BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED,
+ BluetoothStatusCodes.FEATURE_NOT_SUPPORTED,
})
public @interface LeFeatureReturnValues {}
/**
- * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is
- * supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if
- * the feature is not supported or an error code.
+ * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio feature is
+ * supported, returns {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not
+ * supported, or an error code.
*
* @return whether the LE audio is supported
*/
@@ -2483,8 +2483,8 @@ public final class BluetoothAdapter {
}
/**
- * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio broadcast source
- * feature is supported, {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the
+ * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio broadcast source
+ * feature is supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the
* feature is not supported, or an error code.
*
* @return whether the LE audio broadcast source is supported
@@ -2514,8 +2514,8 @@ public final class BluetoothAdapter {
}
/**
- * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio broadcast assistant
- * feature is supported, {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the
+ * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio broadcast assistant
+ * feature is supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the
* feature is not supported, or an error code.
*
* @return whether the LE audio broadcast assistent is supported
diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java
index 9fd29de7a3..9532c1d3c0 100644
--- a/framework/java/android/bluetooth/BluetoothHeadset.java
+++ b/framework/java/android/bluetooth/BluetoothHeadset.java
@@ -915,18 +915,36 @@ public final class BluetoothHeadset implements BluetoothProfile {
return defaultValue;
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
+ BluetoothHeadset.STATE_AUDIO_CONNECTING,
+ BluetoothHeadset.STATE_AUDIO_CONNECTED,
+ BluetoothStatusCodes.ERROR_TIMEOUT
+ })
+ public @interface GetAudioStateReturnValues {}
/**
* Get the current audio state of the Headset.
- * Note: This is an internal function and shouldn't be exposed
+ *
+ * @param device is the Bluetooth device for which the audio state is being queried
+ * @return the audio state of the device or an error code
+ * @throws IllegalArgumentException if the device is null
*
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public int getAudioState(BluetoothDevice device) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @GetAudioStateReturnValues int getAudioState(@NonNull BluetoothDevice device) {
if (VDBG) log("getAudioState");
+ if (device == null) {
+ throw new IllegalArgumentException("device cannot be null");
+ }
final IBluetoothHeadset service = mService;
final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
if (service == null) {
@@ -937,8 +955,12 @@ public final class BluetoothHeadset implements BluetoothProfile {
final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
service.getAudioState(device, mAttributionSource, recv);
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ throw e.rethrowFromSystemServer();
+ } catch (TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ return BluetoothStatusCodes.ERROR_TIMEOUT;
}
}
return defaultValue;
@@ -1076,103 +1098,112 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
}
- /**
- * Check if at least one headset's SCO audio is connected or connecting
- *
- * @return true if at least one device's SCO audio is connected or connecting, false otherwise
- * or on error
- * @hide
- */
- @RequiresLegacyBluetoothPermission
- @RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public boolean isAudioOn() {
- if (VDBG) log("isAudioOn()");
- final IBluetoothHeadset service = mService;
- final boolean defaultValue = false;
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
- } else if (isEnabled()) {
- try {
- final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
- service.isAudioOn(mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
- Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
- }
- }
- return defaultValue;
- }
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
+ BluetoothStatusCodes.ERROR_TIMEOUT,
+ BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED,
+ BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES,
+ BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE,
+ BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED,
+ BluetoothStatusCodes.ERROR_CALL_ACTIVE,
+ BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED
+ })
+ public @interface ConnectAudioReturnValues {}
/**
- * Initiates a connection of headset audio to the current active device
- *
- * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
- * If this function returns true, this intent will be broadcasted with
- * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
- *
- * <p> {@link #EXTRA_STATE} will transition from
- * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
- * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
- * in case of failure to establish the audio connection.
- *
- * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true
- * before calling this method
+ * Initiates a connection of SCO audio to the current active HFP device. The active HFP device
+ * can be identified with {@link BluetoothAdapter#getActiveDevices(int)}.
+ * <p>
+ * If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent
+ * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted twice. First with {@link #EXTRA_STATE}
+ * set to {@link #STATE_AUDIO_CONNECTING}. This will be followed by a broadcast with
+ * {@link #EXTRA_STATE} set to either {@link #STATE_AUDIO_CONNECTED} if the audio connection is
+ * established or {@link #STATE_AUDIO_DISCONNECTED} if there was a failure in establishing the
+ * audio connection.
*
- * @return false if there was some error such as there is no active headset
+ * @return whether the connection was successfully initiated or an error code on failure
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public boolean connectAudio() {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @ConnectAudioReturnValues int connectAudio() {
if (VDBG) log("connectAudio()");
final IBluetoothHeadset service = mService;
- final boolean defaultValue = false;
+ final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled()) {
try {
- final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
service.connectAudio(mAttributionSource, recv);
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
+ } catch (RemoteException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ throw e.rethrowFromSystemServer();
+ } catch (TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ return BluetoothStatusCodes.ERROR_TIMEOUT;
}
}
return defaultValue;
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
+ BluetoothStatusCodes.ERROR_TIMEOUT,
+ BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED,
+ BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED
+ })
+ public @interface DisconnectAudioReturnValues {}
+
/**
- * Initiates a disconnection of HFP SCO audio.
- * Tear down voice recognition or virtual voice call if any.
+ * Initiates a disconnection of HFP SCO audio from actively connected devices. It also tears
+ * down voice recognition or virtual voice call, if any exists.
*
- * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
- * If this function returns true, this intent will be broadcasted with
- * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
+ * <p> If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent
+ * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted with {@link #EXTRA_STATE} set to
+ * {@link #STATE_AUDIO_DISCONNECTED}.
*
- * @return false if audio is not connected, or on error, true otherwise
+ * @return whether the disconnection was initiated successfully or an error code on failure
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public boolean disconnectAudio() {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @DisconnectAudioReturnValues int disconnectAudio() {
if (VDBG) log("disconnectAudio()");
final IBluetoothHeadset service = mService;
- final boolean defaultValue = false;
+ final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled()) {
try {
- final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
service.disconnectAudio(mAttributionSource, recv);
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ throw e.rethrowFromSystemServer();
+ } catch (TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ return BluetoothStatusCodes.ERROR_TIMEOUT;
}
}
return defaultValue;
@@ -1457,7 +1488,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
@SystemApi
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean isInbandRingingEnabled() {
if (DBG) log("isInbandRingingEnabled()");
final IBluetoothHeadset service = mService;
diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java
index 4f57e75583..49b0578f8c 100644
--- a/framework/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java
@@ -20,7 +20,7 @@ import android.annotation.SystemApi;
/**
* A class with constants representing possible return values for Bluetooth APIs. General return
- * values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999.
+ * values occupy the range 0 to 199. Profile-specific return values occupy the range 200-999.
* API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which
* occupies the max integer value.
*/
@@ -29,28 +29,28 @@ public final class BluetoothStatusCodes {
private BluetoothStatusCodes() {}
/**
- * Indicates that the API call was successful
+ * Indicates that the API call was successful.
*/
public static final int SUCCESS = 0;
/**
- * Error code indicating that Bluetooth is not enabled
+ * Error code indicating that Bluetooth is not enabled.
*/
public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1;
/**
* Error code indicating that the API call was initiated by neither the system nor the active
- * Zuser
+ * Zuser.
*/
public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2;
/**
- * Error code indicating that the Bluetooth Device specified is not bonded
+ * Error code indicating that the Bluetooth Device specified is not bonded.
*/
public static final int ERROR_DEVICE_NOT_BONDED = 3;
/**
- * Error code indicating that the Bluetooth Device specified is not connected, but is bonded
+ * Error code indicating that the Bluetooth Device specified is not connected, but is bonded.
*
* @hide
*/
@@ -58,7 +58,7 @@ public final class BluetoothStatusCodes {
/**
* Error code indicating that the caller does not have the
- * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission
+ * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission.
*
* @hide
*/
@@ -66,13 +66,13 @@ public final class BluetoothStatusCodes {
/**
* Error code indicating that the caller does not have the
- * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission
+ * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission.
*/
public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6;
/**
* Error code indicating that the caller does not have the
- * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission
+ * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission.
*
* @hide
*/
@@ -80,20 +80,49 @@ public final class BluetoothStatusCodes {
/**
* Error code indicating that the caller does not have the
- * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission
+ * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission.
*/
public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8;
/**
* Error code indicating that the profile service is not bound. You can bind a profile service
- * by calling {@link BluetoothAdapter#getProfileProxy}
+ * by calling {@link BluetoothAdapter#getProfileProxy}.
*/
public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9;
/**
- * Error code indicating that the feature is not supported.
+ * Indicates that the feature is supported.
*/
- public static final int ERROR_FEATURE_NOT_SUPPORTED = 10;
+ public static final int FEATURE_SUPPORTED = 10;
+
+ /**
+ * Indicates that the feature is not supported.
+ */
+ public static final int FEATURE_NOT_SUPPORTED = 11;
+
+ /**
+ * Error code indicating that the device is not the active device for this profile.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_NOT_ACTIVE_DEVICE = 12;
+
+ /**
+ * Error code indicating that there are no active devices for the profile.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_NO_ACTIVE_DEVICES = 13;
+
+ /**
+ * Indicates that the Bluetooth profile is not connected to this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_PROFILE_NOT_CONNECTED = 14;
/**
* Error code indicating that the requested operation timed out.
@@ -203,12 +232,12 @@ public final class BluetoothStatusCodes {
/**
* A GATT writeCharacteristic request is not permitted on the remote device.
*/
- public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101;
+ public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200;
/**
* A GATT writeCharacteristic request is issued to a busy remote device.
*/
- public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102;
+ public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 201;
/**
* Indicates that the operation is allowed.
@@ -346,6 +375,38 @@ public final class BluetoothStatusCodes {
*/
public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109;
+ /**
+ * Indicates that there is already one device for which SCO audio is connected or connecting.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116;
+
+ /**
+ * Indicates that SCO audio was already not connected for this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117;
+
+ /**
+ * Indicates that there audio route is currently blocked by the system.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118;
+
+ /**
+ * Indicates that there is an active call preventing this operation from succeeding.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_CALL_ACTIVE = 1119;
+
// LE audio related return codes reserved from 1200 to 1300
/**