Make BluetoothHeadset APIs used by Telecom into System APIs
Tag: #feature Bug: 195156304 Test: Manual CTS-Coverage-Bug: 211914959 Change-Id: I13d6f553f5b9d6ca7c2ae9a42450875b16cd1490
This commit is contained in:
@@ -9742,13 +9742,14 @@ package android.bluetooth {
|
|||||||
field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2
|
field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2
|
||||||
field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1
|
field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1
|
||||||
field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3
|
field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3
|
||||||
field public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; // 0xa
|
field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200; // 0xc8
|
||||||
field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; // 0x65
|
field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 201; // 0xc9
|
||||||
field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; // 0x66
|
|
||||||
field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
|
field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
|
||||||
field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8
|
field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8
|
||||||
field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9
|
field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9
|
||||||
field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
|
field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
|
||||||
|
field public static final int FEATURE_NOT_SUPPORTED = 11; // 0xb
|
||||||
|
field public static final int FEATURE_SUPPORTED = 10; // 0xa
|
||||||
field public static final int SUCCESS = 0; // 0x0
|
field public static final int SUCCESS = 0; // 0x0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2218,9 +2218,12 @@ package android.bluetooth {
|
|||||||
|
|
||||||
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
|
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean connect(android.bluetooth.BluetoothDevice);
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean connect(android.bluetooth.BluetoothDevice);
|
||||||
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int connectAudio();
|
||||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(android.bluetooth.BluetoothDevice);
|
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(android.bluetooth.BluetoothDevice);
|
||||||
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int disconnectAudio();
|
||||||
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioState(@NonNull android.bluetooth.BluetoothDevice);
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
|
||||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled();
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInbandRingingEnabled();
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean startScoUsingVirtualVoiceCall();
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean startScoUsingVirtualVoiceCall();
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean stopScoUsingVirtualVoiceCall();
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean stopScoUsingVirtualVoiceCall();
|
||||||
@@ -2299,6 +2302,14 @@ package android.bluetooth {
|
|||||||
|
|
||||||
public final class BluetoothStatusCodes {
|
public final class BluetoothStatusCodes {
|
||||||
field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
|
field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
|
||||||
|
field public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116; // 0x45c
|
||||||
|
field public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117; // 0x45d
|
||||||
|
field public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; // 0x45e
|
||||||
|
field public static final int ERROR_CALL_ACTIVE = 1119; // 0x45f
|
||||||
|
field public static final int ERROR_NOT_ACTIVE_DEVICE = 12; // 0xc
|
||||||
|
field public static final int ERROR_NO_ACTIVE_DEVICES = 13; // 0xd
|
||||||
|
field public static final int ERROR_PROFILE_NOT_CONNECTED = 14; // 0xe
|
||||||
|
field public static final int ERROR_TIMEOUT = 15; // 0xf
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class BluetoothUuid {
|
public final class BluetoothUuid {
|
||||||
|
|||||||
@@ -2249,17 +2249,17 @@ public final class BluetoothAdapter {
|
|||||||
/** @hide */
|
/** @hide */
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@IntDef(value = {
|
@IntDef(value = {
|
||||||
BluetoothStatusCodes.SUCCESS,
|
BluetoothStatusCodes.FEATURE_SUPPORTED,
|
||||||
BluetoothStatusCodes.ERROR_UNKNOWN,
|
BluetoothStatusCodes.ERROR_UNKNOWN,
|
||||||
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
|
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
|
||||||
BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED,
|
BluetoothStatusCodes.FEATURE_NOT_SUPPORTED,
|
||||||
})
|
})
|
||||||
public @interface LeFeatureReturnValues {}
|
public @interface LeFeatureReturnValues {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is
|
* Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio feature is
|
||||||
* supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if
|
* supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not
|
||||||
* the feature is not supported or an error code.
|
* supported, or an error code.
|
||||||
*
|
*
|
||||||
* @return whether the LE audio is supported
|
* @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
|
* Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if LE Periodic Advertising Sync
|
||||||
* feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the
|
* Transfer Sender feature is supported,
|
||||||
* feature is not supported or an error code
|
* {@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
|
* @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package android.bluetooth;
|
|||||||
|
|
||||||
import static android.bluetooth.BluetoothUtils.getSyncTimeout;
|
import static android.bluetooth.BluetoothUtils.getSyncTimeout;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.RequiresPermission;
|
import android.annotation.RequiresPermission;
|
||||||
@@ -44,6 +45,8 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.android.modules.utils.SynchronousResultReceiver;
|
import com.android.modules.utils.SynchronousResultReceiver;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeoutException;
|
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);
|
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.
|
* 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
|
* @hide
|
||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
@SystemApi
|
||||||
@RequiresBluetoothConnectPermission
|
@RequiresBluetoothConnectPermission
|
||||||
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
|
@RequiresPermission(allOf = {
|
||||||
public int getAudioState(BluetoothDevice device) {
|
android.Manifest.permission.BLUETOOTH_CONNECT,
|
||||||
|
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
|
||||||
|
})
|
||||||
|
public @GetAudioStateReturnValues int getAudioState(@NonNull BluetoothDevice device) {
|
||||||
if (VDBG) log("getAudioState");
|
if (VDBG) log("getAudioState");
|
||||||
|
if (device == null) {
|
||||||
|
throw new IllegalArgumentException("device cannot be null");
|
||||||
|
}
|
||||||
final IBluetoothHeadset service = mService;
|
final IBluetoothHeadset service = mService;
|
||||||
final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
|
final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
@@ -916,8 +938,12 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
|||||||
final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
|
final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
|
||||||
service.getAudioState(device, mAttributionSource, recv);
|
service.getAudioState(device, mAttributionSource, recv);
|
||||||
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
|
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
|
||||||
} catch (RemoteException | TimeoutException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
|
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;
|
return defaultValue;
|
||||||
@@ -1005,103 +1031,112 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @hide */
|
||||||
* Check if at least one headset's SCO audio is connected or connecting
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
*
|
@IntDef(value = {
|
||||||
* @return true if at least one device's SCO audio is connected or connecting, false otherwise
|
BluetoothStatusCodes.SUCCESS,
|
||||||
* or on error
|
BluetoothStatusCodes.ERROR_UNKNOWN,
|
||||||
* @hide
|
BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
|
||||||
*/
|
BluetoothStatusCodes.ERROR_TIMEOUT,
|
||||||
@RequiresLegacyBluetoothPermission
|
BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED,
|
||||||
@RequiresBluetoothConnectPermission
|
BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES,
|
||||||
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
|
BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE,
|
||||||
public boolean isAudioOn() {
|
BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED,
|
||||||
if (VDBG) log("isAudioOn()");
|
BluetoothStatusCodes.ERROR_CALL_ACTIVE,
|
||||||
final IBluetoothHeadset service = mService;
|
BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED
|
||||||
final boolean defaultValue = false;
|
})
|
||||||
if (service == null) {
|
public @interface ConnectAudioReturnValues {}
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates a connection of headset audio to the current active device
|
* 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.
|
||||||
*
|
*
|
||||||
* <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
|
* @return whether the connection was successfully initiated or an error code on failure
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* @return false if there was some error such as there is no active headset
|
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage
|
@SystemApi
|
||||||
@RequiresBluetoothConnectPermission
|
@RequiresBluetoothConnectPermission
|
||||||
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
|
@RequiresPermission(allOf = {
|
||||||
public boolean connectAudio() {
|
android.Manifest.permission.BLUETOOTH_CONNECT,
|
||||||
|
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
|
||||||
|
})
|
||||||
|
public @ConnectAudioReturnValues int connectAudio() {
|
||||||
if (VDBG) log("connectAudio()");
|
if (VDBG) log("connectAudio()");
|
||||||
final IBluetoothHeadset service = mService;
|
final IBluetoothHeadset service = mService;
|
||||||
final boolean defaultValue = false;
|
final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN;
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Log.w(TAG, "Proxy not attached to service");
|
Log.w(TAG, "Proxy not attached to service");
|
||||||
if (DBG) log(Log.getStackTraceString(new Throwable()));
|
if (DBG) log(Log.getStackTraceString(new Throwable()));
|
||||||
} else if (isEnabled()) {
|
} else if (isEnabled()) {
|
||||||
try {
|
try {
|
||||||
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
|
final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
|
||||||
service.connectAudio(mAttributionSource, recv);
|
service.connectAudio(mAttributionSource, recv);
|
||||||
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
|
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
|
||||||
} catch (RemoteException | TimeoutException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
|
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;
|
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.
|
* Initiates a disconnection of HFP SCO audio from actively connected devices. It also tears
|
||||||
* Tear down voice recognition or virtual voice call if any.
|
* down voice recognition or virtual voice call, if any exists.
|
||||||
*
|
*
|
||||||
* <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
|
* <p> If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent
|
||||||
* If this function returns true, this intent will be broadcasted with
|
* {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted with {@link #EXTRA_STATE} set to
|
||||||
* {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
|
* {@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
|
* @hide
|
||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage
|
@SystemApi
|
||||||
@RequiresBluetoothConnectPermission
|
@RequiresBluetoothConnectPermission
|
||||||
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
|
@RequiresPermission(allOf = {
|
||||||
public boolean disconnectAudio() {
|
android.Manifest.permission.BLUETOOTH_CONNECT,
|
||||||
|
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
|
||||||
|
})
|
||||||
|
public @DisconnectAudioReturnValues int disconnectAudio() {
|
||||||
if (VDBG) log("disconnectAudio()");
|
if (VDBG) log("disconnectAudio()");
|
||||||
final IBluetoothHeadset service = mService;
|
final IBluetoothHeadset service = mService;
|
||||||
final boolean defaultValue = false;
|
final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN;
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Log.w(TAG, "Proxy not attached to service");
|
Log.w(TAG, "Proxy not attached to service");
|
||||||
if (DBG) log(Log.getStackTraceString(new Throwable()));
|
if (DBG) log(Log.getStackTraceString(new Throwable()));
|
||||||
} else if (isEnabled()) {
|
} else if (isEnabled()) {
|
||||||
try {
|
try {
|
||||||
final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
|
final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
|
||||||
service.disconnectAudio(mAttributionSource, recv);
|
service.disconnectAudio(mAttributionSource, recv);
|
||||||
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
|
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
|
||||||
} catch (RemoteException | TimeoutException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
|
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;
|
return defaultValue;
|
||||||
@@ -1386,7 +1421,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
|||||||
@SystemApi
|
@SystemApi
|
||||||
@RequiresLegacyBluetoothPermission
|
@RequiresLegacyBluetoothPermission
|
||||||
@RequiresBluetoothConnectPermission
|
@RequiresBluetoothConnectPermission
|
||||||
@RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
|
@RequiresPermission(allOf = {
|
||||||
|
android.Manifest.permission.BLUETOOTH_CONNECT,
|
||||||
|
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
|
||||||
|
})
|
||||||
public boolean isInbandRingingEnabled() {
|
public boolean isInbandRingingEnabled() {
|
||||||
if (DBG) log("isInbandRingingEnabled()");
|
if (DBG) log("isInbandRingingEnabled()");
|
||||||
final IBluetoothHeadset service = mService;
|
final IBluetoothHeadset service = mService;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import android.annotation.SystemApi;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A class with constants representing possible return values for Bluetooth APIs. General return
|
* 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
|
* API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which
|
||||||
* occupies the max integer value.
|
* occupies the max integer value.
|
||||||
*/
|
*/
|
||||||
@@ -29,28 +29,28 @@ public final class BluetoothStatusCodes {
|
|||||||
private BluetoothStatusCodes() {}
|
private BluetoothStatusCodes() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the API call was successful
|
* Indicates that the API call was successful.
|
||||||
*/
|
*/
|
||||||
public static final int SUCCESS = 0;
|
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;
|
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
|
* 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;
|
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;
|
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
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -58,7 +58,7 @@ public final class BluetoothStatusCodes {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Error code indicating that the caller does not have the
|
* 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
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -66,13 +66,13 @@ public final class BluetoothStatusCodes {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Error code indicating that the caller does not have the
|
* 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;
|
public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error code indicating that the caller does not have the
|
* 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
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -80,30 +80,67 @@ public final class BluetoothStatusCodes {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Error code indicating that the caller does not have the
|
* 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;
|
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
|
* 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;
|
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.
|
* 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.
|
* 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
|
* If another application has already requested {@link OobData} then another fetch will be
|
||||||
@@ -285,6 +322,38 @@ public final class BluetoothStatusCodes {
|
|||||||
*/
|
*/
|
||||||
public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115;
|
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.
|
* Indicates that an unknown error has occurred has occurred.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ applications that come with the platform
|
|||||||
<privapp-permissions package="com.android.server.telecom">
|
<privapp-permissions package="com.android.server.telecom">
|
||||||
<permission name="android.permission.BIND_CONNECTION_SERVICE"/>
|
<permission name="android.permission.BIND_CONNECTION_SERVICE"/>
|
||||||
<permission name="android.permission.BIND_INCALL_SERVICE"/>
|
<permission name="android.permission.BIND_INCALL_SERVICE"/>
|
||||||
|
<permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
|
||||||
<permission name="android.permission.CALL_PRIVILEGED"/>
|
<permission name="android.permission.CALL_PRIVILEGED"/>
|
||||||
<permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/>
|
<permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/>
|
||||||
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
|
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
|
||||||
|
|||||||
@@ -138,13 +138,6 @@ public class HeadsetProfile implements LocalBluetoothProfile {
|
|||||||
return mService.getActiveDevice();
|
return mService.getActiveDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAudioOn() {
|
|
||||||
if (mService == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mService.isAudioOn();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAudioState(BluetoothDevice device) {
|
public int getAudioState(BluetoothDevice device) {
|
||||||
if (mService == null) {
|
if (mService == null) {
|
||||||
return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
|
return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
|
||||||
|
|||||||
@@ -54,15 +54,6 @@ public class HeadsetProfileTest {
|
|||||||
mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mService);
|
mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void bluetoothProfile_shouldReturnTheAudioStatusFromBlueToothHeadsetService() {
|
|
||||||
when(mService.isAudioOn()).thenReturn(true);
|
|
||||||
assertThat(mProfile.isAudioOn()).isTrue();
|
|
||||||
|
|
||||||
when(mService.isAudioOn()).thenReturn(false);
|
|
||||||
assertThat(mProfile.isAudioOn()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHeadsetProfile_shouldReturnAudioState() {
|
public void testHeadsetProfile_shouldReturnAudioState() {
|
||||||
when(mService.getAudioState(mBluetoothDevice)).
|
when(mService.getAudioState(mBluetoothDevice)).
|
||||||
|
|||||||
@@ -102,8 +102,6 @@ public class BtHelper {
|
|||||||
/*package*/ static final int SCO_MODE_UNDEFINED = -1;
|
/*package*/ static final int SCO_MODE_UNDEFINED = -1;
|
||||||
// SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
|
// SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
|
||||||
/*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0;
|
/*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0;
|
||||||
// SCO audio mode is raw audio (BluetoothHeadset.connectAudio())
|
|
||||||
private static final int SCO_MODE_RAW = 1;
|
|
||||||
// SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
|
// SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
|
||||||
private static final int SCO_MODE_VR = 2;
|
private static final int SCO_MODE_VR = 2;
|
||||||
// max valid SCO audio mode values
|
// max valid SCO audio mode values
|
||||||
@@ -122,8 +120,6 @@ public class BtHelper {
|
|||||||
return "SCO_MODE_UNDEFINED";
|
return "SCO_MODE_UNDEFINED";
|
||||||
case SCO_MODE_VIRTUAL_CALL:
|
case SCO_MODE_VIRTUAL_CALL:
|
||||||
return "SCO_MODE_VIRTUAL_CALL";
|
return "SCO_MODE_VIRTUAL_CALL";
|
||||||
case SCO_MODE_RAW:
|
|
||||||
return "SCO_MODE_RAW";
|
|
||||||
case SCO_MODE_VR:
|
case SCO_MODE_VR:
|
||||||
return "SCO_MODE_VR";
|
return "SCO_MODE_VR";
|
||||||
default:
|
default:
|
||||||
@@ -812,8 +808,6 @@ public class BtHelper {
|
|||||||
private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
|
private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
|
||||||
BluetoothDevice device, int scoAudioMode) {
|
BluetoothDevice device, int scoAudioMode) {
|
||||||
switch (scoAudioMode) {
|
switch (scoAudioMode) {
|
||||||
case SCO_MODE_RAW:
|
|
||||||
return bluetoothHeadset.disconnectAudio();
|
|
||||||
case SCO_MODE_VIRTUAL_CALL:
|
case SCO_MODE_VIRTUAL_CALL:
|
||||||
return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
|
return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
|
||||||
case SCO_MODE_VR:
|
case SCO_MODE_VR:
|
||||||
@@ -826,8 +820,6 @@ public class BtHelper {
|
|||||||
private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
|
private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
|
||||||
BluetoothDevice device, int scoAudioMode) {
|
BluetoothDevice device, int scoAudioMode) {
|
||||||
switch (scoAudioMode) {
|
switch (scoAudioMode) {
|
||||||
case SCO_MODE_RAW:
|
|
||||||
return bluetoothHeadset.connectAudio();
|
|
||||||
case SCO_MODE_VIRTUAL_CALL:
|
case SCO_MODE_VIRTUAL_CALL:
|
||||||
return bluetoothHeadset.startScoUsingVirtualVoiceCall();
|
return bluetoothHeadset.startScoUsingVirtualVoiceCall();
|
||||||
case SCO_MODE_VR:
|
case SCO_MODE_VR:
|
||||||
|
|||||||
Reference in New Issue
Block a user