summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java17
-rw-r--r--framework/java/android/bluetooth/BluetoothHeadset.java170
-rw-r--r--framework/java/android/bluetooth/BluetoothStatusCodes.java99
3 files changed, 197 insertions, 89 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index 7e5e96d802..a695f6d2e7 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -2249,17 +2249,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, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not
+ * supported, or an error code.
*
* @return whether the LE audio is supported
*/
@@ -2282,9 +2282,10 @@ public final class BluetoothAdapter {
}
/**
- * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Periodic Advertising Sync Transfer Sender
- * 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 LE Periodic Advertising Sync
+ * Transfer Sender feature is supported,
+ * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not supported, or
+ * an error code
*
* @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature
*/
diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java
index f2a6276ce8..2ed1eb40f8 100644
--- a/framework/java/android/bluetooth/BluetoothHeadset.java
+++ b/framework/java/android/bluetooth/BluetoothHeadset.java
@@ -18,6 +18,7 @@ package android.bluetooth;
import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -44,6 +45,8 @@ import android.util.Log;
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.TimeoutException;
@@ -895,17 +898,36 @@ public final class BluetoothHeadset implements BluetoothProfile {
com.android.internal.R.bool.config_bluetooth_sco_off_call);
}
+ /** @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) {
@@ -916,8 +938,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;
@@ -1005,103 +1031,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;
@@ -1386,7 +1421,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 fff32fffd8..a8ce4b4118 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
- * user
+ * user.
*/
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,30 +80,67 @@ 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.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_TIMEOUT = 15;
/**
* 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;
/**
* If another application has already requested {@link OobData} then another fetch will be
@@ -286,6 +323,38 @@ public final class BluetoothStatusCodes {
public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115;
/**
+ * 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;
+
+ /**
* Indicates that an unknown error has occurred has occurred.
*/
public static final int ERROR_UNKNOWN = Integer.MAX_VALUE;