summaryrefslogtreecommitdiff
path: root/framework/java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/java')
-rw-r--r--framework/java/android/bluetooth/BluetoothA2dp.java187
-rwxr-xr-xframework/java/android/bluetooth/BluetoothA2dpSink.java92
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java966
-rw-r--r--framework/java/android/bluetooth/BluetoothAudioConfig.java3
-rw-r--r--framework/java/android/bluetooth/BluetoothAvrcpController.java54
-rwxr-xr-xframework/java/android/bluetooth/BluetoothClass.java3
-rw-r--r--framework/java/android/bluetooth/BluetoothCodecConfig.java3
-rw-r--r--framework/java/android/bluetooth/BluetoothCodecStatus.java11
-rw-r--r--framework/java/android/bluetooth/BluetoothDevice.java465
-rw-r--r--framework/java/android/bluetooth/BluetoothDevicePicker.java6
-rw-r--r--framework/java/android/bluetooth/BluetoothGatt.java199
-rw-r--r--framework/java/android/bluetooth/BluetoothGattCharacteristic.java2
-rw-r--r--framework/java/android/bluetooth/BluetoothGattDescriptor.java4
-rw-r--r--framework/java/android/bluetooth/BluetoothGattServer.java106
-rw-r--r--framework/java/android/bluetooth/BluetoothGattService.java8
-rw-r--r--framework/java/android/bluetooth/BluetoothHeadset.java266
-rw-r--r--framework/java/android/bluetooth/BluetoothHeadsetClient.java160
-rw-r--r--framework/java/android/bluetooth/BluetoothHeadsetClientCall.java10
-rw-r--r--framework/java/android/bluetooth/BluetoothHealth.java53
-rw-r--r--framework/java/android/bluetooth/BluetoothHearingAid.java125
-rw-r--r--framework/java/android/bluetooth/BluetoothHidDevice.java141
-rw-r--r--framework/java/android/bluetooth/BluetoothHidHost.java148
-rw-r--r--framework/java/android/bluetooth/BluetoothInputStream.java3
-rw-r--r--framework/java/android/bluetooth/BluetoothLeAudio.java86
-rw-r--r--framework/java/android/bluetooth/BluetoothManager.java104
-rw-r--r--framework/java/android/bluetooth/BluetoothMap.java92
-rw-r--r--framework/java/android/bluetooth/BluetoothMapClient.java136
-rw-r--r--framework/java/android/bluetooth/BluetoothMasInstance.java3
-rw-r--r--framework/java/android/bluetooth/BluetoothOutputStream.java3
-rw-r--r--framework/java/android/bluetooth/BluetoothPan.java82
-rw-r--r--framework/java/android/bluetooth/BluetoothPbap.java62
-rw-r--r--framework/java/android/bluetooth/BluetoothPbapClient.java83
-rw-r--r--framework/java/android/bluetooth/BluetoothProfile.java9
-rw-r--r--framework/java/android/bluetooth/BluetoothProfileConnector.java26
-rw-r--r--framework/java/android/bluetooth/BluetoothSap.java93
-rw-r--r--framework/java/android/bluetooth/BluetoothServerSocket.java5
-rw-r--r--framework/java/android/bluetooth/BluetoothSocket.java20
-rw-r--r--framework/java/android/bluetooth/BluetoothStatusCodes.java205
-rw-r--r--framework/java/android/bluetooth/BluetoothUuid.java2
-rw-r--r--framework/java/android/bluetooth/BluetoothVolumeControl.java53
-rw-r--r--framework/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java39
-rw-r--r--framework/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java39
-rw-r--r--framework/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java41
-rw-r--r--framework/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java39
-rw-r--r--framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java39
-rw-r--r--framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java39
-rw-r--r--framework/java/android/bluetooth/le/AdvertiseData.java2
-rw-r--r--framework/java/android/bluetooth/le/AdvertisingSet.java62
-rw-r--r--framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java78
-rw-r--r--framework/java/android/bluetooth/le/BluetoothLeScanner.java100
-rw-r--r--framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java40
-rw-r--r--framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java2
-rw-r--r--framework/java/android/bluetooth/le/ScanFilter.java2
-rw-r--r--framework/java/android/bluetooth/le/ScanRecord.java24
-rw-r--r--framework/java/android/bluetooth/le/ScanResult.java12
-rw-r--r--framework/java/android/bluetooth/le/TruncatedFilter.java2
56 files changed, 3397 insertions, 1242 deletions
diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java
index 16413e1a1d..65cdca9eb7 100644
--- a/framework/java/android/bluetooth/BluetoothA2dp.java
+++ b/framework/java/android/bluetooth/BluetoothA2dp.java
@@ -20,11 +20,17 @@ import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
@@ -69,10 +75,10 @@ public final class BluetoothA2dp implements BluetoothProfile {
* <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}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
@@ -90,15 +96,17 @@ public final class BluetoothA2dp implements BluetoothProfile {
*
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PLAYING_STATE_CHANGED =
"android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
"android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
@@ -112,11 +120,11 @@ public final class BluetoothA2dp implements BluetoothProfile {
* be null if no device is active. </li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage(trackingBug = 171933273)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
@@ -133,11 +141,11 @@ public final class BluetoothA2dp implements BluetoothProfile {
* connected, otherwise it is not included.</li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage(trackingBug = 181103983)
public static final String ACTION_CODEC_CONFIG_CHANGED =
@@ -258,7 +266,8 @@ public final class BluetoothA2dp implements BluetoothProfile {
@SystemApi
public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp",
IBluetoothA2dp.class.getName()) {
@@ -272,8 +281,10 @@ public final class BluetoothA2dp implements BluetoothProfile {
* Create a BluetoothA2dp proxy object for interacting with the local
* Bluetooth A2DP service.
*/
- /*package*/ BluetoothA2dp(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ /* package */ BluetoothA2dp(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -307,7 +318,9 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
@@ -347,7 +360,9 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
@@ -368,12 +383,16 @@ public final class BluetoothA2dp implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevicesWithAttribution(mAttributionSource),
+ mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
@@ -387,12 +406,17 @@ public final class BluetoothA2dp implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStatesWithAttribution(states,
+ mAttributionSource),
+ mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
@@ -406,6 +430,8 @@ public final class BluetoothA2dp implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
try {
@@ -441,7 +467,9 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
@@ -449,7 +477,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()
&& ((device == null) || isValidDevice(device))) {
- return service.setActiveDevice(device);
+ return service.setActiveDevice(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -468,13 +496,16 @@ public final class BluetoothA2dp implements BluetoothProfile {
*/
@UnsupportedAppUsage(trackingBug = 171933273)
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
if (VDBG) log("getActiveDevice()");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- return service.getActiveDevice();
+ return Attributable.setAttributionSource(
+ service.getActiveDevice(mAttributionSource), mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return null;
@@ -495,7 +526,11 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -514,7 +549,11 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -526,7 +565,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
&& connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
return false;
}
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -546,7 +585,9 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
@@ -554,7 +595,8 @@ public final class BluetoothA2dp implements BluetoothProfile {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()
&& isValidDevice(device)) {
- return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device));
+ return BluetoothAdapter.connectionPolicyToPriority(
+ service.getPriority(device, mAttributionSource));
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.PRIORITY_OFF;
@@ -576,14 +618,18 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()
&& isValidDevice(device)) {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -599,6 +645,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @return true if device supports absolute volume
* @hide
*/
+ @RequiresNoPermission
public boolean isAvrcpAbsoluteVolumeSupported() {
if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
try {
@@ -620,12 +667,14 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @param volume Absolute volume to be set on AVRCP side
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAvrcpAbsoluteVolume(int volume) {
if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- service.setAvrcpAbsoluteVolume(volume);
+ service.setAvrcpAbsoluteVolume(volume, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
} catch (RemoteException e) {
@@ -636,16 +685,17 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Check if A2DP profile is streaming music.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device BluetoothDevice device
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isA2dpPlaying(BluetoothDevice device) {
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()
&& isValidDevice(device)) {
- return service.isA2dpPlaying(device);
+ return service.isA2dpPlaying(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -662,6 +712,8 @@ public final class BluetoothA2dp implements BluetoothProfile {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean shouldSendVolumeKeys(BluetoothDevice device) {
if (isEnabled() && isValidDevice(device)) {
ParcelUuid[] uuids = device.getUuids();
@@ -686,14 +738,16 @@ public final class BluetoothA2dp implements BluetoothProfile {
*/
@UnsupportedAppUsage(trackingBug = 181103983)
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
verifyDeviceNotNull(device, "getCodecStatus");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- return service.getCodecStatus(device);
+ return service.getCodecStatus(device, mAttributionSource);
}
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
@@ -714,7 +768,9 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(trackingBug = 181103983)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setCodecConfigPreference(@NonNull BluetoothDevice device,
@NonNull BluetoothCodecConfig codecConfig) {
if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
@@ -726,7 +782,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- service.setCodecConfigPreference(device, codecConfig);
+ service.setCodecConfigPreference(device, codecConfig, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return;
@@ -744,7 +800,9 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void enableOptionalCodecs(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
verifyDeviceNotNull(device, "enableOptionalCodecs");
@@ -759,7 +817,9 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void disableOptionalCodecs(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
verifyDeviceNotNull(device, "disableOptionalCodecs");
@@ -773,14 +833,15 @@ public final class BluetoothA2dp implements BluetoothProfile {
* active A2DP Bluetooth device.
* @param enable if true, enable the optional codecs, other disable them
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
if (enable) {
- service.enableOptionalCodecs(device);
+ service.enableOptionalCodecs(device, mAttributionSource);
} else {
- service.disableOptionalCodecs(device);
+ service.disableOptionalCodecs(device, mAttributionSource);
}
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
@@ -800,14 +861,16 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsSupportStatus
public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
verifyDeviceNotNull(device, "isOptionalCodecsSupported");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
- return service.supportsOptionalCodecs(device);
+ return service.supportsOptionalCodecs(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
@@ -826,14 +889,16 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsPreferenceStatus
public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
- return service.getOptionalCodecsEnabled(device);
+ return service.getOptionalCodecsEnabled(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return OPTIONAL_CODECS_PREF_UNKNOWN;
@@ -853,7 +918,9 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
@OptionalCodecsPreferenceStatus int value) {
verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
@@ -867,7 +934,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()
&& isValidDevice(device)) {
- service.setOptionalCodecsEnabled(device, value);
+ service.setOptionalCodecsEnabled(device, value, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return;
@@ -889,13 +956,17 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @Type int getDynamicBufferSupport() {
if (VDBG) log("getDynamicBufferSupport()");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- return service.getDynamicBufferSupport();
+ return service.getDynamicBufferSupport(mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return DYNAMIC_BUFFER_SUPPORT_NONE;
@@ -916,13 +987,17 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @Nullable BufferConstraints getBufferConstraints() {
if (VDBG) log("getBufferConstraints()");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- return service.getBufferConstraints();
+ return service.getBufferConstraints(mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return null;
@@ -942,14 +1017,18 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec,
int value) {
if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- return service.setBufferLengthMillis(codec, value);
+ return service.setBufferLengthMillis(codec, value, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java
index 67f3d7b5d7..2dd63a0263 100755
--- a/framework/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java
@@ -19,9 +19,16 @@ package android.bluetooth;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
@@ -67,11 +74,14 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*/
@SystemApi
@SuppressLint("ActionValue")
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK,
"BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) {
@@ -85,8 +95,10 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* Create a BluetoothA2dp proxy object for interacting with the local
* Bluetooth A2DP service.
*/
- /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ /* package */ BluetoothA2dpSink(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -121,13 +133,17 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device);
+ return service.connect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -160,13 +176,15 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -182,12 +200,15 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -203,12 +224,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -224,12 +249,14 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -243,8 +270,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* Get the current audio configuration for the A2DP source device,
* or null if the device has no audio configuration
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Remote bluetooth device.
* @return audio configuration for the device, or null
*
@@ -252,12 +277,15 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
if (VDBG) log("getAudioConfig(" + device + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getAudioConfig(device);
+ return service.getAudioConfig(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return null;
@@ -278,7 +306,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -297,7 +329,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -308,7 +344,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -328,7 +364,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -346,13 +386,17 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -371,12 +415,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean isAudioPlaying(@NonNull BluetoothDevice device) {
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.isA2dpPlaying(device);
+ return service.isA2dpPlaying(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index e305aa8e33..a9ddefbaea 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -19,19 +19,26 @@ package android.bluetooth;
import static java.util.Objects.requireNonNull;
-import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.app.PropertyInvalidatedCache;
import android.bluetooth.BluetoothDevice.Transport;
import android.bluetooth.BluetoothProfile.ConnectionPolicy;
+import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.PeriodicAdvertisingManager;
@@ -41,6 +48,8 @@ import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.BatteryStats;
import android.os.Binder;
@@ -66,6 +75,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -96,11 +106,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
* </p>
* <p>This class is thread safe.</p>
- * <p class="note"><strong>Note:</strong>
- * Most methods require the {@link android.Manifest.permission#BLUETOOTH}
- * permission and some also require the
- * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- * </p>
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>
@@ -142,8 +147,8 @@ public final class BluetoothAdapter {
* <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
* #EXTRA_PREVIOUS_STATE} containing the new and old states
* respectively.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
@@ -276,8 +281,10 @@ public final class BluetoothAdapter {
* <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
* for global notification whenever the scan mode changes. For example, an
* application can be notified when the device has ended discoverability.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
@@ -303,8 +310,10 @@ public final class BluetoothAdapter {
* has rejected the request or an error has occurred.
* <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
* for global notification whenever Bluetooth is turned on or off.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
@@ -323,10 +332,12 @@ public final class BluetoothAdapter {
* has rejected the request or an error has occurred.
* <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
* for global notification whenever Bluetooth is turned on or off.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE";
@@ -353,8 +364,10 @@ public final class BluetoothAdapter {
* <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
* #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
* respectively.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
@@ -506,15 +519,19 @@ public final class BluetoothAdapter {
* progress, and existing connections will experience limited bandwidth
* and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
* discovery.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
/**
* Broadcast Action: The local Bluetooth adapter has finished the device
* discovery process.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
@@ -524,8 +541,10 @@ public final class BluetoothAdapter {
* <p>This name is visible to remote Bluetooth devices.
* <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
* the name.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
/**
@@ -557,9 +576,10 @@ public final class BluetoothAdapter {
* {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE}
* can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
@@ -585,6 +605,7 @@ public final class BluetoothAdapter {
*
* @hide
*/
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@SystemApi public static final String ACTION_BLE_STATE_CHANGED =
"android.bluetooth.adapter.action.BLE_STATE_CHANGED";
@@ -599,6 +620,9 @@ public final class BluetoothAdapter {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED =
"android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED";
@@ -623,6 +647,9 @@ public final class BluetoothAdapter {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BLE_ACL_CONNECTED =
"android.bluetooth.adapter.action.BLE_ACL_CONNECTED";
@@ -637,6 +664,9 @@ public final class BluetoothAdapter {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BLE_ACL_DISCONNECTED =
"android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
@@ -677,20 +707,21 @@ public final class BluetoothAdapter {
*/
private static BluetoothAdapter sAdapter;
- private static BluetoothLeScanner sBluetoothLeScanner;
- private static BluetoothLeAdvertiser sBluetoothLeAdvertiser;
- private static PeriodicAdvertisingManager sPeriodicAdvertisingManager;
+ private BluetoothLeScanner mBluetoothLeScanner;
+ private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
+ private PeriodicAdvertisingManager mPeriodicAdvertisingManager;
private final IBluetoothManager mManagerService;
+ private final AttributionSource mAttributionSource;
+
@UnsupportedAppUsage
private IBluetooth mService;
- private Context mContext;
private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
private final Object mLock = new Object();
private final Map<LeScanCallback, ScanCallback> mLeScanClients;
- private static final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
- sMetadataListeners = new HashMap<>();
+ private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
+ mMetadataListeners = new HashMap<>();
private final Map<BluetoothConnectionCallback, Executor>
mBluetoothConnectionCallbackExecutorMap = new HashMap<>();
@@ -698,14 +729,16 @@ public final class BluetoothAdapter {
* Bluetooth metadata listener. Overrides the default BluetoothMetadataListener
* implementation.
*/
- private static final IBluetoothMetadataListener sBluetoothMetadataListener =
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
+ private final IBluetoothMetadataListener mBluetoothMetadataListener =
new IBluetoothMetadataListener.Stub() {
@Override
public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) {
- synchronized (sMetadataListeners) {
- if (sMetadataListeners.containsKey(device)) {
+ Attributable.setAttributionSource(device, mAttributionSource);
+ synchronized (mMetadataListeners) {
+ if (mMetadataListeners.containsKey(device)) {
List<Pair<OnMetadataChangedListener, Executor>> list =
- sMetadataListeners.get(device);
+ mMetadataListeners.get(device);
for (Pair<OnMetadataChangedListener, Executor> pair : list) {
OnMetadataChangedListener listener = pair.first;
Executor executor = pair.second;
@@ -721,32 +754,43 @@ public final class BluetoothAdapter {
/**
* Get a handle to the default local Bluetooth adapter.
- * <p>Currently Android only supports one Bluetooth adapter, but the API
- * could be extended to support more. This will always return the default
- * adapter.
+ * <p>
+ * Currently Android only supports one Bluetooth adapter, but the API could
+ * be extended to support more. This will always return the default adapter.
* </p>
*
- * @return the default local adapter, or null if Bluetooth is not supported on this hardware
- * platform
+ * @return the default local adapter, or null if Bluetooth is not supported
+ * on this hardware platform
+ * @deprecated this method will continue to work, but developers are
+ * strongly encouraged to migrate to using
+ * {@link BluetoothManager#getAdapter()}, since that approach
+ * enables support for {@link Context#createAttributionContext}.
*/
+ @Deprecated
+ @RequiresNoPermission
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
- IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
- sAdapter = new BluetoothAdapter(managerService);
- } else {
- Log.e(TAG, "Bluetooth binder is null");
- }
+ sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null));
}
return sAdapter;
}
+ /** {@hide} */
+ public static BluetoothAdapter createAdapter(AttributionSource attributionSource) {
+ IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
+ if (binder != null) {
+ return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder),
+ attributionSource);
+ } else {
+ Log.e(TAG, "Bluetooth binder is null");
+ return null;
+ }
+ }
+
/**
* Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
*/
- BluetoothAdapter(IBluetoothManager managerService) {
-
+ BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) {
if (managerService == null) {
throw new IllegalArgumentException("bluetooth manager service is null");
}
@@ -758,7 +802,8 @@ public final class BluetoothAdapter {
} finally {
mServiceLock.writeLock().unlock();
}
- mManagerService = managerService;
+ mManagerService = Objects.requireNonNull(managerService);
+ mAttributionSource = Objects.requireNonNull(attributionSource);
mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
mToken = new Binder();
}
@@ -775,8 +820,11 @@ public final class BluetoothAdapter {
* @param address valid Bluetooth MAC address
* @throws IllegalArgumentException if address is invalid
*/
+ @RequiresNoPermission
public BluetoothDevice getRemoteDevice(String address) {
- return new BluetoothDevice(address);
+ final BluetoothDevice res = new BluetoothDevice(address);
+ res.setAttributionSource(mAttributionSource);
+ return res;
}
/**
@@ -790,13 +838,16 @@ public final class BluetoothAdapter {
* @param address Bluetooth MAC address (6 bytes)
* @throws IllegalArgumentException if address is invalid
*/
+ @RequiresNoPermission
public BluetoothDevice getRemoteDevice(byte[] address) {
if (address == null || address.length != 6) {
throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
}
- return new BluetoothDevice(
+ final BluetoothDevice res = new BluetoothDevice(
String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1],
address[2], address[3], address[4], address[5]));
+ res.setAttributionSource(mAttributionSource);
+ return res;
}
/**
@@ -807,16 +858,17 @@ public final class BluetoothAdapter {
* Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported
* on this device before calling this method.
*/
+ @RequiresNoPermission
public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
if (!getLeAccess()) {
return null;
}
synchronized (mLock) {
- if (sBluetoothLeAdvertiser == null) {
- sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
+ if (mBluetoothLeAdvertiser == null) {
+ mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this);
}
+ return mBluetoothLeAdvertiser;
}
- return sBluetoothLeAdvertiser;
}
/**
@@ -829,6 +881,7 @@ public final class BluetoothAdapter {
*
* @hide
*/
+ @RequiresNoPermission
public PeriodicAdvertisingManager getPeriodicAdvertisingManager() {
if (!getLeAccess()) {
return null;
@@ -839,27 +892,27 @@ public final class BluetoothAdapter {
}
synchronized (mLock) {
- if (sPeriodicAdvertisingManager == null) {
- sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService);
+ if (mPeriodicAdvertisingManager == null) {
+ mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this);
}
+ return mPeriodicAdvertisingManager;
}
- return sPeriodicAdvertisingManager;
}
/**
* Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
*/
+ @RequiresNoPermission
public BluetoothLeScanner getBluetoothLeScanner() {
if (!getLeAccess()) {
return null;
}
synchronized (mLock) {
- if (sBluetoothLeScanner == null) {
- sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(),
- getAttributionTag());
+ if (mBluetoothLeScanner == null) {
+ mBluetoothLeScanner = new BluetoothLeScanner(this);
}
+ return mBluetoothLeScanner;
}
- return sBluetoothLeScanner;
}
/**
@@ -869,7 +922,8 @@ public final class BluetoothAdapter {
*
* @return true if the local adapter is turned on
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isEnabled() {
return getState() == BluetoothAdapter.STATE_ON;
}
@@ -883,6 +937,7 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
+ @RequiresNoPermission
public boolean isLeEnabled() {
final int state = getLeState();
if (DBG) {
@@ -920,13 +975,15 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disableBLE() {
if (!isBleScanAlwaysAvailable()) {
return false;
}
String packageName = ActivityThread.currentPackageName();
try {
- return mManagerService.disableBle(packageName, mToken);
+ return mManagerService.disableBle(mAttributionSource, mToken);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -965,13 +1022,15 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enableBLE() {
if (!isBleScanAlwaysAvailable()) {
return false;
}
String packageName = ActivityThread.currentPackageName();
try {
- return mManagerService.enableBle(packageName, mToken);
+ return mManagerService.enableBle(mAttributionSource, mToken);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -985,6 +1044,7 @@ public final class BluetoothAdapter {
new PropertyInvalidatedCache<Void, Integer>(
8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Integer recompute(Void query) {
try {
return mService.getState();
@@ -995,6 +1055,7 @@ public final class BluetoothAdapter {
};
/** @hide */
+ @RequiresNoPermission
public void disableBluetoothGetStateCache() {
mBluetoothGetStateCache.disableLocal();
}
@@ -1038,7 +1099,8 @@ public final class BluetoothAdapter {
*
* @return current state of Bluetooth adapter
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
@AdapterState
public int getState() {
int state = getStateInternal();
@@ -1074,7 +1136,8 @@ public final class BluetoothAdapter {
* @return current state of Bluetooth adapter
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
@AdapterState
@UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
+ "whether you can use BLE & BT classic.")
@@ -1121,7 +1184,9 @@ public final class BluetoothAdapter {
*
* @return true to indicate adapter startup has begun, or false on immediate error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enable() {
if (isEnabled()) {
if (DBG) {
@@ -1130,7 +1195,7 @@ public final class BluetoothAdapter {
return true;
}
try {
- return mManagerService.enable(ActivityThread.currentPackageName());
+ return mManagerService.enable(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1158,10 +1223,12 @@ public final class BluetoothAdapter {
*
* @return true to indicate adapter shutdown has begun, or false on immediate error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disable() {
try {
- return mManagerService.disable(ActivityThread.currentPackageName(), true);
+ return mManagerService.disable(mAttributionSource, true);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1171,17 +1238,17 @@ public final class BluetoothAdapter {
/**
* Turn off the local Bluetooth adapter and don't persist the setting.
*
- * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission
- *
* @return true to indicate adapter shutdown has begun, or false on immediate error
* @hide
*/
@UnsupportedAppUsage(trackingBug = 171933273)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disable(boolean persist) {
try {
- return mManagerService.disable(ActivityThread.currentPackageName(), persist);
+ return mManagerService.disable(mAttributionSource, persist);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1194,10 +1261,15 @@ public final class BluetoothAdapter {
*
* @return Bluetooth hardware address as string
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.LOCAL_MAC_ADDRESS,
+ })
public String getAddress() {
try {
- return mManagerService.getAddress();
+ return mManagerService.getAddress(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1207,19 +1279,33 @@ public final class BluetoothAdapter {
/**
* Get the friendly Bluetooth name of the local Bluetooth adapter.
* <p>This name is visible to remote Bluetooth devices.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @return the Bluetooth name, or null on error
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getName() {
try {
- return mManagerService.getName();
+ return mManagerService.getName(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
return null;
}
+ /** {@hide} */
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
+ public int getNameLengthForAdvertise() {
+ try {
+ return mService.getNameLengthForAdvertise(mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return -1;
+ }
+
/**
* Factory reset bluetooth settings.
*
@@ -1227,12 +1313,17 @@ public final class BluetoothAdapter {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean factoryReset() {
try {
mServiceLock.readLock().lock();
- if (mService != null && mService.factoryReset()
- && mManagerService != null && mManagerService.onFactoryReset()) {
+ if (mService != null && mService.factoryReset(mAttributionSource)
+ && mManagerService != null
+ && mManagerService.onFactoryReset(mAttributionSource)) {
return true;
}
Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later");
@@ -1252,7 +1343,9 @@ public final class BluetoothAdapter {
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @Nullable ParcelUuid[] getUuids() {
if (getState() != STATE_ON) {
return null;
@@ -1260,7 +1353,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getUuids();
+ return mService.getUuids(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1284,7 +1377,9 @@ public final class BluetoothAdapter {
* @param name a valid Bluetooth name
* @return true if the name was set, false otherwise
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setName(String name) {
if (getState() != STATE_ON) {
return false;
@@ -1292,7 +1387,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.setName(name);
+ return mService.setName(name, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1310,7 +1405,9 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothClass getBluetoothClass() {
if (getState() != STATE_ON) {
return null;
@@ -1318,7 +1415,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getBluetoothClass();
+ return mService.getBluetoothClass(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1339,7 +1436,11 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
if (getState() != STATE_ON) {
return false;
@@ -1347,7 +1448,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.setBluetoothClass(bluetoothClass);
+ return mService.setBluetoothClass(bluetoothClass, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1366,13 +1467,15 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@IoCapability
public int getIoCapability() {
if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
try {
mServiceLock.readLock().lock();
- if (mService != null) return mService.getIoCapability();
+ if (mService != null) return mService.getIoCapability(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
@@ -1394,12 +1497,16 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setIoCapability(@IoCapability int capability) {
if (getState() != STATE_ON) return false;
try {
mServiceLock.readLock().lock();
- if (mService != null) return mService.setIoCapability(capability);
+ if (mService != null) return mService.setIoCapability(capability, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
@@ -1417,13 +1524,15 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@IoCapability
public int getLeIoCapability() {
if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
try {
mServiceLock.readLock().lock();
- if (mService != null) return mService.getLeIoCapability();
+ if (mService != null) return mService.getLeIoCapability(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
@@ -1445,12 +1554,16 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setLeIoCapability(@IoCapability int capability) {
if (getState() != STATE_ON) return false;
try {
mServiceLock.readLock().lock();
- if (mService != null) return mService.setLeIoCapability(capability);
+ if (mService != null) return mService.setLeIoCapability(capability, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
@@ -1474,7 +1587,9 @@ public final class BluetoothAdapter {
*
* @return scan mode
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@ScanMode
public int getScanMode() {
if (getState() != STATE_ON) {
@@ -1483,7 +1598,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getScanMode();
+ return mService.getScanMode(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1521,7 +1636,9 @@ public final class BluetoothAdapter {
*/
@UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which "
+ "shows UI that confirms the user wants to go into discoverable mode.")
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean setScanMode(@ScanMode int mode, long durationMillis) {
if (getState() != STATE_ON) {
return false;
@@ -1530,7 +1647,7 @@ public final class BluetoothAdapter {
mServiceLock.readLock().lock();
if (mService != null) {
int durationSeconds = Math.toIntExact(durationMillis / 1000);
- return mService.setScanMode(mode, durationSeconds);
+ return mService.setScanMode(mode, durationSeconds, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1570,7 +1687,9 @@ public final class BluetoothAdapter {
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean setScanMode(@ScanMode int mode) {
if (getState() != STATE_ON) {
return false;
@@ -1578,7 +1697,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.setScanMode(mode, getDiscoverableTimeout());
+ return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1590,6 +1709,8 @@ public final class BluetoothAdapter {
/** @hide */
@UnsupportedAppUsage
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public int getDiscoverableTimeout() {
if (getState() != STATE_ON) {
return -1;
@@ -1597,7 +1718,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getDiscoverableTimeout();
+ return mService.getDiscoverableTimeout(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1609,6 +1730,8 @@ public final class BluetoothAdapter {
/** @hide */
@UnsupportedAppUsage
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void setDiscoverableTimeout(int timeout) {
if (getState() != STATE_ON) {
return;
@@ -1616,7 +1739,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- mService.setDiscoverableTimeout(timeout);
+ mService.setDiscoverableTimeout(timeout, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1634,12 +1757,16 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public long getDiscoveryEndMillis() {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getDiscoveryEndMillis();
+ return mService.getDiscoveryEndMillis(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1650,32 +1777,6 @@ public final class BluetoothAdapter {
}
/**
- * Set the context for this BluetoothAdapter (only called from BluetoothManager)
- * @hide
- */
- public void setContext(Context context) {
- mContext = context;
- }
-
- private String getOpPackageName() {
- // Workaround for legacy API for getting a BluetoothAdapter not
- // passing a context
- if (mContext != null) {
- return mContext.getOpPackageName();
- }
- return ActivityThread.currentOpPackageName();
- }
-
- private String getAttributionTag() {
- // Workaround for legacy API for getting a BluetoothAdapter not
- // passing a context
- if (mContext != null) {
- return mContext.getAttributionTag();
- }
- return null;
- }
-
- /**
* Start the remote device discovery process.
* <p>The discovery process usually involves an inquiry scan of about 12
* seconds, followed by a page scan of each new device to retrieve its
@@ -1704,7 +1805,10 @@ public final class BluetoothAdapter {
*
* @return true on success, false on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean startDiscovery() {
if (getState() != STATE_ON) {
return false;
@@ -1712,7 +1816,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.startDiscovery(getOpPackageName(), getAttributionTag());
+ return mService.startDiscovery(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1738,7 +1842,9 @@ public final class BluetoothAdapter {
*
* @return true on success, false on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean cancelDiscovery() {
if (getState() != STATE_ON) {
return false;
@@ -1746,7 +1852,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.cancelDiscovery();
+ return mService.cancelDiscovery(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1774,7 +1880,9 @@ public final class BluetoothAdapter {
*
* @return true if discovering
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean isDiscovering() {
if (getState() != STATE_ON) {
return false;
@@ -1782,7 +1890,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.isDiscovering();
+ return mService.isDiscovering(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1806,7 +1914,12 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean removeActiveDevice(@ActiveDeviceUse int profiles) {
if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
&& profiles != ACTIVE_DEVICE_ALL) {
@@ -1820,7 +1933,7 @@ public final class BluetoothAdapter {
mServiceLock.readLock().lock();
if (mService != null) {
if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles);
- return mService.removeActiveDevice(profiles);
+ return mService.removeActiveDevice(profiles, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1846,7 +1959,12 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean setActiveDevice(@NonNull BluetoothDevice device,
@ActiveDeviceUse int profiles) {
if (device == null) {
@@ -1867,7 +1985,7 @@ public final class BluetoothAdapter {
if (DBG) {
Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles);
}
- return mService.setActiveDevice(device, profiles);
+ return mService.setActiveDevice(device, profiles, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1890,12 +2008,17 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.connectAllEnabledProfiles(device);
+ return mService.connectAllEnabledProfiles(device, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1918,12 +2041,16 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.disconnectAllEnabledProfiles(device);
+ return mService.disconnectAllEnabledProfiles(device, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1939,6 +2066,8 @@ public final class BluetoothAdapter {
*
* @return true if Multiple Advertisement feature is supported
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isMultipleAdvertisementSupported() {
if (getState() != STATE_ON) {
return false;
@@ -1967,6 +2096,7 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
+ @RequiresNoPermission
public boolean isBleScanAlwaysAvailable() {
try {
return mManagerService.isBleScanAlwaysAvailable();
@@ -1982,6 +2112,7 @@ public final class BluetoothAdapter {
new PropertyInvalidatedCache<Void, Boolean>(
8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Boolean recompute(Void query) {
try {
mServiceLock.readLock().lock();
@@ -1999,6 +2130,7 @@ public final class BluetoothAdapter {
};
/** @hide */
+ @RequiresNoPermission
public void disableIsOffloadedFilteringSupportedCache() {
mBluetoothFilteringCache.disableLocal();
}
@@ -2013,6 +2145,8 @@ public final class BluetoothAdapter {
*
* @return true if chipset supports on-chip filtering
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isOffloadedFilteringSupported() {
if (!getLeAccess()) {
return false;
@@ -2025,6 +2159,8 @@ public final class BluetoothAdapter {
*
* @return true if chipset supports on-chip scan batching
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isOffloadedScanBatchingSupported() {
if (!getLeAccess()) {
return false;
@@ -2047,6 +2183,8 @@ public final class BluetoothAdapter {
*
* @return true if chipset supports LE 2M PHY feature
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isLe2MPhySupported() {
if (!getLeAccess()) {
return false;
@@ -2069,6 +2207,8 @@ public final class BluetoothAdapter {
*
* @return true if chipset supports LE Coded PHY feature
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isLeCodedPhySupported() {
if (!getLeAccess()) {
return false;
@@ -2091,6 +2231,8 @@ public final class BluetoothAdapter {
*
* @return true if chipset supports LE Extended Advertising feature
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isLeExtendedAdvertisingSupported() {
if (!getLeAccess()) {
return false;
@@ -2113,6 +2255,8 @@ public final class BluetoothAdapter {
*
* @return true if chipset supports LE Periodic Advertising feature
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isLePeriodicAdvertisingSupported() {
if (!getLeAccess()) {
return false;
@@ -2136,6 +2280,8 @@ public final class BluetoothAdapter {
*
* @return the maximum LE advertising data length.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public int getLeMaximumAdvertisingDataLength() {
if (!getLeAccess()) {
return 0;
@@ -2158,6 +2304,7 @@ public final class BluetoothAdapter {
*
* @return true if phone supports Hearing Aid Profile
*/
+ @RequiresNoPermission
private boolean isHearingAidProfileSupported() {
try {
return mManagerService.isHearingAidProfileSupported();
@@ -2173,12 +2320,14 @@ public final class BluetoothAdapter {
* @return the maximum number of connected audio devices
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getMaxConnectedAudioDevices() {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getMaxConnectedAudioDevices();
+ return mService.getMaxConnectedAudioDevices(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e);
@@ -2194,6 +2343,8 @@ public final class BluetoothAdapter {
* @return true if there are hw entries available for matching beacons
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isHardwareTrackingFiltersAvailable() {
if (!getLeAccess()) {
return false;
@@ -2204,7 +2355,7 @@ public final class BluetoothAdapter {
// BLE is not supported
return false;
}
- return (iGatt.numHwTrackFiltersAvailable() != 0);
+ return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -2224,6 +2375,11 @@ public final class BluetoothAdapter {
* instead.
*/
@Deprecated
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
SynchronousResultReceiver receiver = new SynchronousResultReceiver();
requestControllerActivityEnergyInfo(receiver);
@@ -2249,11 +2405,16 @@ public final class BluetoothAdapter {
* @param result The callback to which to send the activity info.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public void requestControllerActivityEnergyInfo(ResultReceiver result) {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- mService.requestActivityInfo(result);
+ mService.requestActivityInfo(result, mAttributionSource);
result = null;
}
} catch (RemoteException e) {
@@ -2276,7 +2437,9 @@ public final class BluetoothAdapter {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() {
if (getState() != STATE_ON) {
return new ArrayList<>();
@@ -2284,7 +2447,9 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getMostRecentlyConnectedDevices();
+ return Attributable.setAttributionSource(
+ mService.getMostRecentlyConnectedDevices(mAttributionSource),
+ mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -2304,17 +2469,21 @@ public final class BluetoothAdapter {
*
* @return unmodifiable set of {@link BluetoothDevice}, or null on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Set<BluetoothDevice> getBondedDevices() {
if (getState() != STATE_ON) {
- return toDeviceSet(new BluetoothDevice[0]);
+ return toDeviceSet(Arrays.asList());
}
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return toDeviceSet(mService.getBondedDevices());
+ return toDeviceSet(Attributable.setAttributionSource(
+ Arrays.asList(mService.getBondedDevices(mAttributionSource)),
+ mAttributionSource));
}
- return toDeviceSet(new BluetoothDevice[0]);
+ return toDeviceSet(Arrays.asList());
} catch (RemoteException e) {
Log.e(TAG, "", e);
} finally {
@@ -2333,6 +2502,7 @@ public final class BluetoothAdapter {
* BluetoothProfile}.
* @hide
*/
+ @RequiresNoPermission
public @NonNull List<Integer> getSupportedProfiles() {
final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
@@ -2359,6 +2529,38 @@ public final class BluetoothAdapter {
return supportedProfiles;
}
+ private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY =
+ "cache_key.bluetooth.get_adapter_connection_state";
+ private final PropertyInvalidatedCache<Void, Integer>
+ mBluetoothGetAdapterConnectionStateCache =
+ new PropertyInvalidatedCache<Void, Integer> (
+ 8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) {
+ /**
+ * This method must not be called when mService is null.
+ */
+ @Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ protected Integer recompute(Void query) {
+ try {
+ return mService.getAdapterConnectionState();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ };
+
+ /** @hide */
+ @RequiresNoPermission
+ public void disableGetAdapterConnectionStateCache() {
+ mBluetoothGetAdapterConnectionStateCache.disableLocal();
+ }
+
+ /** @hide */
+ public static void invalidateGetAdapterConnectionStateCache() {
+ PropertyInvalidatedCache.invalidateCache(
+ BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY);
+ }
+
/**
* Get the current connection state of the local Bluetooth adapter.
* This can be used to check whether the local Bluetooth adapter is connected
@@ -2372,6 +2574,8 @@ public final class BluetoothAdapter {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public int getConnectionState() {
if (getState() != STATE_ON) {
return BluetoothAdapter.STATE_DISCONNECTED;
@@ -2379,10 +2583,14 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getAdapterConnectionState();
+ return mBluetoothGetAdapterConnectionStateCache.query(null);
+ }
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof RemoteException) {
+ Log.e(TAG, "getConnectionState:", e.getCause());
+ } else {
+ throw e;
}
- } catch (RemoteException e) {
- Log.e(TAG, "getConnectionState:", e);
} finally {
mServiceLock.readLock().unlock();
}
@@ -2396,6 +2604,7 @@ public final class BluetoothAdapter {
new PropertyInvalidatedCache<Integer, Integer>(
8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Integer recompute(Integer query) {
try {
mServiceLock.readLock().lock();
@@ -2417,6 +2626,7 @@ public final class BluetoothAdapter {
};
/** @hide */
+ @RequiresNoPermission
public void disableGetProfileConnectionStateCache() {
mGetProfileConnectionStateCache.disableLocal();
}
@@ -2438,7 +2648,10 @@ public final class BluetoothAdapter {
* {@link BluetoothProfile#STATE_CONNECTED},
* {@link BluetoothProfile#STATE_DISCONNECTING}
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public int getProfileConnectionState(int profile) {
if (getState() != STATE_ON) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -2453,7 +2666,6 @@ public final class BluetoothAdapter {
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
* connections from a listening {@link BluetoothServerSocket}.
* <p>Valid RFCOMM channels are in range 1 to 30.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param channel RFCOMM channel to listen on
* @return a listening RFCOMM BluetoothServerSocket
@@ -2461,6 +2673,9 @@ public final class BluetoothAdapter {
* permissions, or channel in use.
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
return listenUsingRfcommOn(channel, false, false);
}
@@ -2472,7 +2687,6 @@ public final class BluetoothAdapter {
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
* connections from a listening {@link BluetoothServerSocket}.
* <p>Valid RFCOMM channels are in range 1 to 30.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
* <p>To auto assign a channel without creating a SDP record use
* {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
*
@@ -2486,6 +2700,9 @@ public final class BluetoothAdapter {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
boolean min16DigitPin) throws IOException {
BluetoothServerSocket socket =
@@ -2526,7 +2743,9 @@ public final class BluetoothAdapter {
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or channel in use.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
throws IOException {
return createNewRfcommSocketAndRecord(name, uuid, true, true);
@@ -2558,7 +2777,9 @@ public final class BluetoothAdapter {
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or channel in use.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
throws IOException {
return createNewRfcommSocketAndRecord(name, uuid, false, false);
@@ -2589,7 +2810,6 @@ public final class BluetoothAdapter {
* closed, or if this application closes unexpectedly.
* <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
* connect to this socket from another device using the same {@link UUID}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param name service name for SDP record
* @param uuid uuid for SDP record
@@ -2599,12 +2819,16 @@ public final class BluetoothAdapter {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)
throws IOException {
return createNewRfcommSocketAndRecord(name, uuid, false, true);
}
-
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
boolean auth, boolean encrypt) throws IOException {
BluetoothServerSocket socket;
@@ -2630,6 +2854,8 @@ public final class BluetoothAdapter {
* permissions.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
BluetoothServerSocket socket =
new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port);
@@ -2661,6 +2887,8 @@ public final class BluetoothAdapter {
* permissions.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
throws IOException {
BluetoothServerSocket socket =
@@ -2693,11 +2921,12 @@ public final class BluetoothAdapter {
* permissions.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
return listenUsingL2capOn(port, false, false);
}
-
/**
* Construct an insecure L2CAP server socket.
* Call #accept to retrieve connections to this socket.
@@ -2710,6 +2939,8 @@ public final class BluetoothAdapter {
* permissions.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
BluetoothServerSocket socket =
@@ -2736,11 +2967,14 @@ public final class BluetoothAdapter {
/**
* Read the local Out of Band Pairing Data
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @return Pair<byte[], byte[]> of Hash and Randomizer
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public Pair<byte[], byte[]> readOutOfBandData() {
return null;
}
@@ -2761,6 +2995,10 @@ public final class BluetoothAdapter {
* BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}.
* @return true on success, false on error
*/
+ @SuppressLint({
+ "AndroidFrameworkRequiresPermission",
+ "AndroidFrameworkBluetoothPermission"
+ })
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
int profile) {
if (context == null || listener == null) {
@@ -2768,53 +3006,57 @@ public final class BluetoothAdapter {
}
if (profile == BluetoothProfile.HEADSET) {
- BluetoothHeadset headset = new BluetoothHeadset(context, listener);
+ BluetoothHeadset headset = new BluetoothHeadset(context, listener, this);
return true;
} else if (profile == BluetoothProfile.A2DP) {
- BluetoothA2dp a2dp = new BluetoothA2dp(context, listener);
+ BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this);
return true;
} else if (profile == BluetoothProfile.A2DP_SINK) {
- BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener);
+ BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this);
return true;
} else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
- BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener);
+ BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this);
return true;
} else if (profile == BluetoothProfile.HID_HOST) {
- BluetoothHidHost iDev = new BluetoothHidHost(context, listener);
+ BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this);
return true;
} else if (profile == BluetoothProfile.PAN) {
- BluetoothPan pan = new BluetoothPan(context, listener);
+ BluetoothPan pan = new BluetoothPan(context, listener, this);
return true;
} else if (profile == BluetoothProfile.PBAP) {
- BluetoothPbap pbap = new BluetoothPbap(context, listener);
+ BluetoothPbap pbap = new BluetoothPbap(context, listener, this);
return true;
} else if (profile == BluetoothProfile.HEALTH) {
Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
return false;
} else if (profile == BluetoothProfile.MAP) {
- BluetoothMap map = new BluetoothMap(context, listener);
+ BluetoothMap map = new BluetoothMap(context, listener, this);
return true;
} else if (profile == BluetoothProfile.HEADSET_CLIENT) {
- BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener);
+ BluetoothHeadsetClient headsetClient =
+ new BluetoothHeadsetClient(context, listener, this);
return true;
} else if (profile == BluetoothProfile.SAP) {
- BluetoothSap sap = new BluetoothSap(context, listener);
+ BluetoothSap sap = new BluetoothSap(context, listener, this);
return true;
} else if (profile == BluetoothProfile.PBAP_CLIENT) {
- BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener);
+ BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this);
return true;
} else if (profile == BluetoothProfile.MAP_CLIENT) {
- BluetoothMapClient mapClient = new BluetoothMapClient(context, listener);
+ BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this);
return true;
} else if (profile == BluetoothProfile.HID_DEVICE) {
- BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener);
+ BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this);
return true;
} else if (profile == BluetoothProfile.HEARING_AID) {
if (isHearingAidProfileSupported()) {
- BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener);
+ BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this);
return true;
}
return false;
+ } else if (profile == BluetoothProfile.LE_AUDIO) {
+ BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
+ return true;
} else if (profile == BluetoothProfile.VOLUME_CONTROL) {
BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
return true;
@@ -2833,6 +3075,10 @@ public final class BluetoothAdapter {
* @param profile
* @param proxy Profile proxy object
*/
+ @SuppressLint({
+ "AndroidFrameworkRequiresPermission",
+ "AndroidFrameworkBluetoothPermission"
+ })
public void closeProfileProxy(int profile, BluetoothProfile proxy) {
if (proxy == null) {
return;
@@ -2903,6 +3149,10 @@ public final class BluetoothAdapter {
BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
hearingAid.close();
break;
+ case BluetoothProfile.LE_AUDIO:
+ BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy;
+ leAudio.close();
+ break;
case BluetoothProfile.VOLUME_CONTROL:
BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
vcs.close();
@@ -2910,8 +3160,10 @@ public final class BluetoothAdapter {
}
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothManagerCallback mManagerCallback =
new IBluetoothManagerCallback.Stub() {
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onBluetoothServiceUp(IBluetooth bluetoothService) {
if (DBG) {
Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
@@ -2934,11 +3186,11 @@ public final class BluetoothAdapter {
}
}
}
- synchronized (sMetadataListeners) {
- sMetadataListeners.forEach((device, pair) -> {
+ synchronized (mMetadataListeners) {
+ mMetadataListeners.forEach((device, pair) -> {
try {
- mService.registerMetadataListener(sBluetoothMetadataListener,
- device);
+ mService.registerMetadataListener(mBluetoothMetadataListener,
+ device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register metadata listener", e);
}
@@ -2947,7 +3199,8 @@ public final class BluetoothAdapter {
synchronized (mBluetoothConnectionCallbackExecutorMap) {
if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
try {
- mService.registerBluetoothConnectionCallback(mConnectionCallback);
+ mService.registerBluetoothConnectionCallback(mConnectionCallback,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
+ "connection callback", e);
@@ -2967,11 +3220,11 @@ public final class BluetoothAdapter {
if (mLeScanClients != null) {
mLeScanClients.clear();
}
- if (sBluetoothLeAdvertiser != null) {
- sBluetoothLeAdvertiser.cleanup();
+ if (mBluetoothLeAdvertiser != null) {
+ mBluetoothLeAdvertiser.cleanup();
}
- if (sBluetoothLeScanner != null) {
- sBluetoothLeScanner.cleanup();
+ if (mBluetoothLeScanner != null) {
+ mBluetoothLeScanner.cleanup();
}
} finally {
mServiceLock.writeLock().unlock();
@@ -3006,7 +3259,9 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enableNoAutoConnect() {
if (isEnabled()) {
if (DBG) {
@@ -3015,7 +3270,7 @@ public final class BluetoothAdapter {
return true;
}
try {
- return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName());
+ return mManagerService.enableNoAutoConnect(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -3024,41 +3279,16 @@ public final class BluetoothAdapter {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "OOB_ERROR_" }, value = {
- OOB_ERROR_UNKNOWN,
- OOB_ERROR_ANOTHER_ACTIVE_REQUEST,
- OOB_ERROR_ADAPTER_DISABLED
+ @IntDef(value = {
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
+ BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST,
})
public @interface OobError {}
/**
- * An unknown error has occurred in the controller, stack, or callback pipeline.
- *
- * @hide
- */
- @SystemApi
- public static final int OOB_ERROR_UNKNOWN = 0;
-
- /**
- * If another application has already requested {@link OobData} then another fetch will be
- * disallowed until the callback is removed.
- *
- * @hide
- */
- @SystemApi
- public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1;
-
- /**
- * The adapter is currently disabled, please enable it.
- *
- * @hide
- */
- @SystemApi
- public static final int OOB_ERROR_ADAPTER_DISABLED = 2;
-
- /**
* Provides callback methods for receiving {@link OobData} from the host stack, as well as an
- * error interface in order to allow the caller to determine next steps based on the {@link
+ * error interface in order to allow the caller to determine next steps based on the {@code
* ErrorCode}.
*
* @hide
@@ -3076,7 +3306,7 @@ public final class BluetoothAdapter {
/**
* Provides feedback when things don't go as expected.
*
- * @param errorCode - the code descibing the type of error that occurred.
+ * @param errorCode - the code describing the type of error that occurred.
*/
void onError(@OobError int errorCode);
}
@@ -3159,7 +3389,11 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public void generateLocalOobData(@Transport int transport,
@NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) {
if (transport != BluetoothDevice.TRANSPORT_BREDR && transport
@@ -3169,11 +3403,11 @@ public final class BluetoothAdapter {
requireNonNull(callback);
if (!isEnabled()) {
Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!");
- callback.onError(OOB_ERROR_ADAPTER_DISABLED);
+ callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED);
} else {
try {
mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback,
- executor));
+ executor), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -3203,12 +3437,14 @@ public final class BluetoothAdapter {
* reason. If Bluetooth is already on and if this function is called to turn
* it on, the api will return true and a callback will be called.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
- *
* @param on True for on, false for off.
* @param callback The callback to notify changes to the state.
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean changeApplicationBluetoothState(boolean on,
BluetoothStateChangeCallback callback) {
return false;
@@ -3240,8 +3476,8 @@ public final class BluetoothAdapter {
}
}
- private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) {
- Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(Arrays.asList(devices));
+ private Set<BluetoothDevice> toDeviceSet(List<BluetoothDevice> devices) {
+ Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(devices);
return Collections.unmodifiableSet(deviceSet);
}
@@ -3306,11 +3542,19 @@ public final class BluetoothAdapter {
&& (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11;
}
+ /** {@hide} */
@UnsupportedAppUsage
- /*package*/ IBluetoothManager getBluetoothManager() {
+ @RequiresNoPermission
+ public IBluetoothManager getBluetoothManager() {
return mManagerService;
}
+ /** {@hide} */
+ @RequiresNoPermission
+ public AttributionSource getAttributionSource() {
+ return mAttributionSource;
+ }
+
private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks =
new ArrayList<IBluetoothManagerCallback>();
@@ -3352,6 +3596,61 @@ public final class BluetoothAdapter {
}
/**
+ * Register a callback to receive events whenever the bluetooth stack goes down and back up,
+ * e.g. in the event the bluetooth is turned off/on via settings.
+ *
+ * If the bluetooth stack is currently up, there will not be an initial callback call.
+ * You can use the return value as an indication of this being the case.
+ *
+ * Callbacks will be delivered on a binder thread.
+ *
+ * @return whether bluetooth is already up currently
+ *
+ * @hide
+ */
+ public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+ return getBluetoothService(callback.mRemote) != null;
+ }
+
+ /**
+ * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
+ *
+ * @hide
+ */
+ public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+ removeServiceStateCallback(callback.mRemote);
+ }
+
+ /**
+ * A callback for {@link #registerServiceLifecycleCallback}
+ *
+ * @hide
+ */
+ public abstract static class ServiceLifecycleCallback {
+
+ /** Called when the bluetooth stack is up */
+ public abstract void onBluetoothServiceUp();
+
+ /** Called when the bluetooth stack is down */
+ public abstract void onBluetoothServiceDown();
+
+ IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
+ @Override
+ public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+ ServiceLifecycleCallback.this.onBluetoothServiceUp();
+ }
+
+ @Override
+ public void onBluetoothServiceDown() {
+ ServiceLifecycleCallback.this.onBluetoothServiceDown();
+ }
+
+ @Override
+ public void onBrEdrDown() {}
+ };
+ }
+
+ /**
* Starts a scan for Bluetooth LE devices.
*
* <p>Results of the scan are reported using the
@@ -3363,7 +3662,10 @@ public final class BluetoothAdapter {
* instead.
*/
@Deprecated
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean startLeScan(LeScanCallback callback) {
return startLeScan(null, callback);
}
@@ -3382,7 +3684,10 @@ public final class BluetoothAdapter {
* instead.
*/
@Deprecated
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
if (DBG) {
Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
@@ -3416,6 +3721,7 @@ public final class BluetoothAdapter {
return false;
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
@@ -3479,7 +3785,9 @@ public final class BluetoothAdapter {
* @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead.
*/
@Deprecated
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void stopLeScan(LeScanCallback callback) {
if (DBG) {
Log.d(TAG, "stopLeScan()");
@@ -3520,7 +3828,9 @@ public final class BluetoothAdapter {
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or unable to start this CoC
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull BluetoothServerSocket listenUsingL2capChannel()
throws IOException {
BluetoothServerSocket socket =
@@ -3566,7 +3876,9 @@ public final class BluetoothAdapter {
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or unable to start this CoC
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel()
throws IOException {
BluetoothServerSocket socket =
@@ -3611,7 +3923,11 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device,
@NonNull Executor executor, @NonNull OnMetadataChangedListener listener) {
if (DBG) Log.d(TAG, "addOnMetadataChangedListener()");
@@ -3631,13 +3947,13 @@ public final class BluetoothAdapter {
throw new NullPointerException("executor is null");
}
- synchronized (sMetadataListeners) {
+ synchronized (mMetadataListeners) {
List<Pair<OnMetadataChangedListener, Executor>> listenerList =
- sMetadataListeners.get(device);
+ mMetadataListeners.get(device);
if (listenerList == null) {
// Create new listener/executor list for registeration
listenerList = new ArrayList<>();
- sMetadataListeners.put(device, listenerList);
+ mMetadataListeners.put(device, listenerList);
} else {
// Check whether this device was already registed by the lisenter
if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) {
@@ -3651,7 +3967,8 @@ public final class BluetoothAdapter {
boolean ret = false;
try {
- ret = service.registerMetadataListener(sBluetoothMetadataListener, device);
+ ret = service.registerMetadataListener(mBluetoothMetadataListener, device,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "registerMetadataListener fail", e);
} finally {
@@ -3660,7 +3977,7 @@ public final class BluetoothAdapter {
listenerList.remove(listenerPair);
if (listenerList.isEmpty()) {
// Remove the device if its listener list is empty
- sMetadataListeners.remove(device);
+ mMetadataListeners.remove(device);
}
}
}
@@ -3684,7 +4001,11 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device,
@NonNull OnMetadataChangedListener listener) {
if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()");
@@ -3695,24 +4016,24 @@ public final class BluetoothAdapter {
throw new NullPointerException("listener is null");
}
- synchronized (sMetadataListeners) {
- if (!sMetadataListeners.containsKey(device)) {
+ synchronized (mMetadataListeners) {
+ if (!mMetadataListeners.containsKey(device)) {
throw new IllegalArgumentException("device was not registered");
}
// Remove issued listener from the registered device
- sMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener)));
+ mMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener)));
- if (sMetadataListeners.get(device).isEmpty()) {
+ if (mMetadataListeners.get(device).isEmpty()) {
// Unregister to Bluetooth service if all listeners are removed from
// the registered device
- sMetadataListeners.remove(device);
+ mMetadataListeners.remove(device);
final IBluetooth service = mService;
if (service == null) {
// Bluetooth is OFF, do nothing to Bluetooth service.
return true;
}
try {
- return service.unregisterMetadataListener(device);
+ return service.unregisterMetadataListener(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "unregisterMetadataListener fail", e);
return false;
@@ -3740,10 +4061,12 @@ public final class BluetoothAdapter {
@Nullable byte[] value);
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothConnectionCallback mConnectionCallback =
new IBluetoothConnectionCallback.Stub() {
@Override
public void onDeviceConnected(BluetoothDevice device) {
+ Attributable.setAttributionSource(device, mAttributionSource);
for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
mBluetoothConnectionCallbackExecutorMap.entrySet()) {
BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
@@ -3754,6 +4077,7 @@ public final class BluetoothAdapter {
@Override
public void onDeviceDisconnected(BluetoothDevice device, int hciReason) {
+ Attributable.setAttributionSource(device, mAttributionSource);
for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
mBluetoothConnectionCallbackExecutorMap.entrySet()) {
BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
@@ -3773,6 +4097,11 @@ public final class BluetoothAdapter {
* @throws IllegalArgumentException if the callback is already registered
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull BluetoothConnectionCallback callback) {
if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()");
@@ -3786,7 +4115,8 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
+ if (!mService.registerBluetoothConnectionCallback(mConnectionCallback,
+ mAttributionSource)) {
return false;
}
}
@@ -3815,6 +4145,11 @@ public final class BluetoothAdapter {
* @return true if the callback was unregistered successfully, false otherwise
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean unregisterBluetoothConnectionCallback(
@NonNull BluetoothConnectionCallback callback) {
if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()");
@@ -3836,7 +4171,8 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.unregisterBluetoothConnectionCallback(mConnectionCallback);
+ return mService.unregisterBluetoothConnectionCallback(mConnectionCallback,
+ mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -3872,141 +4208,45 @@ public final class BluetoothAdapter {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "REASON_" }, value = {
- REASON_UNKNOWN,
- REASON_LOCAL_REQUEST,
- REASON_REMOTE_REQUEST,
- REASON_LOCAL_ERROR,
- REASON_REMOTE_ERROR,
- REASON_TIMEOUT,
- REASON_SECURITY,
- REASON_SYSTEM_POLICY,
- REASON_RESOURCE_LIMIT_REACHED,
- REASON_CONNECTION_EXISTS,
- REASON_BAD_PARAMETERS})
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS,
+ BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS})
public @interface DisconnectReason {}
/**
- * Indicates that the ACL disconnected due to an unknown reason.
- */
- public static final int REASON_UNKNOWN = 0;
-
- /**
- * Indicates that the ACL disconnected due to an explicit request from the local device.
- * <p>
- * Example cause: This is a normal disconnect reason, e.g., user/app initiates
- * disconnection.
- */
- public static final int REASON_LOCAL_REQUEST = 1;
-
- /**
- * Indicates that the ACL disconnected due to an explicit request from the remote device.
- * <p>
- * Example cause: This is a normal disconnect reason, e.g., user/app initiates
- * disconnection.
- * <p>
- * Example solution: The app can also prompt the user to check their remote device.
- */
- public static final int REASON_REMOTE_REQUEST = 2;
-
- /**
- * Generic disconnect reason indicating the ACL disconnected due to an error on the local
- * device.
- * <p>
- * Example solution: Prompt the user to check their local device (e.g., phone, car
- * headunit).
- */
- public static final int REASON_LOCAL_ERROR = 3;
-
- /**
- * Generic disconnect reason indicating the ACL disconnected due to an error on the remote
- * device.
- * <p>
- * Example solution: Prompt the user to check their remote device (e.g., headset, car
- * headunit, watch).
- */
- public static final int REASON_REMOTE_ERROR = 4;
-
- /**
- * Indicates that the ACL disconnected due to a timeout.
- * <p>
- * Example cause: remote device might be out of range.
- * <p>
- * Example solution: Prompt user to verify their remote device is on or in
- * connection/pairing mode.
- */
- public static final int REASON_TIMEOUT = 5;
-
- /**
- * Indicates that the ACL disconnected due to link key issues.
- * <p>
- * Example cause: Devices are either unpaired or remote device is refusing our pairing
- * request.
- * <p>
- * Example solution: Prompt user to unpair and pair again.
- */
- public static final int REASON_SECURITY = 6;
-
- /**
- * Indicates that the ACL disconnected due to the local device's system policy.
- * <p>
- * Example cause: privacy policy, power management policy, permissions, etc.
- * <p>
- * Example solution: Prompt the user to check settings, or check with their system
- * administrator (e.g. some corp-managed devices do not allow OPP connection).
- */
- public static final int REASON_SYSTEM_POLICY = 7;
-
- /**
- * Indicates that the ACL disconnected due to resource constraints, either on the local
- * device or the remote device.
- * <p>
- * Example cause: controller is busy, memory limit reached, maximum number of connections
- * reached.
- * <p>
- * Example solution: The app should wait and try again. If still failing, prompt the user
- * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device.
- */
- public static final int REASON_RESOURCE_LIMIT_REACHED = 8;
-
- /**
- * Indicates that the ACL disconnected because another ACL connection already exists.
- */
- public static final int REASON_CONNECTION_EXISTS = 9;
-
- /**
- * Indicates that the ACL disconnected due to incorrect parameters passed in from the app.
- * <p>
- * Example solution: Change parameters and try again. If error persists, the app can report
- * telemetry and/or log the error in a bugreport.
- */
- public static final int REASON_BAD_PARAMETERS = 10;
-
- /**
* Returns human-readable strings corresponding to {@link DisconnectReason}.
*/
public static String disconnectReasonText(@DisconnectReason int reason) {
switch (reason) {
- case REASON_UNKNOWN:
+ case BluetoothStatusCodes.ERROR_UNKNOWN:
return "Reason unknown";
- case REASON_LOCAL_REQUEST:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST:
return "Local request";
- case REASON_REMOTE_REQUEST:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST:
return "Remote request";
- case REASON_LOCAL_ERROR:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL:
return "Local error";
- case REASON_REMOTE_ERROR:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE:
return "Remote error";
- case REASON_TIMEOUT:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT:
return "Timeout";
- case REASON_SECURITY:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY:
return "Security";
- case REASON_SYSTEM_POLICY:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY:
return "System policy";
- case REASON_RESOURCE_LIMIT_REACHED:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED:
return "Resource constrained";
- case REASON_CONNECTION_EXISTS:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS:
return "Connection already exists";
- case REASON_BAD_PARAMETERS:
+ case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS:
return "Bad parameters";
default:
return "Unrecognized disconnect reason: " + reason;
diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.java b/framework/java/android/bluetooth/BluetoothAudioConfig.java
index 9591a70b05..4c8b8c11fb 100644
--- a/framework/java/android/bluetooth/BluetoothAudioConfig.java
+++ b/framework/java/android/bluetooth/BluetoothAudioConfig.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,7 +40,7 @@ public final class BluetoothAudioConfig implements Parcelable {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothAudioConfig) {
BluetoothAudioConfig bac = (BluetoothAudioConfig) o;
return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig
diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java
index 4e7e4415c5..d27c276480 100644
--- a/framework/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java
@@ -16,6 +16,14 @@
package android.bluetooth;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -54,10 +62,11 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
* <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}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
@@ -70,13 +79,17 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
* most recent player setting. </li>
* </ul>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PLAYER_SETTING =
"android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
public static final String EXTRA_PLAYER_SETTING =
"android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER,
"BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
@@ -91,8 +104,10 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
* Create a BluetoothAvrcpController proxy object for interacting with the local
* Bluetooth AVRCP service.
*/
- /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ /* package */ BluetoothAvrcpController(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -113,13 +128,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothAvrcpController service =
getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -133,13 +151,17 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothAvrcpController service =
getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -153,13 +175,15 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothAvrcpController service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -174,6 +198,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
*
* @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getPlayerSettings");
BluetoothAvrcpPlayerSettings settings = null;
@@ -181,7 +207,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
getService();
if (service != null && isEnabled()) {
try {
- settings = service.getPlayerSettings(device);
+ settings = service.getPlayerSettings(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
return null;
@@ -194,13 +220,15 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
* Sets the player app setting for current player.
* returns true in case setting is supported by remote, false otherwise
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
final IBluetoothAvrcpController service =
getService();
if (service != null && isEnabled()) {
try {
- return service.setPlayerApplicationSetting(plAppSetting);
+ return service.setPlayerApplicationSetting(plAppSetting, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
return false;
@@ -214,6 +242,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
* Send Group Navigation Command to Remote.
* possible keycode values: next_grp, previous_grp defined above
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
+ keyState);
@@ -221,7 +251,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
getService();
if (service != null && isEnabled()) {
try {
- service.sendGroupNavigationCmd(device, keyCode, keyState);
+ service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource);
return;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java
index 1e92fc0568..7fe18a0704 100755
--- a/framework/java/android/bluetooth/BluetoothClass.java
+++ b/framework/java/android/bluetooth/BluetoothClass.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -72,7 +73,7 @@ public final class BluetoothClass implements Parcelable {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothClass) {
return mClass == ((BluetoothClass) o).mClass;
}
diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java
index a52fc89179..1d0bf97c34 100644
--- a/framework/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java
@@ -18,6 +18,7 @@ package android.bluetooth;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -208,7 +209,7 @@ public final class BluetoothCodecConfig implements Parcelable {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothCodecConfig) {
BluetoothCodecConfig other = (BluetoothCodecConfig) o;
return (other.mCodecType == mCodecType
diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java
index f43a9e8cab..7764ebeb2e 100644
--- a/framework/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java
@@ -18,7 +18,6 @@ package android.bluetooth;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -40,7 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable {
* This extra represents the current codec status of the A2DP
* profile.
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage
public static final String EXTRA_CODEC_STATUS =
"android.bluetooth.extra.CODEC_STATUS";
@@ -57,7 +56,7 @@ public final class BluetoothCodecStatus implements Parcelable {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothCodecStatus) {
BluetoothCodecStatus other = (BluetoothCodecStatus) o;
return (Objects.equals(other.mCodecConfig, mCodecConfig)
@@ -199,7 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable {
*
* @return the current codec configuration
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig getCodecConfig() {
return mCodecConfig;
}
@@ -209,7 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable {
*
* @return an array with the codecs local capabilities
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() {
return mCodecsLocalCapabilities;
}
@@ -219,7 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable {
*
* @return an array with the codecs selectable capabilities
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() {
return mCodecsSelectableCapabilities;
}
diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java
index 11b45e32c4..21ec91865d 100644
--- a/framework/java/android/bluetooth/BluetoothDevice.java
+++ b/framework/java/android/bluetooth/BluetoothDevice.java
@@ -16,7 +16,6 @@
package android.bluetooth;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,7 +25,15 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PropertyInvalidatedCache;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
+import android.companion.AssociationRequest;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
@@ -65,9 +72,6 @@ import java.util.UUID;
* {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using
* {@link #createL2capChannel(int)} over Bluetooth LE.
*
- * <p class="note"><strong>Note:</strong>
- * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>
@@ -80,7 +84,7 @@ import java.util.UUID;
* {@see BluetoothAdapter}
* {@see BluetoothSocket}
*/
-public final class BluetoothDevice implements Parcelable {
+public final class BluetoothDevice implements Parcelable, Attributable {
private static final String TAG = "BluetoothDevice";
private static final boolean DBG = false;
@@ -107,10 +111,12 @@ public final class BluetoothDevice implements Parcelable {
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or
* {@link #EXTRA_RSSI} if they are available.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} and
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive.
*/
// TODO: Change API to not broadcast RSSI if not available (incoming connection)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_FOUND =
"android.bluetooth.device.action.FOUND";
@@ -119,9 +125,11 @@ public final class BluetoothDevice implements Parcelable {
* Broadcast Action: Bluetooth class of a remote device has changed.
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_CLASS}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
* {@see BluetoothClass}
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CLASS_CHANGED =
"android.bluetooth.device.action.CLASS_CHANGED";
@@ -132,8 +140,10 @@ public final class BluetoothDevice implements Parcelable {
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
* <p>ACL connections are managed automatically by the Android Bluetooth
* stack.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACL_CONNECTED =
"android.bluetooth.device.action.ACL_CONNECTED";
@@ -145,8 +155,10 @@ public final class BluetoothDevice implements Parcelable {
* this intent as a hint to immediately terminate higher level connections
* (RFCOMM, L2CAP, or profile connections) to the remote device.
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACL_DISCONNECT_REQUESTED =
"android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
@@ -157,8 +169,10 @@ public final class BluetoothDevice implements Parcelable {
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
* <p>ACL connections are managed automatically by the Android Bluetooth
* stack.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACL_DISCONNECTED =
"android.bluetooth.device.action.ACL_DISCONNECTED";
@@ -168,8 +182,10 @@ public final class BluetoothDevice implements Parcelable {
* been retrieved for the first time, or changed since the last retrieval.
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_NAME}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_NAME_CHANGED =
"android.bluetooth.device.action.NAME_CHANGED";
@@ -178,9 +194,11 @@ public final class BluetoothDevice implements Parcelable {
* Broadcast Action: Indicates the alias of a remote device has been
* changed.
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
@SuppressLint("ActionValue")
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ALIAS_CHANGED =
"android.bluetooth.device.action.ALIAS_CHANGED";
@@ -190,10 +208,12 @@ public final class BluetoothDevice implements Parcelable {
* device. For example, if a device is bonded (paired).
* <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link
* #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
// Note: When EXTRA_BOND_STATE is BOND_NONE then this will also
// contain a hidden extra field EXTRA_REASON with the result code.
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BOND_STATE_CHANGED =
"android.bluetooth.device.action.BOND_STATE_CHANGED";
@@ -203,10 +223,12 @@ public final class BluetoothDevice implements Parcelable {
* been retrieved for the first time, or changed since the last retrieval
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_BATTERY_LEVEL}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BATTERY_LEVEL_CHANGED =
"android.bluetooth.device.action.BATTERY_LEVEL_CHANGED";
@@ -328,6 +350,26 @@ public final class BluetoothDevice implements Parcelable {
public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
/**
+ * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
+ * intents as the value of passkey.
+ * @hide
+ */
+ public static final String EXTRA_PAIRING_INITIATOR =
+ "android.bluetooth.device.extra.PAIRING_INITIATOR";
+
+ /**
+ * Bluetooth pairing initiator, Foreground App
+ * @hide
+ */
+ public static final int EXTRA_PAIRING_INITIATOR_FOREGROUND = 1;
+
+ /**
+ * Bluetooth pairing initiator, Background
+ * @hide
+ */
+ public static final int EXTRA_PAIRING_INITIATOR_BACKGROUND = 2;
+
+ /**
* Bluetooth device type, Unknown
*/
public static final int DEVICE_TYPE_UNKNOWN = 0;
@@ -349,6 +391,8 @@ public final class BluetoothDevice implements Parcelable {
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final String ACTION_SDP_RECORD =
@@ -621,13 +665,17 @@ public final class BluetoothDevice implements Parcelable {
* device are requested to be fetched using Service Discovery Protocol
* <p> Always contains the extra field {@link #EXTRA_DEVICE}
* <p> Always contains the extra field {@link #EXTRA_UUID}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to receive.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_UUID =
"android.bluetooth.device.action.UUID";
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MAS_INSTANCE =
"android.bluetooth.device.action.MAS_INSTANCE";
@@ -636,40 +684,51 @@ public final class BluetoothDevice implements Parcelable {
* Broadcast Action: Indicates a failure to retrieve the name of a remote
* device.
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*
* @hide
*/
//TODO: is this actually useful?
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_NAME_FAILED =
"android.bluetooth.device.action.NAME_FAILED";
/**
* Broadcast Action: This intent is used to broadcast PAIRING REQUEST
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to
- * receive.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PAIRING_REQUEST =
"android.bluetooth.device.action.PAIRING_REQUEST";
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage
public static final String ACTION_PAIRING_CANCEL =
"android.bluetooth.device.action.PAIRING_CANCEL";
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_ACCESS_REQUEST =
"android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST";
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_ACCESS_REPLY =
"android.bluetooth.device.action.CONNECTION_ACCESS_REPLY";
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_ACCESS_CANCEL =
"android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
@@ -680,6 +739,8 @@ public final class BluetoothDevice implements Parcelable {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@SystemApi
public static final String ACTION_SILENCE_MODE_CHANGED =
@@ -1045,6 +1106,8 @@ public final class BluetoothDevice implements Parcelable {
private final String mAddress;
@AddressType private final int mAddressType;
+ private AttributionSource mAttributionSource;
+
/*package*/
@UnsupportedAppUsage
static IBluetooth getService() {
@@ -1090,6 +1153,7 @@ public final class BluetoothDevice implements Parcelable {
* and is validated in this constructor.
*
* @param address valid Bluetooth MAC address
+ * @param attributionSource attribution for permission-protected calls
* @throws RuntimeException Bluetooth is not available on this platform
* @throws IllegalArgumentException address is invalid
* @hide
@@ -1103,10 +1167,21 @@ public final class BluetoothDevice implements Parcelable {
mAddress = address;
mAddressType = ADDRESS_TYPE_PUBLIC;
+ mAttributionSource = BluetoothManager.resolveAttributionSource(null);
+ }
+
+ /** {@hide} */
+ public void setAttributionSource(@NonNull AttributionSource attributionSource) {
+ mAttributionSource = attributionSource;
+ }
+
+ /** {@hide} */
+ public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) {
+ setAttributionSource(attributionSource);
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothDevice) {
return mAddress.equals(((BluetoothDevice) o).getAddress());
}
@@ -1165,6 +1240,18 @@ public final class BluetoothDevice implements Parcelable {
}
/**
+ * Returns the anonymized hardware address of this BluetoothDevice. The first three octets
+ * will be suppressed for anonymization.
+ * <p> For example, "XX:XX:XX:AA:BB:CC".
+ *
+ * @return Anonymized bluetooth hardware address as string
+ * @hide
+ */
+ public String getAnonymizedAddress() {
+ return "XX:XX:XX" + getAddress().substring(8);
+ }
+
+ /**
* Get the friendly Bluetooth name of the remote device.
*
* <p>The local adapter will automatically retrieve remote names when
@@ -1173,7 +1260,9 @@ public final class BluetoothDevice implements Parcelable {
*
* @return the Bluetooth name, or null if there was a problem.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getName() {
final IBluetooth service = sService;
if (service == null) {
@@ -1181,7 +1270,7 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
try {
- String name = service.getRemoteName(this);
+ String name = service.getRemoteName(this, mAttributionSource);
if (name != null) {
// remove whitespace characters from the name
return name
@@ -1202,7 +1291,9 @@ public final class BluetoothDevice implements Parcelable {
* @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link
* #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getType() {
final IBluetooth service = sService;
if (service == null) {
@@ -1210,7 +1301,7 @@ public final class BluetoothDevice implements Parcelable {
return DEVICE_TYPE_UNKNOWN;
}
try {
- return service.getRemoteType(this);
+ return service.getRemoteType(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1218,14 +1309,15 @@ public final class BluetoothDevice implements Parcelable {
}
/**
- * Get the Bluetooth alias of the remote device.
- * <p>Alias is the locally modified name of a remote device.
+ * Get the locally modifiable name (alias) of the remote Bluetooth device.
*
* @return the Bluetooth alias, the friendly device name if no alias, or
* null if there was a problem
*/
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getAlias() {
final IBluetooth service = sService;
if (service == null) {
@@ -1233,7 +1325,7 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
try {
- String alias = service.getRemoteAlias(this);
+ String alias = service.getRemoteAliasWithAttribution(this, mAttributionSource);
if (alias == null) {
return getName();
}
@@ -1244,30 +1336,53 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
- /**
- * Set the Bluetooth alias of the remote device.
- * <p>Alias is the locally modified name of a remote device.
- * <p>This methoid overwrites the alias. The changed
- * alias is saved in the local storage so that the change
- * is preserved over power cycle.
- *
- * @return true on success, false on error
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
- public boolean setAlias(@NonNull String alias) {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
+ BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
+ })
+ public @interface SetAliasReturnValues{}
+
+ /**
+ * Sets the locally modifiable name (alias) of the remote Bluetooth device. This method
+ * overwrites the previously stored alias. The new alias is saved in local
+ * storage so that the change is preserved over power cycles.
+ *
+ * <p>This method requires the calling app to be associated with Companion Device Manager (see
+ * {@link android.companion.CompanionDeviceManager#associate(AssociationRequest,
+ * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the
+ * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. Alternatively, if the
+ * caller has the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can
+ * bypass the Companion Device Manager association requirement as well as other permission
+ * requirements.
+ *
+ * @param alias is the new locally modifiable name for the remote Bluetooth device which must
+ * be the empty string. If null, we clear the alias.
+ * @return whether the alias was successfully changed
+ * @throws IllegalArgumentException if the alias is the empty string
+ */
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public @SetAliasReturnValues int setAlias(@Nullable String alias) {
+ if (alias != null && alias.isEmpty()) {
+ throw new IllegalArgumentException("alias cannot be the empty string");
+ }
final IBluetooth service = sService;
if (service == null) {
Log.e(TAG, "BT not enabled. Cannot set Remote Device name");
- return false;
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
try {
- return service.setRemoteAlias(this, alias);
+ return service.setRemoteAlias(this, alias, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
+ throw e.rethrowFromSystemServer();
}
- return false;
}
/**
@@ -1279,7 +1394,9 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getBatteryLevel() {
final IBluetooth service = sService;
if (service == null) {
@@ -1287,7 +1404,7 @@ public final class BluetoothDevice implements Parcelable {
return BATTERY_LEVEL_BLUETOOTH_OFF;
}
try {
- return service.getBatteryLevel(this);
+ return service.getBatteryLevel(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1304,7 +1421,9 @@ public final class BluetoothDevice implements Parcelable {
*
* @return false on immediate error, true if bonding will begin
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean createBond() {
return createBond(TRANSPORT_AUTO);
}
@@ -1325,7 +1444,9 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean createBond(int transport) {
return createBondInternal(transport, null, null);
}
@@ -1353,7 +1474,7 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
@Nullable OobData remoteP256Data) {
if (remoteP192Data == null && remoteP256Data == null) {
@@ -1364,6 +1485,7 @@ public final class BluetoothDevice implements Parcelable {
return createBondInternal(transport, remoteP192Data, remoteP256Data);
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
@Nullable OobData remoteP256Data) {
final IBluetooth service = sService;
@@ -1372,7 +1494,8 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.createBond(this, transport, remoteP192Data, remoteP256Data);
+ return service.createBond(
+ this, transport, remoteP192Data, remoteP256Data, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1387,7 +1510,9 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isBondingInitiatedLocally() {
final IBluetooth service = sService;
if (service == null) {
@@ -1395,7 +1520,7 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.isBondingInitiatedLocally(this);
+ return service.isBondingInitiatedLocally(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1409,7 +1534,7 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean cancelBondProcess() {
final IBluetooth service = sService;
if (service == null) {
@@ -1420,7 +1545,7 @@ public final class BluetoothDevice implements Parcelable {
Log.i(TAG, "cancelBondProcess() for device " + getAddress()
+ " called by pid: " + Process.myPid()
+ " tid: " + Process.myTid());
- return service.cancelBondProcess(this);
+ return service.cancelBondProcess(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1437,7 +1562,7 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean removeBond() {
final IBluetooth service = sService;
if (service == null) {
@@ -1448,7 +1573,7 @@ public final class BluetoothDevice implements Parcelable {
Log.i(TAG, "removeBond() for device " + getAddress()
+ " called by pid: " + Process.myPid()
+ " tid: " + Process.myTid());
- return service.removeBond(this);
+ return service.removeBond(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1461,9 +1586,10 @@ public final class BluetoothDevice implements Parcelable {
new PropertyInvalidatedCache<BluetoothDevice, Integer>(
8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Integer recompute(BluetoothDevice query) {
try {
- return sService.getBondState(query);
+ return sService.getBondState(query, mAttributionSource);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -1489,7 +1615,10 @@ public final class BluetoothDevice implements Parcelable {
*
* @return the bond state
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public int getBondState() {
final IBluetooth service = sService;
if (service == null) {
@@ -1509,13 +1638,43 @@ public final class BluetoothDevice implements Parcelable {
}
/**
+ * Checks whether this bluetooth device is associated with CDM and meets the criteria to skip
+ * the bluetooth pairing dialog because it has been already consented by the CDM prompt.
+ *
+ * @return true if we can bond without the dialog, false otherwise
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean canBondWithoutDialog() {
+ final IBluetooth service = sService;
+ if (service == null) {
+ Log.e(TAG, "BT not enabled. Cannot check if we can skip pairing dialog");
+ return false;
+ }
+ try {
+ if (DBG) Log.d(TAG, "canBondWithoutDialog, device: " + this);
+ return service.canBondWithoutDialog(this, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return false;
+ }
+
+ /**
* Returns whether there is an open connection to this device.
*
* @return True if there is at least one open connection to this device.
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isConnected() {
final IBluetooth service = sService;
if (service == null) {
@@ -1523,7 +1682,8 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED;
+ return service.getConnectionStateWithAttribution(this, mAttributionSource)
+ != CONNECTION_STATE_DISCONNECTED;
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1538,7 +1698,9 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isEncrypted() {
final IBluetooth service = sService;
if (service == null) {
@@ -1546,7 +1708,8 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.getConnectionState(this) > CONNECTION_STATE_CONNECTED;
+ return service.getConnectionStateWithAttribution(this, mAttributionSource)
+ > CONNECTION_STATE_CONNECTED;
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1558,7 +1721,9 @@ public final class BluetoothDevice implements Parcelable {
*
* @return Bluetooth class object, or null on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothClass getBluetoothClass() {
final IBluetooth service = sService;
if (service == null) {
@@ -1566,7 +1731,7 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
try {
- int classInt = service.getRemoteClass(this);
+ int classInt = service.getRemoteClass(this, mAttributionSource);
if (classInt == BluetoothClass.ERROR) return null;
return new BluetoothClass(classInt);
} catch (RemoteException e) {
@@ -1585,7 +1750,9 @@ public final class BluetoothDevice implements Parcelable {
*
* @return the supported features (UUIDs) of the remote device, or null on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public ParcelUuid[] getUuids() {
final IBluetooth service = sService;
if (service == null || !isBluetoothEnabled()) {
@@ -1593,7 +1760,7 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
try {
- return service.getRemoteUuids(this);
+ return service.getRemoteUuids(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1613,7 +1780,9 @@ public final class BluetoothDevice implements Parcelable {
* @return False if the check fails, True if the process of initiating an ACL connection
* to the remote device was started or cached UUIDs will be broadcast.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean fetchUuidsWithSdp() {
final IBluetooth service = sService;
if (service == null || !isBluetoothEnabled()) {
@@ -1621,7 +1790,7 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.fetchRemoteUuids(this);
+ return service.fetchRemoteUuidsWithAttribution(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1639,8 +1808,7 @@ public final class BluetoothDevice implements Parcelable {
* {@link #EXTRA_SDP_SEARCH_STATUS} different from 0.
* Detailed status error codes can be found by members of the Bluetooth package in
* the AbstractionLayer class.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
- * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
+ * <p>The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
* The object type will match one of the SdpXxxRecord types, depending on the UUID searched
* for.
*
@@ -1649,6 +1817,9 @@ public final class BluetoothDevice implements Parcelable {
* was started.
*/
/** @hide */
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sdpSearch(ParcelUuid uuid) {
final IBluetooth service = sService;
if (service == null) {
@@ -1656,7 +1827,7 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.sdpSearch(this, uuid);
+ return service.sdpSearch(this, uuid, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1665,10 +1836,12 @@ public final class BluetoothDevice implements Parcelable {
/**
* Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
* @return true pin has been set false for error
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPin(byte[] pin) {
final IBluetooth service = sService;
if (service == null) {
@@ -1676,7 +1849,7 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.setPin(this, true, pin.length, pin);
+ return service.setPin(this, true, pin.length, pin, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1690,7 +1863,9 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPin(@NonNull String pin) {
byte[] pinBytes = convertPinToBytes(pin);
if (pinBytes == null) {
@@ -1704,7 +1879,10 @@ public final class BluetoothDevice implements Parcelable {
*
* @return true confirmation has been sent out false for error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPairingConfirmation(boolean confirm) {
final IBluetooth service = sService;
if (service == null) {
@@ -1712,7 +1890,7 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.setPairingConfirmation(this, confirm);
+ return service.setPairingConfirmation(this, confirm, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1727,7 +1905,9 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean cancelPairing() {
final IBluetooth service = sService;
if (service == null) {
@@ -1735,7 +1915,7 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.cancelBondProcess(this);
+ return service.cancelBondProcess(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1759,14 +1939,16 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @AccessPermission int getPhonebookAccessPermission() {
final IBluetooth service = sService;
if (service == null) {
return ACCESS_UNKNOWN;
}
try {
- return service.getPhonebookAccessPermission(this);
+ return service.getPhonebookAccessPermission(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1791,22 +1973,23 @@ public final class BluetoothDevice implements Parcelable {
* If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot
* enter silence mode.
*
- * <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
- *
* @param silence true to enter silence mode, false to exit
* @return true on success, false on error.
* @throws IllegalStateException if Bluetooth is not turned ON.
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setSilenceMode(boolean silence) {
final IBluetooth service = sService;
if (service == null) {
throw new IllegalStateException("Bluetooth is not turned ON");
}
try {
- return service.setSilenceMode(this, silence);
+ return service.setSilenceMode(this, silence, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "setSilenceMode fail", e);
return false;
@@ -1816,21 +1999,22 @@ public final class BluetoothDevice implements Parcelable {
/**
* Check whether the {@link BluetoothDevice} is in silence mode
*
- * <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
- *
* @return true on device in silence mode, otherwise false.
* @throws IllegalStateException if Bluetooth is not turned ON.
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean isInSilenceMode() {
final IBluetooth service = sService;
if (service == null) {
throw new IllegalStateException("Bluetooth is not turned ON");
}
try {
- return service.getSilenceMode(this);
+ return service.getSilenceMode(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "isInSilenceMode fail", e);
return false;
@@ -1846,14 +2030,17 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPhonebookAccessPermission(@AccessPermission int value) {
final IBluetooth service = sService;
if (service == null) {
return false;
}
try {
- return service.setPhonebookAccessPermission(this, value);
+ return service.setPhonebookAccessPermission(this, value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1867,14 +2054,16 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @AccessPermission int getMessageAccessPermission() {
final IBluetooth service = sService;
if (service == null) {
return ACCESS_UNKNOWN;
}
try {
- return service.getMessageAccessPermission(this);
+ return service.getMessageAccessPermission(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1891,7 +2080,10 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setMessageAccessPermission(@AccessPermission int value) {
// Validates param value is one of the accepted constants
if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) {
@@ -1902,7 +2094,7 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.setMessageAccessPermission(this, value);
+ return service.setMessageAccessPermission(this, value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1916,14 +2108,16 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @AccessPermission int getSimAccessPermission() {
final IBluetooth service = sService;
if (service == null) {
return ACCESS_UNKNOWN;
}
try {
- return service.getSimAccessPermission(this);
+ return service.getSimAccessPermission(this, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1940,14 +2134,17 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setSimAccessPermission(int value) {
final IBluetooth service = sService;
if (service == null) {
return false;
}
try {
- return service.setSimAccessPermission(this, value);
+ return service.setSimAccessPermission(this, value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1971,7 +2168,6 @@ public final class BluetoothDevice implements Parcelable {
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
* connection.
* <p>Valid RFCOMM channels are in range 1 to 30.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param channel RFCOMM channel to connect to
* @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
@@ -1980,6 +2176,10 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createRfcommSocket(int channel) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2006,7 +2206,6 @@ public final class BluetoothDevice implements Parcelable {
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
* connection.
* <p>Valid L2CAP PSM channels are in range 1 to 2^16.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param channel L2cap PSM/channel to connect to
* @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
@@ -2014,6 +2213,10 @@ public final class BluetoothDevice implements Parcelable {
* permissions
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createL2capSocket(int channel) throws IOException {
return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel,
null);
@@ -2027,7 +2230,6 @@ public final class BluetoothDevice implements Parcelable {
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
* connection.
* <p>Valid L2CAP PSM channels are in range 1 to 2^16.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param channel L2cap PSM/channel to connect to
* @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
@@ -2035,6 +2237,10 @@ public final class BluetoothDevice implements Parcelable {
* permissions
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException {
return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel,
null);
@@ -2070,7 +2276,10 @@ public final class BluetoothDevice implements Parcelable {
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2108,7 +2317,10 @@ public final class BluetoothDevice implements Parcelable {
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2124,7 +2336,6 @@ public final class BluetoothDevice implements Parcelable {
* Call #connect on the returned #BluetoothSocket to begin the connection.
* The remote device will not be authenticated and communication on this
* socket will not be encrypted.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param port remote port
* @return An RFCOMM BluetoothSocket
@@ -2134,6 +2345,10 @@ public final class BluetoothDevice implements Parcelable {
*/
@UnsupportedAppUsage(publicAlternatives = "Use "
+ "{@link #createInsecureRfcommSocketToServiceRecord} instead.")
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2146,7 +2361,6 @@ public final class BluetoothDevice implements Parcelable {
/**
* Construct a SCO socket ready to start an outgoing connection.
* Call #connect on the returned #BluetoothSocket to begin the connection.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @return a SCO BluetoothSocket
* @throws IOException on error, for example Bluetooth not available, or insufficient
@@ -2154,6 +2368,10 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createScoSocket() throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2201,6 +2419,8 @@ public final class BluetoothDevice implements Parcelable {
* automatically connect as soon as the remote device becomes available (true).
* @throws IllegalArgumentException if callback is null
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback) {
return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO));
@@ -2221,6 +2441,8 @@ public final class BluetoothDevice implements Parcelable {
* BluetoothDevice#TRANSPORT_LE}
* @throws IllegalArgumentException if callback is null
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport) {
return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK));
@@ -2245,6 +2467,8 @@ public final class BluetoothDevice implements Parcelable {
* is set to true.
* @throws NullPointerException if callback is null
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport, int phy) {
return connectGatt(context, autoConnect, callback, transport, phy, null);
@@ -2271,6 +2495,8 @@ public final class BluetoothDevice implements Parcelable {
* an un-specified background thread.
* @throws NullPointerException if callback is null
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport, int phy,
Handler handler) {
@@ -2304,6 +2530,8 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport,
boolean opportunistic, int phy, Handler handler) {
@@ -2321,7 +2549,8 @@ public final class BluetoothDevice implements Parcelable {
// BLE is not supported
return null;
}
- BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, opportunistic, phy);
+ BluetoothGatt gatt = new BluetoothGatt(
+ iGatt, this, transport, opportunistic, phy, mAttributionSource);
gatt.connect(autoConnect, callback, handler);
return gatt;
} catch (RemoteException e) {
@@ -2348,7 +2577,10 @@ public final class BluetoothDevice implements Parcelable {
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
@@ -2376,7 +2608,10 @@ public final class BluetoothDevice implements Parcelable {
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
@@ -2404,7 +2639,10 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) {
final IBluetooth service = sService;
if (service == null) {
@@ -2416,7 +2654,7 @@ public final class BluetoothDevice implements Parcelable {
+ ", should not over " + METADATA_MAX_LENGTH);
}
try {
- return service.setMetadata(this, key, value);
+ return service.setMetadata(this, key, value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "setMetadata fail", e);
return false;
@@ -2432,7 +2670,10 @@ public final class BluetoothDevice implements Parcelable {
*/
@SystemApi
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public byte[] getMetadata(@MetadataKey int key) {
final IBluetooth service = sService;
if (service == null) {
@@ -2440,7 +2681,7 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
try {
- return service.getMetadata(this, key);
+ return service.getMetadata(this, key, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "getMetadata fail", e);
return null;
diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java
index 09b0a80313..26e46573dd 100644
--- a/framework/java/android/bluetooth/BluetoothDevicePicker.java
+++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java
@@ -16,8 +16,10 @@
package android.bluetooth;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
/**
* A helper to show a system "Device Picker" activity to the user.
@@ -39,6 +41,8 @@ public interface BluetoothDevicePicker {
* Selected {@link BluetoothDevice} is returned in extra data named
* {@link BluetoothDevice#EXTRA_DEVICE}.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_DEVICE_SELECTED =
"android.bluetooth.devicepicker.action.DEVICE_SELECTED";
@@ -54,6 +58,8 @@ public interface BluetoothDevicePicker {
* - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent
* come from
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LAUNCH =
"android.bluetooth.devicepicker.action.LAUNCH";
diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java
index 1d08ddb65c..2f771e9d3b 100644
--- a/framework/java/android/bluetooth/BluetoothGatt.java
+++ b/framework/java/android/bluetooth/BluetoothGatt.java
@@ -16,7 +16,13 @@
package android.bluetooth;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
@@ -64,6 +70,7 @@ public final class BluetoothGatt implements BluetoothProfile {
private int mTransport;
private int mPhy;
private boolean mOpportunistic;
+ private final AttributionSource mAttributionSource;
private static final int AUTH_RETRY_STATE_IDLE = 0;
private static final int AUTH_RETRY_STATE_NO_MITM = 1;
@@ -150,6 +157,7 @@ public final class BluetoothGatt implements BluetoothProfile {
/**
* Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
*/
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothGattCallback mBluetoothGattCallback =
new IBluetoothGattCallback.Stub() {
/**
@@ -157,6 +165,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onClientRegistered(int status, int clientIf) {
if (DBG) {
Log.d(TAG, "onClientRegistered() - status=" + status
@@ -191,7 +200,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.clientConnect(mClientIf, mDevice.getAddress(),
!mAutoConnect, mTransport, mOpportunistic,
- mPhy); // autoConnect is inverse of "isDirect"
+ mPhy, mAttributionSource); // autoConnect is inverse of "isDirect"
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -347,6 +356,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onCharacteristicRead(String address, int status, int handle,
byte[] value) {
if (VDBG) {
@@ -368,7 +378,8 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
- mService.readCharacteristic(mClientIf, address, handle, authReq);
+ mService.readCharacteristic(
+ mClientIf, address, handle, authReq, mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -404,6 +415,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onCharacteristicWrite(String address, int status, int handle) {
if (VDBG) {
Log.d(TAG, "onCharacteristicWrite() - Device=" + address
@@ -430,7 +442,7 @@ public final class BluetoothGatt implements BluetoothProfile {
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
mService.writeCharacteristic(mClientIf, address, handle,
characteristic.getWriteType(), authReq,
- characteristic.getValue());
+ characteristic.getValue(), mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -487,6 +499,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onDescriptorRead(String address, int status, int handle, byte[] value) {
if (VDBG) {
Log.d(TAG,
@@ -511,7 +524,8 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
- mService.readDescriptor(mClientIf, address, handle, authReq);
+ mService.readDescriptor(
+ mClientIf, address, handle, authReq, mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -538,6 +552,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onDescriptorWrite(String address, int status, int handle) {
if (VDBG) {
Log.d(TAG,
@@ -562,7 +577,7 @@ public final class BluetoothGatt implements BluetoothProfile {
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
mService.writeDescriptor(mClientIf, address, handle,
- authReq, descriptor.getValue());
+ authReq, descriptor.getValue(), mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -715,13 +730,14 @@ public final class BluetoothGatt implements BluetoothProfile {
}
};
- /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
- int transport, boolean opportunistic, int phy) {
+ /* package */ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport,
+ boolean opportunistic, int phy, AttributionSource attributionSource) {
mService = iGatt;
mDevice = device;
mTransport = transport;
mPhy = phy;
mOpportunistic = opportunistic;
+ mAttributionSource = attributionSource;
mServices = new ArrayList<BluetoothGattService>();
mConnState = CONN_STATE_IDLE;
@@ -734,6 +750,8 @@ public final class BluetoothGatt implements BluetoothProfile {
* Application should call this method as early as possible after it is done with
* this GATT client.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void close() {
if (DBG) Log.d(TAG, "close()");
@@ -817,12 +835,13 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
* is used to notify success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @return If true, the callback will be called to notify success or failure, false on immediate
* error
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
return registerApp(callback, handler, false);
}
@@ -833,14 +852,15 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
* is used to notify success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @param eatt_support indicate to allow for eatt support
* @return If true, the callback will be called to notify success or failure, false on immediate
* error
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean registerApp(BluetoothGattCallback callback, Handler handler,
boolean eatt_support) {
if (DBG) Log.d(TAG, "registerApp()");
@@ -852,7 +872,8 @@ public final class BluetoothGatt implements BluetoothProfile {
if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
try {
- mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support);
+ mService.registerClient(
+ new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -865,13 +886,15 @@ public final class BluetoothGatt implements BluetoothProfile {
* Unregister the current application and callbacks.
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void unregisterApp() {
if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
if (mService == null || mClientIf == 0) return;
try {
mCallback = null;
- mService.unregisterClient(mClientIf);
+ mService.unregisterClient(mClientIf, mAttributionSource);
mClientIf = 0;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -893,14 +916,15 @@ public final class BluetoothGatt implements BluetoothProfile {
* subsequent connections to known devices should be invoked with the
* autoConnect parameter set to true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Remote device to connect to
* @param autoConnect Whether to directly connect to the remote device (false) or to
* automatically connect as soon as the remote device becomes available (true).
* @return true, if the connection attempt was initiated successfully
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Handler handler) {
if (DBG) {
@@ -931,15 +955,16 @@ public final class BluetoothGatt implements BluetoothProfile {
/**
* Disconnects an established connection, or cancels a connection attempt
* currently in progress.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void disconnect() {
if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return;
try {
- mService.clientDisconnect(mClientIf, mDevice.getAddress());
+ mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -954,10 +979,13 @@ public final class BluetoothGatt implements BluetoothProfile {
*
* @return true, if the connection attempt was initiated successfully
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect() {
try {
+ // autoConnect is inverse of "isDirect"
mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
- mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
+ mOpportunistic, mPhy, mAttributionSource);
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -983,10 +1011,12 @@ public final class BluetoothGatt implements BluetoothProfile {
* of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
* {@link BluetoothDevice#PHY_OPTION_S8}
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
try {
mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
- phyOptions);
+ phyOptions, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -996,9 +1026,11 @@ public final class BluetoothGatt implements BluetoothProfile {
* Read the current transmitter PHY and receiver PHY of the connection. The values are returned
* in {@link BluetoothGattCallback#onPhyRead}
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void readPhy() {
try {
- mService.clientReadPhy(mClientIf, mDevice.getAddress());
+ mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1009,6 +1041,7 @@ public final class BluetoothGatt implements BluetoothProfile {
*
* @return remote bluetooth device
*/
+ @RequiresNoPermission
public BluetoothDevice getDevice() {
return mDevice;
}
@@ -1022,10 +1055,11 @@ public final class BluetoothGatt implements BluetoothProfile {
* triggered. If the discovery was successful, the remote services can be
* retrieved using the {@link #getServices} function.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the remote service discovery has been started
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean discoverServices() {
if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1033,7 +1067,7 @@ public final class BluetoothGatt implements BluetoothProfile {
mServices.clear();
try {
- mService.discoverServices(mClientIf, mDevice.getAddress());
+ mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1047,11 +1081,12 @@ public final class BluetoothGatt implements BluetoothProfile {
* It should never be used by real applications. The service is not searched
* for characteristics and descriptors, or returned in any callback.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the remote service discovery has been started
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean discoverServiceByUuid(UUID uuid) {
if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1059,7 +1094,8 @@ public final class BluetoothGatt implements BluetoothProfile {
mServices.clear();
try {
- mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
+ mService.discoverServiceByUuid(
+ mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1073,11 +1109,11 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>This function requires that service discovery has been completed
* for the given device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return List of services on the remote device. Returns an empty list if service discovery has
* not yet been performed.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public List<BluetoothGattService> getServices() {
List<BluetoothGattService> result =
new ArrayList<BluetoothGattService>();
@@ -1101,12 +1137,12 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>If multiple instances of the same service (as identified by UUID)
* exist, the first instance of the service is returned.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param uuid UUID of the requested service
* @return BluetoothGattService if supported, or null if the requested service is not offered by
* the remote device.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public BluetoothGattService getService(UUID uuid) {
for (BluetoothGattService service : mServices) {
if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
@@ -1124,11 +1160,12 @@ public final class BluetoothGatt implements BluetoothProfile {
* is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
* callback.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param characteristic Characteristic to read from the remote device
* @return true, if the read operation was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
return false;
@@ -1150,7 +1187,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.readCharacteristic(mClientIf, device.getAddress(),
- characteristic.getInstanceId(), AUTHENTICATION_NONE);
+ characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
@@ -1169,12 +1206,13 @@ public final class BluetoothGatt implements BluetoothProfile {
* is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
* callback.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param uuid UUID of characteristic to read from the remote device
* @return true, if the read operation was initiated successfully
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
if (mService == null || mClientIf == 0) return false;
@@ -1186,7 +1224,8 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
- new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
+ new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
@@ -1206,11 +1245,12 @@ public final class BluetoothGatt implements BluetoothProfile {
* {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
* reporting the result of the operation.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param characteristic Characteristic to write on the remote device
* @return true, if the write operation was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
&& (characteristic.getProperties()
@@ -1235,7 +1275,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.writeCharacteristic(mClientIf, device.getAddress(),
characteristic.getInstanceId(), characteristic.getWriteType(),
- AUTHENTICATION_NONE, characteristic.getValue());
+ AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
@@ -1254,11 +1294,12 @@ public final class BluetoothGatt implements BluetoothProfile {
* {@link BluetoothGattCallback#onDescriptorRead} callback is
* triggered, signaling the result of the operation.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param descriptor Descriptor value to read from the remote device
* @return true, if the read operation was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
if (mService == null || mClientIf == 0) return false;
@@ -1279,7 +1320,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.readDescriptor(mClientIf, device.getAddress(),
- descriptor.getInstanceId(), AUTHENTICATION_NONE);
+ descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
@@ -1297,11 +1338,12 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
* triggered to report the result of the write operation.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param descriptor Descriptor to write to the associated remote device
* @return true, if the write operation was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
@@ -1322,7 +1364,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
- AUTHENTICATION_NONE, descriptor.getValue());
+ AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
@@ -1350,16 +1392,17 @@ public final class BluetoothGatt implements BluetoothProfile {
* cancel the current transaction without committing any values on the
* remote device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the reliable write transaction has been initiated
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean beginReliableWrite() {
if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
try {
- mService.beginReliableWrite(mClientIf, mDevice.getAddress());
+ mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1377,10 +1420,11 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
* invoked to indicate whether the transaction has been executed correctly.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the request to execute the transaction has been sent
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean executeReliableWrite() {
if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1391,7 +1435,7 @@ public final class BluetoothGatt implements BluetoothProfile {
}
try {
- mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
+ mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
@@ -1408,15 +1452,16 @@ public final class BluetoothGatt implements BluetoothProfile {
*
* <p>Calling this function will discard all queued characteristic write
* operations for a given remote device.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void abortReliableWrite() {
if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return;
try {
- mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
+ mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1426,6 +1471,8 @@ public final class BluetoothGatt implements BluetoothProfile {
* @deprecated Use {@link #abortReliableWrite()}
*/
@Deprecated
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void abortReliableWrite(BluetoothDevice mDevice) {
abortReliableWrite();
}
@@ -1438,12 +1485,13 @@ public final class BluetoothGatt implements BluetoothProfile {
* triggered if the remote device indicates that the given characteristic
* has changed.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param characteristic The characteristic for which to enable notifications
* @param enable Set to true to enable notifications/indications
* @return true, if the requested notification status was set successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enable) {
if (DBG) {
@@ -1460,7 +1508,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.registerForNotification(mClientIf, device.getAddress(),
- characteristic.getInstanceId(), enable);
+ characteristic.getInstanceId(), enable, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1476,12 +1524,14 @@ public final class BluetoothGatt implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean refresh() {
if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
try {
- mService.refreshDevice(mClientIf, mDevice.getAddress());
+ mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1496,16 +1546,17 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
* invoked when the RSSI value has been read.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the RSSI value has been requested successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean readRemoteRssi() {
if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
try {
- mService.readRemoteRssi(mClientIf, mDevice.getAddress());
+ mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1524,10 +1575,11 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
* whether this operation was successful.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the new MTU value has been requested successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean requestMtu(int mtu) {
if (DBG) {
Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
@@ -1536,7 +1588,7 @@ public final class BluetoothGatt implements BluetoothProfile {
if (mService == null || mClientIf == 0) return false;
try {
- mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
+ mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1556,6 +1608,8 @@ public final class BluetoothGatt implements BluetoothProfile {
* or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
* @throws IllegalArgumentException If the parameters are outside of their specified range.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean requestConnectionPriority(int connectionPriority) {
if (connectionPriority < CONNECTION_PRIORITY_BALANCED
|| connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
@@ -1566,7 +1620,8 @@ public final class BluetoothGatt implements BluetoothProfile {
if (mService == null || mClientIf == 0) return false;
try {
- mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
+ mService.connectionParameterUpdate(
+ mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1583,6 +1638,8 @@ public final class BluetoothGatt implements BluetoothProfile {
* @return true, if the request is send to the Bluetooth stack.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
int slaveLatency, int supervisionTimeout,
int minConnectionEventLen, int maxConnectionEventLen) {
@@ -1598,9 +1655,10 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
- minConnectionInterval, maxConnectionInterval,
- slaveLatency, supervisionTimeout,
- minConnectionEventLen, maxConnectionEventLen);
+ minConnectionInterval, maxConnectionInterval,
+ slaveLatency, supervisionTimeout,
+ minConnectionEventLen, maxConnectionEventLen,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1616,6 +1674,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public int getConnectionState(BluetoothDevice device) {
throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
@@ -1627,6 +1686,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public List<BluetoothDevice> getConnectedDevices() {
throw new UnsupportedOperationException(
"Use BluetoothManager#getConnectedDevices instead.");
@@ -1640,6 +1700,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
throw new UnsupportedOperationException(
"Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java
index 8f1b59cf69..8a7d4baf5a 100644
--- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -237,7 +237,6 @@ public class BluetoothGattCharacteristic implements Parcelable {
/**
* Create a new BluetoothGattCharacteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param uuid The UUID for this characteristic
* @param properties Properties of this characteristic
@@ -344,7 +343,6 @@ public class BluetoothGattCharacteristic implements Parcelable {
/**
* Adds a descriptor to this characteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param descriptor Descriptor to be added to this characteristic.
* @return true, if the descriptor was added to the characteristic
diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java
index 49ba281e2e..ed5ea08730 100644
--- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -128,7 +128,6 @@ public class BluetoothGattDescriptor implements Parcelable {
/**
* Create a new BluetoothGattDescriptor.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param uuid The UUID for this descriptor
* @param permissions Permissions for this descriptor
@@ -139,7 +138,6 @@ public class BluetoothGattDescriptor implements Parcelable {
/**
* Create a new BluetoothGattDescriptor.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param characteristic The characteristic this descriptor belongs to
* @param uuid The UUID for this descriptor
@@ -228,8 +226,6 @@ public class BluetoothGattDescriptor implements Parcelable {
* <p>If a remote device offers multiple descriptors with the same UUID,
* the instance ID is used to distuinguish between descriptors.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return Instance ID of this descriptor
* @hide
*/
diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java
index 088b0169b6..3e799defa5 100644
--- a/framework/java/android/bluetooth/BluetoothGattServer.java
+++ b/framework/java/android/bluetooth/BluetoothGattServer.java
@@ -16,6 +16,12 @@
package android.bluetooth;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
+import android.content.AttributionSource;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
@@ -40,8 +46,10 @@ public final class BluetoothGattServer implements BluetoothProfile {
private static final boolean DBG = true;
private static final boolean VDBG = false;
- private BluetoothAdapter mAdapter;
- private IBluetoothGatt mService;
+ private final IBluetoothGatt mService;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
+
private BluetoothGattServerCallback mCallback;
private Object mServerIfLock = new Object();
@@ -55,6 +63,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Bluetooth GATT interface callbacks
*/
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
new IBluetoothGattServerCallback.Stub() {
/**
@@ -376,9 +385,11 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Create a BluetoothGattServer proxy object.
*/
- /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) {
+ /* package */ BluetoothGattServer(IBluetoothGatt iGatt, int transport,
+ BluetoothAdapter adapter) {
mService = iGatt;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mCallback = null;
mServerIf = 0;
mTransport = transport;
@@ -425,6 +436,8 @@ public final class BluetoothGattServer implements BluetoothProfile {
* Application should call this method as early as possible after it is done with
* this GATT server.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void close() {
if (DBG) Log.d(TAG, "close()");
unregisterCallback();
@@ -436,12 +449,13 @@ public final class BluetoothGattServer implements BluetoothProfile {
* <p>This is an asynchronous call. The callback is used to notify
* success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @return true, the callback will be called to notify success or failure, false on immediate
* error
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
return registerCallback(callback, false);
}
@@ -452,14 +466,15 @@ public final class BluetoothGattServer implements BluetoothProfile {
* <p>This is an asynchronous call. The callback is used to notify
* success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @param eatt_support indicates if server can use eatt
* @return true, the callback will be called to notify success or failure, false on immediate
* error
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ boolean registerCallback(BluetoothGattServerCallback callback,
boolean eatt_support) {
if (DBG) Log.d(TAG, "registerCallback()");
@@ -478,7 +493,8 @@ public final class BluetoothGattServer implements BluetoothProfile {
mCallback = callback;
try {
- mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, eatt_support);
+ mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback,
+ eatt_support, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
mCallback = null;
@@ -504,13 +520,15 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Unregister the current application and callbacks.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void unregisterCallback() {
if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
if (mService == null || mServerIf == 0) return;
try {
mCallback = null;
- mService.unregisterServer(mServerIf);
+ mService.unregisterServer(mServerIf, mAttributionSource);
mServerIf = 0;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -548,12 +566,13 @@ public final class BluetoothGattServer implements BluetoothProfile {
* subsequent connections to known devices should be invoked with the
* autoConnect parameter set to true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param autoConnect Whether to directly connect to the remote device (false) or to
* automatically connect as soon as the remote device becomes available (true).
* @return true, if the connection attempt was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device, boolean autoConnect) {
if (DBG) {
Log.d(TAG,
@@ -563,7 +582,8 @@ public final class BluetoothGattServer implements BluetoothProfile {
try {
// autoConnect is inverse of "isDirect"
- mService.serverConnect(mServerIf, device.getAddress(), !autoConnect, mTransport);
+ mService.serverConnect(
+ mServerIf, device.getAddress(), !autoConnect, mTransport, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -576,16 +596,17 @@ public final class BluetoothGattServer implements BluetoothProfile {
* Disconnects an established connection, or cancels a connection attempt
* currently in progress.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Remote device
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void cancelConnection(BluetoothDevice device) {
if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
if (mService == null || mServerIf == 0) return;
try {
- mService.serverDisconnect(mServerIf, device.getAddress());
+ mService.serverDisconnect(mServerIf, device.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -609,10 +630,12 @@ public final class BluetoothGattServer implements BluetoothProfile {
* of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
* {@link BluetoothDevice#PHY_OPTION_S8}
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
try {
mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
- phyOptions);
+ phyOptions, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -624,9 +647,11 @@ public final class BluetoothGattServer implements BluetoothProfile {
*
* @param device The remote device to send this response to
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void readPhy(BluetoothDevice device) {
try {
- mService.serverReadPhy(mServerIf, device.getAddress());
+ mService.serverReadPhy(mServerIf, device.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -645,14 +670,15 @@ public final class BluetoothGattServer implements BluetoothProfile {
* <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device The remote device to send this response to
* @param requestId The ID of the request that was received with the callback
* @param status The status of the request to be sent to the remote devices
* @param offset Value offset for partial read/write response
* @param value The value of the attribute that was read/written (optional)
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendResponse(BluetoothDevice device, int requestId,
int status, int offset, byte[] value) {
if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
@@ -660,7 +686,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
try {
mService.sendResponse(mServerIf, device.getAddress(), requestId,
- status, offset, value);
+ status, offset, value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -677,8 +703,6 @@ public final class BluetoothGattServer implements BluetoothProfile {
* for every client that requests notifications/indications by writing
* to the "Client Configuration" descriptor for the given characteristic.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device The remote device to receive the notification/indication
* @param characteristic The local characteristic that has been updated
* @param confirm true to request confirmation from the client (indication), false to send a
@@ -686,6 +710,9 @@ public final class BluetoothGattServer implements BluetoothProfile {
* @return true, if the notification has been triggered successfully
* @throws IllegalArgumentException
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean notifyCharacteristicChanged(BluetoothDevice device,
BluetoothGattCharacteristic characteristic, boolean confirm) {
if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
@@ -702,7 +729,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
try {
mService.sendNotification(mServerIf, device.getAddress(),
characteristic.getInstanceId(), confirm,
- characteristic.getValue());
+ characteristic.getValue(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -724,11 +751,12 @@ public final class BluetoothGattServer implements BluetoothProfile {
* whether this service has been added successfully. Do not add another service
* before this callback.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param service Service to be added to the list of services provided by this device.
* @return true, if the request to add service has been initiated
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean addService(BluetoothGattService service) {
if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
if (mService == null || mServerIf == 0) return false;
@@ -736,7 +764,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
mPendingService = service;
try {
- mService.addService(mServerIf, service);
+ mService.addService(mServerIf, service, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -748,11 +776,12 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Removes a service from the list of services to be provided.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param service Service to be removed.
* @return true, if the service has been removed
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean removeService(BluetoothGattService service) {
if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
if (mService == null || mServerIf == 0) return false;
@@ -762,7 +791,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
if (intService == null) return false;
try {
- mService.removeService(mServerIf, service.getInstanceId());
+ mService.removeService(mServerIf, service.getInstanceId(), mAttributionSource);
mServices.remove(intService);
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -774,14 +803,16 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Remove all services from the list of provided services.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void clearServices() {
if (DBG) Log.d(TAG, "clearServices()");
if (mService == null || mServerIf == 0) return;
try {
- mService.clearServices(mServerIf);
+ mService.clearServices(mServerIf, mAttributionSource);
mServices.clear();
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -794,10 +825,10 @@ public final class BluetoothGattServer implements BluetoothProfile {
* <p>An application must call {@link #addService} to add a serice to the
* list of services offered by this device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return List of services. Returns an empty list if no services have been added yet.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public List<BluetoothGattService> getServices() {
return mServices;
}
@@ -809,12 +840,12 @@ public final class BluetoothGattServer implements BluetoothProfile {
* <p>If multiple instances of the same service (as identified by UUID)
* exist, the first instance of the service is returned.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param uuid UUID of the requested service
* @return BluetoothGattService if supported, or null if the requested service is not offered by
* this device.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public BluetoothGattService getService(UUID uuid) {
for (BluetoothGattService service : mServices) {
if (service.getUuid().equals(uuid)) {
@@ -833,6 +864,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public int getConnectionState(BluetoothDevice device) {
throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
@@ -844,6 +876,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public List<BluetoothDevice> getConnectedDevices() {
throw new UnsupportedOperationException(
"Use BluetoothManager#getConnectedDevices instead.");
@@ -857,6 +890,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
throw new UnsupportedOperationException(
"Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java
index 23dc7c8308..f64d09fc30 100644
--- a/framework/java/android/bluetooth/BluetoothGattService.java
+++ b/framework/java/android/bluetooth/BluetoothGattService.java
@@ -15,6 +15,9 @@
*/
package android.bluetooth;
+import android.annotation.RequiresPermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -98,7 +101,6 @@ public class BluetoothGattService implements Parcelable {
/**
* Create a new BluetoothGattService.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param uuid The UUID for this service
* @param serviceType The type of this service,
@@ -225,11 +227,11 @@ public class BluetoothGattService implements Parcelable {
/**
* Add an included service to this service.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param service The service to be added
* @return true, if the included service was added to the service
*/
+ @RequiresLegacyBluetoothPermission
public boolean addService(BluetoothGattService service) {
mIncludedServices.add(service);
return true;
@@ -237,11 +239,11 @@ public class BluetoothGattService implements Parcelable {
/**
* Add a characteristic to this service.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param characteristic The characteristics to be added
* @return true, if the characteristic was added to the service
*/
+ @RequiresLegacyBluetoothPermission
public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) {
mCharacteristics.add(characteristic);
characteristic.setService(this);
diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java
index 632572dea3..b594ae3443 100644
--- a/framework/java/android/bluetooth/BluetoothHeadset.java
+++ b/framework/java/android/bluetooth/BluetoothHeadset.java
@@ -21,11 +21,18 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -33,6 +40,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.util.CloseGuard;
import android.util.Log;
import java.util.ArrayList;
@@ -70,10 +78,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
* <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}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
@@ -90,10 +98,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
* </ul>
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
- * to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_AUDIO_STATE_CHANGED =
"android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
@@ -107,11 +115,11 @@ public final class BluetoothHeadset implements BluetoothProfile {
* be null if no device is active. </li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage(trackingBug = 171933273)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
@@ -147,9 +155,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
* <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
* <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
* </ul>
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
- * to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
"android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
@@ -299,10 +308,13 @@ public final class BluetoothHeadset implements BluetoothProfile {
* are given an assigned number. Below shows the assigned number of Indicator added so far
* - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
* - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
*
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
"android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
@@ -328,11 +340,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
+ private final CloseGuard mCloseGuard = new CloseGuard();
+
private Context mContext;
private ServiceListener mServiceListener;
private volatile IBluetoothHeadset mService;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
public void onBluetoothStateChange(boolean up) {
@@ -348,10 +364,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
/**
* Create a BluetoothHeadset proxy object.
*/
- /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
+ /* package */ BluetoothHeadset(Context context, ServiceListener l, BluetoothAdapter adapter) {
mContext = context;
mServiceListener = l;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
+
+ // Preserve legacy compatibility where apps were depending on
+ // registerStateChangeCallback() performing a permissions check which
+ // has been relaxed in modern platform versions
+ if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+ && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Need BLUETOOTH permission");
+ }
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
@@ -363,6 +389,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
doBind();
+ mCloseGuard.open("close");
}
private boolean doBind() {
@@ -416,6 +443,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
mServiceListener = null;
doUnbind();
+ mCloseGuard.close();
+ }
+
+ /** {@hide} */
+ @Override
+ protected void finalize() throws Throwable {
+ mCloseGuard.warnIfOpen();
+ close();
}
/**
@@ -432,15 +467,17 @@ public final class BluetoothHeadset implements BluetoothProfile {
* the state. Users can get the connection state of the profile
* from this intent.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -474,15 +511,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
* {@link #STATE_DISCONNECTING} can be used to distinguish between the
* two scenarios.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -502,12 +538,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevicesWithAttribution(mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -521,12 +561,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -540,6 +584,8 @@ public final class BluetoothHeadset implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -571,7 +617,12 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
@Deprecated
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
final IBluetoothHeadset service = mService;
@@ -582,7 +633,8 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
try {
return service.setPriority(
- device, BluetoothAdapter.priorityToConnectionPolicy(priority));
+ device, BluetoothAdapter.priorityToConnectionPolicy(priority),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -605,7 +657,12 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -616,7 +673,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -638,13 +695,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device));
+ return BluetoothAdapter.connectionPolicyToPriority(
+ service.getPriority(device, mAttributionSource));
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.PRIORITY_OFF;
@@ -666,13 +726,17 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -688,13 +752,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @param device Bluetooth device
* @return true if echo cancellation and/or noise reduction is supported, false otherwise
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isNoiseReductionSupported()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.isNoiseReductionSupported(device);
+ return service.isNoiseReductionSupported(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -709,13 +775,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @param device Bluetooth device
* @return true if voice recognition is supported, false otherwise
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isVoiceRecognitionSupported()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.isVoiceRecognitionSupported(device);
+ return service.isVoiceRecognitionSupported(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -738,19 +806,23 @@ public final class BluetoothHeadset implements BluetoothProfile {
* audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
* in case of failure to establish the audio connection.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Bluetooth headset
* @return false if there is no headset connected, or the connected headset doesn't support
* voice recognition, or voice recognition is already started, or audio channel is occupied,
* or on error, true otherwise
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.startVoiceRecognition(device);
+ return service.startVoiceRecognition(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -767,18 +839,19 @@ public final class BluetoothHeadset implements BluetoothProfile {
* If this function returns true, this intent will be broadcasted with
* {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Bluetooth headset
* @return false if there is no headset connected, or voice recognition has not started,
* or voice recognition has ended on this headset, or on error, true otherwise
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.stopVoiceRecognition(device);
+ return service.stopVoiceRecognition(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -790,17 +863,18 @@ public final class BluetoothHeadset implements BluetoothProfile {
/**
* Check if Bluetooth SCO audio is connected.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Bluetooth headset
* @return true if SCO is connected, false otherwise or on error
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isAudioConnected(BluetoothDevice device) {
if (VDBG) log("isAudioConnected()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.isAudioConnected(device);
+ return service.isAudioConnected(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -827,12 +901,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
final IBluetoothHeadset service = mService;
if (service != null && !isDisabled()) {
try {
- return service.getAudioState(device);
+ return service.getAudioState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -853,12 +929,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAudioRouteAllowed(boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- service.setAudioRouteAllowed(allowed);
+ service.setAudioRouteAllowed(allowed, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -874,12 +952,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getAudioRouteAllowed() {
if (VDBG) log("getAudioRouteAllowed");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.getAudioRouteAllowed();
+ return service.getAudioRouteAllowed(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -897,12 +977,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
* False to use SCO audio in normal manner
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setForceScoAudio(boolean forced) {
if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- service.setForceScoAudio(forced);
+ service.setForceScoAudio(forced, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -915,18 +997,19 @@ public final class BluetoothHeadset implements BluetoothProfile {
/**
* Check if at least one headset's SCO audio is connected or connecting
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @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;
if (service != null && isEnabled()) {
try {
- return service.isAudioOn();
+ return service.isAudioOn(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -955,11 +1038,13 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio() {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.connectAudio();
+ return service.connectAudio(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -982,11 +1067,13 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio() {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.disconnectAudio();
+ return service.disconnectAudio(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1018,14 +1105,19 @@ public final class BluetoothHeadset implements BluetoothProfile {
* - binder is dead or Bluetooth is disabled or other error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
@UnsupportedAppUsage
public boolean startScoUsingVirtualVoiceCall() {
if (DBG) log("startScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.startScoUsingVirtualVoiceCall();
+ return service.startScoUsingVirtualVoiceCall(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1048,14 +1140,19 @@ public final class BluetoothHeadset implements BluetoothProfile {
* - binder is dead or Bluetooth is disabled or other error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
@UnsupportedAppUsage
public boolean stopScoUsingVirtualVoiceCall() {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.stopScoUsingVirtualVoiceCall();
+ return service.stopScoUsingVirtualVoiceCall(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1075,12 +1172,18 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
int type, String name) {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- service.phoneStateChanged(numActive, numHeld, callState, number, type, name);
+ service.phoneStateChanged(numActive, numHeld, callState, number, type, name,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1095,12 +1198,18 @@ public final class BluetoothHeadset implements BluetoothProfile {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
String number, int type) {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- service.clccResponse(index, direction, status, mode, mpty, number, type);
+ service.clccResponse(index, direction, status, mode, mpty, number, type,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1119,8 +1228,6 @@ public final class BluetoothHeadset implements BluetoothProfile {
*
* <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Bluetooth headset.
* @param command A vendor-specific command.
* @param arg The argument that will be attached to the command.
@@ -1128,6 +1235,9 @@ public final class BluetoothHeadset implements BluetoothProfile {
* vendor-specific unsolicited result code, or on error. {@code true} otherwise.
* @throws IllegalArgumentException if {@code command} is {@code null}.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
String arg) {
if (DBG) {
@@ -1139,7 +1249,8 @@ public final class BluetoothHeadset implements BluetoothProfile {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.sendVendorSpecificResultCode(device, command, arg);
+ return service.sendVendorSpecificResultCode(device, command, arg,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -1164,15 +1275,17 @@ public final class BluetoothHeadset implements BluetoothProfile {
* {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
* with the active device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
* @param device Remote Bluetooth Device, could be null if phone call audio should not be
* streamed to a headset
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
@UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) {
@@ -1181,7 +1294,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
try {
- return service.setActiveDevice(device);
+ return service.setActiveDevice(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -1201,7 +1314,9 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
@UnsupportedAppUsage(trackingBug = 171933273)
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
if (VDBG) {
Log.d(TAG, "getActiveDevice");
@@ -1209,7 +1324,8 @@ public final class BluetoothHeadset implements BluetoothProfile {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.getActiveDevice();
+ return Attributable.setAttributionSource(
+ service.getActiveDevice(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -1227,7 +1343,9 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @return true if in-band ringing is enabled, false if in-band ringing is disabled
* @hide
*/
- @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isInbandRingingEnabled() {
if (DBG) {
log("isInbandRingingEnabled()");
@@ -1235,7 +1353,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- return service.isInbandRingingEnabled();
+ return service.isInbandRingingEnabled(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -1257,6 +1375,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothProfileServiceConnection mConnection =
new IBluetoothProfileServiceConnection.Stub() {
@Override
@@ -1293,6 +1412,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
Log.d(TAG, msg);
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java
index e5b2a1e23c..83108d22e0 100644
--- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -16,10 +16,15 @@
package android.bluetooth;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
@@ -71,6 +76,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* booleans with value <code>true</code>,
* and not supported ones are <strong>not</strong> being sent at all.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED";
@@ -89,6 +97,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* it also includes {@link #EXTRA_AUDIO_WBS}
* indicating wide band speech support.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_AUDIO_STATE_CHANGED =
"android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED";
@@ -105,6 +116,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* {@link #EXTRA_VOICE_RECOGNITION},
* {@link #EXTRA_IN_BAND_RING}</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_AG_EVENT =
"android.bluetooth.headsetclient.profile.action.AG_EVENT";
@@ -116,6 +130,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* with value of {@link BluetoothHeadsetClientCall} instance,
* representing actual call state.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CALL_CHANGED =
"android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED";
@@ -126,6 +143,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* like <code>ACTION_AG_EVENT</code> with <code>EXTRA_VOICE_RECOGNITION</code> value
* when for example user started voice recognition from HF unit.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_RESULT =
"android.bluetooth.headsetclient.profile.action.RESULT";
@@ -137,6 +157,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* Vendor event can be a response to an vendor specific command or unsolicited.
*
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT =
"android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT";
@@ -148,6 +171,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* {@link #EXTRA_NUMBER},
* with a <code>String</code> value representing phone number.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LAST_VTAG =
"android.bluetooth.headsetclient.profile.action.LAST_VTAG";
@@ -400,7 +426,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
public static final int CALL_ACCEPT_HOLD = 1;
public static final int CALL_ACCEPT_TERMINATE = 2;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT,
"BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) {
@@ -413,8 +440,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
/**
* Create a BluetoothHeadsetClient proxy object.
*/
- /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ /* package */ BluetoothHeadsetClient(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -447,13 +476,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device);
+ return service.connect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -473,13 +504,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -495,13 +528,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return list of connected devices; empty list if nothing is connected.
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -519,13 +555,17 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* list if nothing matches the <code>states</code>
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -542,13 +582,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return the state of connection of the device
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -569,7 +611,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -587,7 +630,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -599,7 +643,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -619,7 +663,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -636,14 +682,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -664,13 +712,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature
* is not supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.startVoiceRecognition(device);
+ return service.startVoiceRecognition(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -688,6 +738,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId,
String atCommand) {
if (DBG) log("sendVendorSpecificCommand()");
@@ -695,7 +747,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.sendVendorAtCommand(device, vendorId, atCommand);
+ return service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -715,13 +767,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature
* is not supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.stopVoiceRecognition(device);
+ return service.stopVoiceRecognition(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -736,13 +790,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @param device remote device
* @return list of calls; empty list if none call exists
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getCurrentCalls(device);
+ return Attributable.setAttributionSource(
+ service.getCurrentCalls(device, mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -757,13 +814,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @param device remote device
* @return bundle of AG indicators; null if device is not in CONNECTED state
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgEvents(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getCurrentAgEvents(device);
+ return service.getCurrentAgEvents(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -782,13 +841,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean acceptCall(BluetoothDevice device, int flag) {
if (DBG) log("acceptCall()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.acceptCall(device, flag);
+ return service.acceptCall(device, flag, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -804,13 +865,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean holdCall(BluetoothDevice device) {
if (DBG) log("holdCall()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.holdCall(device);
+ return service.holdCall(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -831,13 +894,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* supported.</p>
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean rejectCall(BluetoothDevice device) {
if (DBG) log("rejectCall()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.rejectCall(device);
+ return service.rejectCall(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -862,13 +927,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not
* supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
if (DBG) log("terminateCall()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.terminateCall(device, call);
+ return service.terminateCall(device, call, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -891,13 +958,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not
* supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enterPrivateMode(BluetoothDevice device, int index) {
if (DBG) log("enterPrivateMode()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.enterPrivateMode(device, index);
+ return service.enterPrivateMode(device, index, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -919,13 +988,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature
* is not supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean explicitCallTransfer(BluetoothDevice device) {
if (DBG) log("explicitCallTransfer()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.explicitCallTransfer(device);
+ return service.explicitCallTransfer(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -943,13 +1014,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* successfully; <code>{@link null}</code> otherwise; upon completion HFP sends {@link
* #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise;
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
if (DBG) log("dial()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.dial(device, number);
+ return Attributable.setAttributionSource(
+ service.dial(device, number, mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -968,13 +1042,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent;
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendDTMF(BluetoothDevice device, byte code) {
if (DBG) log("sendDTMF()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.sendDTMF(device, code);
+ return service.sendDTMF(device, code, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -995,13 +1071,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when
* feature is not supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getLastVoiceTagNumber(BluetoothDevice device) {
if (DBG) log("getLastVoiceTagNumber()");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getLastVoiceTagNumber(device);
+ return service.getLastVoiceTagNumber(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -1016,13 +1094,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* Note: This is an internal function and shouldn't be exposed
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled()) {
try {
- return service.getAudioState(device);
+ return service.getAudioState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1040,13 +1120,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @param allowed if routing is allowed to the device Note: This is an internal function and
* shouldn't be exposed
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled()) {
try {
- service.setAudioRouteAllowed(device, allowed);
+ service.setAudioRouteAllowed(device, allowed, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1063,13 +1145,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return whether the command succeeded Note: This is an internal function and shouldn't be
* exposed
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getAudioRouteAllowed(BluetoothDevice device) {
if (VDBG) log("getAudioRouteAllowed");
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled()) {
try {
- return service.getAudioRouteAllowed(device);
+ return service.getAudioRouteAllowed(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1089,12 +1173,14 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio(BluetoothDevice device) {
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled()) {
try {
- return service.connectAudio(device);
+ return service.connectAudio(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1114,12 +1200,14 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio(BluetoothDevice device) {
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled()) {
try {
- return service.disconnectAudio(device);
+ return service.disconnectAudio(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1136,12 +1224,14 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @param device remote device
* @return bundle of AG features; null if no service or AG not connected
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgFeatures(BluetoothDevice device) {
final IBluetoothHeadsetClient service =
getService();
if (service != null && isEnabled()) {
try {
- return service.getCurrentAgFeatures(device);
+ return service.getCurrentAgFeatures(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java
index 219d1596fb..3f1ef84612 100644
--- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -16,7 +16,10 @@
package android.bluetooth;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,7 +33,7 @@ import java.util.UUID;
*
* @hide
*/
-public final class BluetoothHeadsetClientCall implements Parcelable {
+public final class BluetoothHeadsetClientCall implements Parcelable, Attributable {
/* Call state */
/**
@@ -98,6 +101,11 @@ public final class BluetoothHeadsetClientCall implements Parcelable {
mCreationElapsedMilli = SystemClock.elapsedRealtime();
}
+ /** {@hide} */
+ public void setAttributionSource(@NonNull AttributionSource attributionSource) {
+ Attributable.setAttributionSource(mDevice, attributionSource);
+ }
+
/**
* Sets call's state.
*
diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java
index 5fd60e0016..65f68a943e 100644
--- a/framework/java/android/bluetooth/BluetoothHealth.java
+++ b/framework/java/android/bluetooth/BluetoothHealth.java
@@ -16,6 +16,10 @@
package android.bluetooth;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -111,8 +115,6 @@ public final class BluetoothHealth implements BluetoothProfile {
* which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so
* the callback is used to notify success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param name The friendly name associated with the application or configuration.
* @param dataType The dataType of the Source role of Health Profile to which the sink wants to
* connect to.
@@ -126,6 +128,10 @@ public final class BluetoothHealth implements BluetoothProfile {
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean registerSinkAppConfiguration(String name, int dataType,
BluetoothHealthCallback callback) {
Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated");
@@ -136,8 +142,6 @@ public final class BluetoothHealth implements BluetoothProfile {
* Unregister an application configuration that has been registered using
* {@link #registerSinkAppConfiguration}
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param config The health app configuration
* @return Success or failure.
*
@@ -147,6 +151,10 @@ public final class BluetoothHealth implements BluetoothProfile {
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated");
return false;
@@ -157,8 +165,6 @@ public final class BluetoothHealth implements BluetoothProfile {
* This is an asynchronous call. If this function returns true, the callback
* associated with the application configuration will be called.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device The remote Bluetooth device.
* @param config The application configuration which has been registered using {@link
* #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
@@ -170,6 +176,10 @@ public final class BluetoothHealth implements BluetoothProfile {
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean connectChannelToSource(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated");
@@ -181,8 +191,6 @@ public final class BluetoothHealth implements BluetoothProfile {
* This is an asynchronous call. If this function returns true, the callback
* associated with the application configuration will be called.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device The remote Bluetooth device.
* @param config The application configuration which has been registered using {@link
* #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
@@ -195,6 +203,10 @@ public final class BluetoothHealth implements BluetoothProfile {
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, int channelId) {
Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated");
@@ -205,8 +217,6 @@ public final class BluetoothHealth implements BluetoothProfile {
* Get the file descriptor of the main channel associated with the remote device
* and application configuration.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* <p> Its the responsibility of the caller to close the ParcelFileDescriptor
* when done.
*
@@ -220,6 +230,10 @@ public final class BluetoothHealth implements BluetoothProfile {
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated");
@@ -229,8 +243,6 @@ public final class BluetoothHealth implements BluetoothProfile {
/**
* Get the current connection state of the profile.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* This is not specific to any application configuration but represents the connection
* state of the local Bluetooth adapter with the remote device. This can be used
* by applications like status bar which would just like to know the state of the
@@ -241,6 +253,10 @@ public final class BluetoothHealth implements BluetoothProfile {
* #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
*/
@Override
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public int getConnectionState(BluetoothDevice device) {
Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated");
return STATE_DISCONNECTED;
@@ -251,8 +267,6 @@ public final class BluetoothHealth implements BluetoothProfile {
*
* <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* This is not specific to any application configuration but represents the connection
* state of the local Bluetooth adapter for this profile. This can be used
* by applications like status bar which would just like to know the state of the
@@ -261,6 +275,10 @@ public final class BluetoothHealth implements BluetoothProfile {
* @return List of devices. The list will be empty on error.
*/
@Override
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public List<BluetoothDevice> getConnectedDevices() {
Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated");
return new ArrayList<>();
@@ -273,8 +291,7 @@ public final class BluetoothHealth implements BluetoothProfile {
* <p> If none of the devices match any of the given states,
* an empty list will be returned.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- * This is not specific to any application configuration but represents the connection
+ * <p>This is not specific to any application configuration but represents the connection
* state of the local Bluetooth adapter for this profile. This can be used
* by applications like status bar which would just like to know the state of the
* local adapter.
@@ -284,6 +301,10 @@ public final class BluetoothHealth implements BluetoothProfile {
* @return List of devices. The list will be empty on error.
*/
@Override
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated");
return new ArrayList<>();
diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java
index ff78825e0f..183f4d55bd 100644
--- a/framework/java/android/bluetooth/BluetoothHearingAid.java
+++ b/framework/java/android/bluetooth/BluetoothHearingAid.java
@@ -21,9 +21,15 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
@@ -64,10 +70,10 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* <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}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
@@ -81,11 +87,11 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* be null if no device is active. </li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
@@ -126,7 +132,8 @@ public final class BluetoothHearingAid implements BluetoothProfile {
*/
public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID,
"BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
@@ -140,8 +147,10 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* Create a BluetoothHearingAid proxy object for interacting with the local
* Bluetooth Hearing Aid service.
*/
- /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ /* package */ BluetoothHearingAid(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -167,13 +176,17 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled() && isValidDevice(device)) {
- return service.connect(device);
+ return service.connect(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -205,13 +218,17 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled() && isValidDevice(device)) {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -225,12 +242,15 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled()) {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
@@ -244,13 +264,17 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled()) {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
@@ -264,6 +288,8 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BluetoothProfile.BtProfileState int getConnectionState(
@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
@@ -271,7 +297,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
try {
if (service != null && isEnabled()
&& isValidDevice(device)) {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
@@ -295,14 +321,14 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
* with the active device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
* @param device the remote Bluetooth device. Could be null to clear
* the active device and stop streaming audio to a Bluetooth device.
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
@@ -310,7 +336,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
try {
if (service != null && isEnabled()
&& ((device == null) || isValidDevice(device))) {
- service.setActiveDevice(device);
+ service.setActiveDevice(device, mAttributionSource);
return true;
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
@@ -330,13 +356,16 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled()) {
- return service.getActiveDevices();
+ return Attributable.setAttributionSource(
+ service.getActiveDevices(mAttributionSource), mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<>();
@@ -357,7 +386,11 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -376,7 +409,11 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -389,7 +426,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
&& connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
return false;
}
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -409,7 +446,11 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -427,7 +468,11 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
verifyDeviceNotNull(device, "getConnectionPolicy");
@@ -435,7 +480,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
try {
if (service != null && isEnabled()
&& isValidDevice(device)) {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -473,6 +518,8 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @param volume Absolute volume to be set on remote
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setVolume(int volume) {
if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
@@ -485,7 +532,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
if (!isEnabled()) return;
- service.setVolume(volume);
+ service.setVolume(volume, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
@@ -502,7 +549,11 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public long getHiSyncId(@NonNull BluetoothDevice device) {
if (VDBG) {
log("getHiSyncId(" + device + ")");
@@ -517,7 +568,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
- return service.getHiSyncId(device);
+ return service.getHiSyncId(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return HI_SYNC_ID_INVALID;
@@ -531,7 +582,9 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @return SIDE_LEFT or SIDE_RIGHT
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceSide(BluetoothDevice device) {
if (VDBG) {
log("getDeviceSide(" + device + ")");
@@ -540,7 +593,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
try {
if (service != null && isEnabled()
&& isValidDevice(device)) {
- return service.getDeviceSide(device);
+ return service.getDeviceSide(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return SIDE_LEFT;
@@ -557,7 +610,9 @@ public final class BluetoothHearingAid implements BluetoothProfile {
* @return MODE_MONAURAL or MODE_BINAURAL
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceMode(BluetoothDevice device) {
if (VDBG) {
log("getDeviceMode(" + device + ")");
@@ -566,7 +621,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
try {
if (service != null && isEnabled()
&& isValidDevice(device)) {
- return service.getDeviceMode(device);
+ return service.getDeviceMode(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return MODE_MONAURAL;
diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java
index b5959c06cc..c2744b89aa 100644
--- a/framework/java/android/bluetooth/BluetoothHidDevice.java
+++ b/framework/java/android/bluetooth/BluetoothHidDevice.java
@@ -22,6 +22,10 @@ import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -56,9 +60,10 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* <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}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
@@ -334,56 +339,94 @@ public final class BluetoothHidDevice implements BluetoothProfile {
private final Executor mExecutor;
private final Callback mCallback;
+ private final AttributionSource mAttributionSource;
- CallbackWrapper(Executor executor, Callback callback) {
+ CallbackWrapper(Executor executor, Callback callback, AttributionSource attributionSource) {
mExecutor = executor;
mCallback = callback;
+ mAttributionSource = attributionSource;
}
@Override
public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
- clearCallingIdentity();
- mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered));
+ Attributable.setAttributionSource(pluggedDevice, mAttributionSource);
+ final long token = clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered));
+ } finally {
+ restoreCallingIdentity(token);
+ }
}
@Override
public void onConnectionStateChanged(BluetoothDevice device, int state) {
- clearCallingIdentity();
- mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state));
+ Attributable.setAttributionSource(device, mAttributionSource);
+ final long token = clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state));
+ } finally {
+ restoreCallingIdentity(token);
+ }
}
@Override
public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
- clearCallingIdentity();
- mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize));
+ Attributable.setAttributionSource(device, mAttributionSource);
+ final long token = clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize));
+ } finally {
+ restoreCallingIdentity(token);
+ }
}
@Override
public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
- clearCallingIdentity();
- mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data));
+ Attributable.setAttributionSource(device, mAttributionSource);
+ final long token = clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data));
+ } finally {
+ restoreCallingIdentity(token);
+ }
}
@Override
public void onSetProtocol(BluetoothDevice device, byte protocol) {
- clearCallingIdentity();
- mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol));
+ Attributable.setAttributionSource(device, mAttributionSource);
+ final long token = clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol));
+ } finally {
+ restoreCallingIdentity(token);
+ }
}
@Override
public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
- clearCallingIdentity();
- mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data));
+ Attributable.setAttributionSource(device, mAttributionSource);
+ final long token = clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data));
+ } finally {
+ restoreCallingIdentity(token);
+ }
}
@Override
public void onVirtualCableUnplug(BluetoothDevice device) {
- clearCallingIdentity();
- mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device));
+ Attributable.setAttributionSource(device, mAttributionSource);
+ final long token = clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device));
+ } finally {
+ restoreCallingIdentity(token);
+ }
}
}
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE,
"BluetoothHidDevice", IBluetoothHidDevice.class.getName()) {
@@ -393,8 +436,9 @@ public final class BluetoothHidDevice implements BluetoothProfile {
}
};
- BluetoothHidDevice(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothHidDevice(Context context, ServiceListener listener, BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -408,11 +452,14 @@ public final class BluetoothHidDevice implements BluetoothProfile {
/** {@inheritDoc} */
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -425,11 +472,15 @@ public final class BluetoothHidDevice implements BluetoothProfile {
/** {@inheritDoc} */
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -442,11 +493,13 @@ public final class BluetoothHidDevice implements BluetoothProfile {
/** {@inheritDoc} */
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -480,6 +533,8 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* object is required.
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean registerApp(
BluetoothHidDeviceAppSdpSettings sdp,
BluetoothHidDeviceAppQosSettings inQos,
@@ -503,8 +558,8 @@ public final class BluetoothHidDevice implements BluetoothProfile {
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- CallbackWrapper cbw = new CallbackWrapper(executor, callback);
- result = service.registerApp(sdp, inQos, outQos, cbw);
+ CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource);
+ result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -525,13 +580,15 @@ public final class BluetoothHidDevice implements BluetoothProfile {
*
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean unregisterApp() {
boolean result = false;
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- result = service.unregisterApp();
+ result = service.unregisterApp(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -550,13 +607,15 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* @param data Report data, not including Report Id.
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
boolean result = false;
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- result = service.sendReport(device, id, data);
+ result = service.sendReport(device, id, data, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -576,13 +635,15 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* @param data Report data, not including Report Id.
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
boolean result = false;
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- result = service.replyReport(device, type, id, data);
+ result = service.replyReport(device, type, id, data, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -600,13 +661,15 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* @param error Error to be sent for SET_REPORT via HANDSHAKE.
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean reportError(BluetoothDevice device, byte error) {
boolean result = false;
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- result = service.reportError(device, error);
+ result = service.reportError(device, error, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -623,12 +686,14 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* @return the current user name, or empty string if cannot get the name
* {@hide}
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getUserAppName() {
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- return service.getUserAppName();
+ return service.getUserAppName(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -647,13 +712,15 @@ public final class BluetoothHidDevice implements BluetoothProfile {
*
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
boolean result = false;
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- result = service.connect(device);
+ result = service.connect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -671,13 +738,15 @@ public final class BluetoothHidDevice implements BluetoothProfile {
*
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
boolean result = false;
final IBluetoothHidDevice service = getService();
if (service != null) {
try {
- result = service.disconnect(device);
+ result = service.disconnect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -706,7 +775,11 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -718,7 +791,7 @@ public final class BluetoothHidDevice implements BluetoothProfile {
&& connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
return false;
}
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java
index 9561d93838..fb4cbb2eb1 100644
--- a/framework/java/android/bluetooth/BluetoothHidHost.java
+++ b/framework/java/android/bluetooth/BluetoothHidHost.java
@@ -21,8 +21,13 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -65,11 +70,11 @@ public final class BluetoothHidHost implements BluetoothProfile {
* <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}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
@SuppressLint("ActionValue")
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
@@ -77,6 +82,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PROTOCOL_MODE_CHANGED =
"android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED";
@@ -84,6 +91,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_HANDSHAKE =
"android.bluetooth.input.profile.action.HANDSHAKE";
@@ -91,6 +100,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_REPORT =
"android.bluetooth.input.profile.action.REPORT";
@@ -98,6 +109,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_VIRTUAL_UNPLUG_STATUS =
"android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS";
@@ -105,6 +118,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_IDLE_TIME_CHANGED =
"android.bluetooth.input.profile.action.IDLE_TIME_CHANGED";
@@ -223,7 +238,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
public static final String EXTRA_IDLE_TIME =
"android.bluetooth.BluetoothHidHost.extra.IDLE_TIME";
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST,
"BluetoothHidHost", IBluetoothHidHost.class.getName()) {
@@ -237,8 +253,10 @@ public final class BluetoothHidHost implements BluetoothProfile {
* Create a BluetoothHidHost proxy object for interacting with the local
* Bluetooth Service which handles the InputDevice profile
*/
- /*package*/ BluetoothHidHost(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ /* package */ BluetoothHidHost(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -267,13 +285,17 @@ public final class BluetoothHidHost implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device);
+ return service.connect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -305,13 +327,17 @@ public final class BluetoothHidHost implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -328,13 +354,15 @@ public final class BluetoothHidHost implements BluetoothProfile {
*/
@SystemApi
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -350,12 +378,16 @@ public final class BluetoothHidHost implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -372,7 +404,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
*/
@SystemApi
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
if (device == null) {
@@ -381,7 +414,7 @@ public final class BluetoothHidHost implements BluetoothProfile {
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -402,7 +435,11 @@ public final class BluetoothHidHost implements BluetoothProfile {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -421,7 +458,11 @@ public final class BluetoothHidHost implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -435,7 +476,7 @@ public final class BluetoothHidHost implements BluetoothProfile {
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -455,7 +496,11 @@ public final class BluetoothHidHost implements BluetoothProfile {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -473,7 +518,11 @@ public final class BluetoothHidHost implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
if (device == null) {
@@ -482,7 +531,7 @@ public final class BluetoothHidHost implements BluetoothProfile {
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -503,18 +552,19 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* Initiate virtual unplug for a HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean virtualUnplug(BluetoothDevice device) {
if (DBG) log("virtualUnplug(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.virtualUnplug(device);
+ return service.virtualUnplug(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -529,18 +579,19 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* Send Get_Protocol_Mode command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getProtocolMode(BluetoothDevice device) {
if (VDBG) log("getProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getProtocolMode(device);
+ return service.getProtocolMode(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -553,18 +604,19 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* Send Set_Protocol_Mode command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
if (DBG) log("setProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.setProtocolMode(device, protocolMode);
+ return service.setProtocolMode(device, protocolMode, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -577,8 +629,6 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* Send Get_Report command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @param reportType Report type
* @param reportId Report ID
@@ -586,6 +636,9 @@ public final class BluetoothHidHost implements BluetoothProfile {
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getReport(BluetoothDevice device, byte reportType, byte reportId,
int bufferSize) {
if (VDBG) {
@@ -595,7 +648,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getReport(device, reportType, reportId, bufferSize);
+ return service.getReport(device, reportType, reportId, bufferSize,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -608,20 +662,21 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* Send Set_Report command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @param reportType Report type
* @param report Report receiving buffer size
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setReport(BluetoothDevice device, byte reportType, String report) {
if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.setReport(device, reportType, report);
+ return service.setReport(device, reportType, report, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -634,19 +689,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* Send Send_Data command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @param report Report to send
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendData(BluetoothDevice device, String report) {
if (DBG) log("sendData(" + device + "), report=" + report);
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.sendData(device, report);
+ return service.sendData(device, report, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -659,18 +715,19 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* Send Get_Idle_Time command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getIdleTime(BluetoothDevice device) {
if (DBG) log("getIdletime(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getIdleTime(device);
+ return service.getIdleTime(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -683,19 +740,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
/**
* Send Set_Idle_Time command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @param idleTime Idle time to be set on HID Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.setIdleTime(device, idleTime);
+ return service.setIdleTime(device, idleTime, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java
index 8eb79b248d..95f9229f04 100644
--- a/framework/java/android/bluetooth/BluetoothInputStream.java
+++ b/framework/java/android/bluetooth/BluetoothInputStream.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.SuppressLint;
+
import java.io.IOException;
import java.io.InputStream;
@@ -26,6 +28,7 @@ import java.io.InputStream;
*
* @hide
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
/*package*/ final class BluetoothInputStream extends InputStream {
private BluetoothSocket mSocket;
diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java
index 75fcab9359..c438dd34f8 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudio.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudio.java
@@ -23,6 +23,11 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -65,10 +70,10 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* <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}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED =
"android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
@@ -82,11 +87,11 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* be null if no device is active. </li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED =
"android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED";
@@ -98,7 +103,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
*/
public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio",
IBluetoothLeAudio.class.getName()) {
@@ -112,8 +118,10 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* Create a BluetoothLeAudio proxy object for interacting with the local
* Bluetooth LeAudio service.
*/
- /*package*/ BluetoothLeAudio(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ /* package */ BluetoothLeAudio(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
mCloseGuard = new CloseGuard();
mCloseGuard.open("close");
@@ -122,7 +130,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
/**
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public void close() {
mProfileConnector.disconnect();
}
@@ -131,7 +138,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
return mProfileConnector.getService();
}
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
protected void finalize() {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
@@ -154,13 +160,14 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(@Nullable BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled() && isValidDevice(device)) {
- return service.connect(device);
+ return service.connect(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -193,13 +200,14 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(@Nullable BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled() && isValidDevice(device)) {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -213,12 +221,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled()) {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
@@ -232,13 +243,17 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled()) {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
@@ -252,14 +267,16 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* {@inheritDoc}
*/
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled()
&& isValidDevice(device)) {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
@@ -289,14 +306,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled()
&& ((device == null) || isValidDevice(device))) {
- service.setActiveDevice(device);
+ service.setActiveDevice(device, mAttributionSource);
return true;
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
@@ -314,13 +332,16 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* @hide
*/
@NonNull
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled()) {
- return service.getActiveDevices();
+ return Attributable.setAttributionSource(
+ service.getActiveDevices(mAttributionSource), mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<>();
@@ -336,13 +357,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* @param device LE Audio capable device
* @return group id that this device currently belongs to
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getGroupId(@NonNull BluetoothDevice device) {
if (VDBG) log("getGroupId()");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled()) {
- return service.getGroupId(device);
+ return service.getGroupId(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return GROUP_ID_INVALID;
@@ -364,7 +387,11 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -376,7 +403,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
&& connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
return false;
}
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -397,14 +424,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
try {
final IBluetoothLeAudio service = getService();
if (service != null && mAdapter.isEnabled()
&& isValidDevice(device)) {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java
index d5c1c3e2d6..c21362cd89 100644
--- a/framework/java/android/bluetooth/BluetoothManager.java
+++ b/framework/java/android/bluetooth/BluetoothManager.java
@@ -16,15 +16,21 @@
package android.bluetooth;
-import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresFeature;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.app.ActivityThread;
+import android.app.AppGlobals;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Log;
import java.util.ArrayList;
@@ -56,34 +62,41 @@ public final class BluetoothManager {
private static final String TAG = "BluetoothManager";
private static final boolean DBG = false;
+ private final AttributionSource mAttributionSource;
private final BluetoothAdapter mAdapter;
/**
* @hide
*/
public BluetoothManager(Context context) {
- if (context.getAttributionTag() == null) {
- context = context.getApplicationContext();
- if (context == null) {
- throw new IllegalArgumentException(
- "context not associated with any application (using a mock context?)");
- }
+ mAttributionSource = resolveAttributionSource(context);
+ mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
+ }
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- } else {
- IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- mAdapter = new BluetoothAdapter(IBluetoothManager.Stub.asInterface(b));
- } else {
- Log.e(TAG, "Bluetooth binder is null");
- mAdapter = null;
+ /** {@hide} */
+ public static @NonNull AttributionSource resolveAttributionSource(@Nullable Context context) {
+ AttributionSource res = null;
+ if (context != null) {
+ res = context.getAttributionSource();
+ }
+ if (res == null) {
+ res = ActivityThread.currentAttributionSource();
+ }
+ if (res == null) {
+ int uid = android.os.Process.myUid();
+ if (uid == android.os.Process.ROOT_UID) {
+ uid = android.os.Process.SYSTEM_UID;
+ }
+ try {
+ res = new AttributionSource(uid,
+ AppGlobals.getPackageManager().getPackagesForUid(uid)[0], null);
+ } catch (RemoteException ignored) {
}
}
-
- // Context is not initialized in constructor
- if (mAdapter != null) {
- mAdapter.setContext(context);
+ if (res == null) {
+ throw new IllegalStateException("Failed to resolve AttributionSource");
}
+ return res;
}
/**
@@ -91,6 +104,7 @@ public final class BluetoothManager {
*
* @return the BLUETOOTH Adapter
*/
+ @RequiresNoPermission
public BluetoothAdapter getAdapter() {
return mAdapter;
}
@@ -109,7 +123,9 @@ public final class BluetoothManager {
* {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED},
* {@link BluetoothProfile#STATE_DISCONNECTING}
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device, int profile) {
if (DBG) Log.d(TAG, "getConnectionState()");
@@ -136,27 +152,14 @@ public final class BluetoothManager {
* @param profile GATT or GATT_SERVER
* @return List of devices. The list will be empty on error.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices(int profile) {
if (DBG) Log.d(TAG, "getConnectedDevices");
- if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
- throw new IllegalArgumentException("Profile not supported: " + profile);
- }
-
- List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
-
- try {
- IBluetoothManager managerService = mAdapter.getBluetoothManager();
- IBluetoothGatt iGatt = managerService.getBluetoothGatt();
- if (iGatt == null) return connectedDevices;
-
- connectedDevices = iGatt.getDevicesMatchingConnectionStates(
- new int[]{BluetoothProfile.STATE_CONNECTED});
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
-
- return connectedDevices;
+ return getDevicesMatchingConnectionStates(profile, new int[] {
+ BluetoothProfile.STATE_CONNECTED
+ });
}
/**
@@ -177,7 +180,9 @@ public final class BluetoothManager {
* {@link BluetoothProfile#STATE_DISCONNECTING},
* @return List of devices. The list will be empty on error.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates");
@@ -191,7 +196,9 @@ public final class BluetoothManager {
IBluetoothManager managerService = mAdapter.getBluetoothManager();
IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) return devices;
- devices = iGatt.getDevicesMatchingConnectionStates(states);
+ devices = Attributable.setAttributionSource(
+ iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -210,6 +217,8 @@ public final class BluetoothManager {
* @param callback GATT server callback handler that will receive asynchronous callbacks.
* @return BluetoothGattServer instance
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback) {
@@ -229,6 +238,8 @@ public final class BluetoothManager {
* @return BluetoothGattServer instance
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback, boolean eatt_support) {
return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support));
@@ -249,6 +260,8 @@ public final class BluetoothManager {
* @return BluetoothGattServer instance
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback, int transport) {
return (openGattServer(context, callback, transport, false));
@@ -270,6 +283,8 @@ public final class BluetoothManager {
* @return BluetoothGattServer instance
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback, int transport, boolean eatt_support) {
if (context == null || callback == null) {
@@ -286,7 +301,8 @@ public final class BluetoothManager {
Log.e(TAG, "Fail to get GATT Server connection");
return null;
}
- BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt, transport);
+ BluetoothGattServer mGattServer =
+ new BluetoothGattServer(iGatt, transport, mAdapter);
Boolean regStatus = mGattServer.registerCallback(callback, eatt_support);
return regStatus ? mGattServer : null;
} catch (RemoteException e) {
diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java
index 3554995400..86796519df 100644
--- a/framework/java/android/bluetooth/BluetoothMap.java
+++ b/framework/java/android/bluetooth/BluetoothMap.java
@@ -18,10 +18,16 @@ package android.bluetooth;
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
@@ -51,6 +57,9 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
/** @hide */
@SuppressLint("ActionValue")
@SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
@@ -72,7 +81,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
*/
public static final int RESULT_CANCELED = 2;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.MAP,
"BluetoothMap", IBluetoothMap.class.getName()) {
@@ -85,15 +95,16 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
/**
* Create a BluetoothMap proxy object.
*/
- /*package*/ BluetoothMap(Context context, ServiceListener listener) {
+ /* package */ BluetoothMap(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
mCloseGuard = new CloseGuard();
mCloseGuard.open("close");
}
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
protected void finalize() {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
@@ -110,7 +121,6 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public void close() {
if (VDBG) log("close()");
mProfileConnector.disconnect();
@@ -128,12 +138,14 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getState() {
if (VDBG) log("getState()");
final IBluetoothMap service = getService();
if (service != null) {
try {
- return service.getState();
+ return service.getState(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -152,12 +164,15 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothMap service = getService();
if (service != null) {
try {
- return service.getClient();
+ return Attributable.setAttributionSource(
+ service.getClient(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -175,12 +190,14 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothMap service = getService();
if (service != null) {
try {
- return service.isConnected(device);
+ return service.isConnected(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -197,6 +214,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
*
* @hide
*/
+ @RequiresNoPermission
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
return false;
@@ -211,12 +229,14 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -257,13 +277,18 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothMap service = getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -280,12 +305,16 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothMap service = getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -302,12 +331,14 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -328,7 +359,11 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -347,7 +382,11 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -358,7 +397,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -378,7 +417,11 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -396,13 +439,17 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -417,13 +464,10 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
}
private boolean isEnabled() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
- log("Bluetooth is Not enabled");
- return false;
+ return mAdapter.isEnabled();
}
+
private static boolean isValidDevice(BluetoothDevice device) {
return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
-
}
diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java
index 0312a2190a..042b58669a 100644
--- a/framework/java/android/bluetooth/BluetoothMapClient.java
+++ b/framework/java/android/bluetooth/BluetoothMapClient.java
@@ -20,9 +20,14 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.app.PendingIntent;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.net.Uri;
import android.os.Binder;
@@ -48,16 +53,27 @@ public final class BluetoothMapClient implements BluetoothProfile {
private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
/** @hide */
+ @RequiresPermission(android.Manifest.permission.RECEIVE_SMS)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MESSAGE_RECEIVED =
"android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
/* Actions to be used for pending intents */
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
"android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
/** @hide */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
"android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
@@ -66,6 +82,9 @@ public final class BluetoothMapClient implements BluetoothProfile {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MESSAGE_READ_STATUS_CHANGED =
"android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED";
@@ -74,6 +93,9 @@ public final class BluetoothMapClient implements BluetoothProfile {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED =
"android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED";
@@ -152,7 +174,8 @@ public final class BluetoothMapClient implements BluetoothProfile {
/** @hide */
public static final int DELETED = 3;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
"BluetoothMapClient", IBluetoothMapClient.class.getName()) {
@@ -165,9 +188,11 @@ public final class BluetoothMapClient implements BluetoothProfile {
/**
* Create a BluetoothMapClient proxy object.
*/
- /*package*/ BluetoothMapClient(Context context, ServiceListener listener) {
+ /* package */ BluetoothMapClient(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -192,12 +217,14 @@ public final class BluetoothMapClient implements BluetoothProfile {
* currently connected to the Map service.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
final IBluetoothMapClient service = getService();
if (service != null) {
try {
- return service.isConnected(device);
+ return service.isConnected(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -214,13 +241,17 @@ public final class BluetoothMapClient implements BluetoothProfile {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
final IBluetoothMapClient service = getService();
if (service != null) {
try {
- return service.connect(device);
+ return service.connect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -239,13 +270,17 @@ public final class BluetoothMapClient implements BluetoothProfile {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean disconnect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "disconnect(" + device + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
@@ -261,12 +296,15 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) Log.d(TAG, "getConnectedDevices()");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<>();
@@ -283,12 +321,16 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<>();
@@ -305,12 +347,14 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -331,7 +375,11 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -349,7 +397,11 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -360,7 +412,7 @@ public final class BluetoothMapClient implements BluetoothProfile {
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -380,7 +432,11 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -397,13 +453,17 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -427,7 +487,11 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.SEND_SMS)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.SEND_SMS,
+ })
public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts,
@NonNull String message, @Nullable PendingIntent sentIntent,
@Nullable PendingIntent deliveredIntent) {
@@ -436,7 +500,7 @@ public final class BluetoothMapClient implements BluetoothProfile {
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]),
- message, sentIntent, deliveredIntent);
+ message, sentIntent, deliveredIntent, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -459,13 +523,19 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.SEND_SMS,
+ })
public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
PendingIntent sentIntent, PendingIntent deliveredIntent) {
if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
+ return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -481,12 +551,17 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @return true if the message is enqueued, false on error
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.READ_SMS,
+ })
public boolean getUnreadMessages(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getUnreadMessages(device);
+ return service.getUnreadMessages(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -503,11 +578,14 @@ public final class BluetoothMapClient implements BluetoothProfile {
* MapSupportedFeatures field is set. False is returned otherwise.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isUploadingSupported(BluetoothDevice device) {
final IBluetoothMapClient service = getService();
try {
return (service != null && isEnabled() && isValidDevice(device))
- && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
+ && ((service.getSupportedFeatures(device, mAttributionSource)
+ & UPLOADING_FEATURE_BITMASK) > 0);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage());
}
@@ -530,14 +608,18 @@ public final class BluetoothMapClient implements BluetoothProfile {
* @return <code>true</code> if request has been sent, <code>false</code> on error
* @hide
*/
- @RequiresPermission(Manifest.permission.READ_SMS)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.READ_SMS,
+ })
public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device) && handle != null &&
(status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) {
try {
- return service.setMessageStatus(device, handle, status);
+ return service.setMessageStatus(device, handle, status, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -547,14 +629,10 @@ public final class BluetoothMapClient implements BluetoothProfile {
}
private boolean isEnabled() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
- if (DBG) Log.d(TAG, "Bluetooth is Not enabled");
- return false;
+ return mAdapter.isEnabled();
}
private static boolean isValidDevice(BluetoothDevice device) {
return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
-
}
diff --git a/framework/java/android/bluetooth/BluetoothMasInstance.java b/framework/java/android/bluetooth/BluetoothMasInstance.java
index b64d0492fa..eeaf085451 100644
--- a/framework/java/android/bluetooth/BluetoothMasInstance.java
+++ b/framework/java/android/bluetooth/BluetoothMasInstance.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,7 +35,7 @@ public final class BluetoothMasInstance implements Parcelable {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothMasInstance) {
return mId == ((BluetoothMasInstance) o).mId;
}
diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java
index a0aa2dee9d..ac2b3edb0e 100644
--- a/framework/java/android/bluetooth/BluetoothOutputStream.java
+++ b/framework/java/android/bluetooth/BluetoothOutputStream.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.SuppressLint;
+
import java.io.IOException;
import java.io.OutputStream;
@@ -26,6 +28,7 @@ import java.io.OutputStream;
*
* @hide
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
/*package*/ final class BluetoothOutputStream extends OutputStream {
private BluetoothSocket mSocket;
diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java
index f06dad8232..577be3d2ae 100644
--- a/framework/java/android/bluetooth/BluetoothPan.java
+++ b/framework/java/android/bluetooth/BluetoothPan.java
@@ -24,7 +24,11 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
@@ -74,10 +78,11 @@ public final class BluetoothPan implements BluetoothProfile {
*
* <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or
* {@link #LOCAL_PANU_ROLE}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
@SuppressLint("ActionValue")
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
@@ -102,9 +107,8 @@ public final class BluetoothPan implements BluetoothProfile {
*
* <p> {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or
* {@link #TETHERING_STATE_ON}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_TETHERING_STATE_CHANGED =
"android.bluetooth.action.TETHERING_STATE_CHANGED";
@@ -179,7 +183,8 @@ public final class BluetoothPan implements BluetoothProfile {
private final Context mContext;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.PAN,
"BluetoothPan", IBluetoothPan.class.getName()) {
@@ -197,8 +202,10 @@ public final class BluetoothPan implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage
- /*package*/ BluetoothPan(Context context, ServiceListener listener) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ /* package */ BluetoothPan(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mContext = context;
mProfileConnector.connect(context, listener);
}
@@ -236,12 +243,17 @@ public final class BluetoothPan implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothPan service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device);
+ return service.connect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -274,12 +286,14 @@ public final class BluetoothPan implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothPan service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
@@ -302,7 +316,11 @@ public final class BluetoothPan implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -314,7 +332,7 @@ public final class BluetoothPan implements BluetoothProfile {
&& connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
return false;
}
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -330,13 +348,18 @@ public final class BluetoothPan implements BluetoothProfile {
*/
@SystemApi
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothPan service = getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -351,13 +374,20 @@ public final class BluetoothPan implements BluetoothProfile {
* @hide
*/
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothPan service = getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -373,13 +403,17 @@ public final class BluetoothPan implements BluetoothProfile {
*/
@SystemApi
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothPan service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -396,14 +430,19 @@ public final class BluetoothPan implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ })
public void setBluetoothTethering(boolean value) {
String pkgName = mContext.getOpPackageName();
if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
final IBluetoothPan service = getService();
if (service != null && isEnabled()) {
try {
- service.setBluetoothTethering(value, pkgName, null);
+ service.setBluetoothTethering(value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
@@ -417,13 +456,14 @@ public final class BluetoothPan implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
final IBluetoothPan service = getService();
if (service != null && isEnabled()) {
try {
- return service.isTetheringOn();
+ return service.isTetheringOn(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java
index 6e5c45f3d1..c000e56e7b 100644
--- a/framework/java/android/bluetooth/BluetoothPbap.java
+++ b/framework/java/android/bluetooth/BluetoothPbap.java
@@ -22,11 +22,16 @@ import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
@@ -82,14 +87,13 @@ public class BluetoothPbap implements BluetoothProfile {
* can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
* {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
* {@link BluetoothProfile#STATE_DISCONNECTING}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*
* @hide
*/
@SuppressLint("ActionValue")
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
@@ -97,7 +101,8 @@ public class BluetoothPbap implements BluetoothProfile {
private volatile IBluetoothPbap mService;
private final Context mContext;
private ServiceListener mServiceListener;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
/** @hide */
public static final int RESULT_FAILURE = 0;
@@ -110,6 +115,7 @@ public class BluetoothPbap implements BluetoothProfile {
*/
public static final int RESULT_CANCELED = 2;
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
public void onBluetoothStateChange(boolean up) {
@@ -127,10 +133,21 @@ public class BluetoothPbap implements BluetoothProfile {
*
* @hide
*/
- public BluetoothPbap(Context context, ServiceListener l) {
+ public BluetoothPbap(Context context, ServiceListener l, BluetoothAdapter adapter) {
mContext = context;
mServiceListener = l;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
+
+ // Preserve legacy compatibility where apps were depending on
+ // registerStateChangeCallback() performing a permissions check which
+ // has been relaxed in modern platform versions
+ if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+ && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Need BLUETOOTH permission");
+ }
+
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
try {
@@ -142,6 +159,7 @@ public class BluetoothPbap implements BluetoothProfile {
doBind();
}
+ @SuppressLint("AndroidFrameworkRequiresPermission")
boolean doBind() {
synchronized (mConnection) {
try {
@@ -216,6 +234,8 @@ public class BluetoothPbap implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
log("getConnectedDevices()");
final IBluetoothPbap service = mService;
@@ -224,7 +244,8 @@ public class BluetoothPbap implements BluetoothProfile {
return new ArrayList<BluetoothDevice>();
}
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -238,13 +259,17 @@ public class BluetoothPbap implements BluetoothProfile {
*/
@SystemApi
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
log("getConnectionState: device=" + device);
try {
final IBluetoothPbap service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
}
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
@@ -262,6 +287,8 @@ public class BluetoothPbap implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
final IBluetoothPbap service = mService;
@@ -270,7 +297,9 @@ public class BluetoothPbap implements BluetoothProfile {
return new ArrayList<BluetoothDevice>();
}
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -294,7 +323,11 @@ public class BluetoothPbap implements BluetoothProfile {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -306,7 +339,7 @@ public class BluetoothPbap implements BluetoothProfile {
&& connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
return false;
}
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
@@ -324,6 +357,8 @@ public class BluetoothPbap implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
log("disconnect()");
final IBluetoothPbap service = mService;
@@ -332,7 +367,7 @@ public class BluetoothPbap implements BluetoothProfile {
return false;
}
try {
- service.disconnect(device);
+ service.disconnect(device, mAttributionSource);
return true;
} catch (RemoteException e) {
Log.e(TAG, e.toString());
@@ -340,6 +375,7 @@ public class BluetoothPbap implements BluetoothProfile {
return false;
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
log("Proxy object connected");
diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java
index f356da18fc..c7dd6bd9af 100644
--- a/framework/java/android/bluetooth/BluetoothPbapClient.java
+++ b/framework/java/android/bluetooth/BluetoothPbapClient.java
@@ -19,6 +19,12 @@ package android.bluetooth;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -39,6 +45,9 @@ public final class BluetoothPbapClient implements BluetoothProfile {
private static final boolean DBG = false;
private static final boolean VDBG = false;
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
@@ -50,7 +59,8 @@ public final class BluetoothPbapClient implements BluetoothProfile {
/** Connection canceled before completion. */
public static final int RESULT_CANCELED = 2;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT,
"BluetoothPbapClient", IBluetoothPbapClient.class.getName()) {
@@ -63,11 +73,12 @@ public final class BluetoothPbapClient implements BluetoothProfile {
/**
* Create a BluetoothPbapClient proxy object.
*/
- BluetoothPbapClient(Context context, ServiceListener listener) {
+ BluetoothPbapClient(Context context, ServiceListener listener, BluetoothAdapter adapter) {
if (DBG) {
Log.d(TAG, "Create BluetoothPbapClient proxy object");
}
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -104,7 +115,11 @@ public final class BluetoothPbapClient implements BluetoothProfile {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) {
log("connect(" + device + ") for PBAP Client.");
@@ -112,7 +127,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device);
+ return service.connect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -132,7 +147,11 @@ public final class BluetoothPbapClient implements BluetoothProfile {
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean disconnect(BluetoothDevice device) {
if (DBG) {
log("disconnect(" + device + ")" + new Exception());
@@ -140,7 +159,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- service.disconnect(device);
+ service.disconnect(device, mAttributionSource);
return true;
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
@@ -160,6 +179,8 @@ public final class BluetoothPbapClient implements BluetoothProfile {
* @return list of connected devices
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) {
log("getConnectedDevices()");
@@ -167,7 +188,8 @@ public final class BluetoothPbapClient implements BluetoothProfile {
final IBluetoothPbapClient service = getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -185,6 +207,8 @@ public final class BluetoothPbapClient implements BluetoothProfile {
* @return list of matching devices
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) {
log("getDevicesMatchingStates()");
@@ -192,7 +216,9 @@ public final class BluetoothPbapClient implements BluetoothProfile {
final IBluetoothPbapClient service = getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -210,6 +236,8 @@ public final class BluetoothPbapClient implements BluetoothProfile {
* @return device connection state
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) {
log("getConnectionState(" + device + ")");
@@ -217,7 +245,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -234,12 +262,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
}
private boolean isEnabled() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
- return true;
- }
- log("Bluetooth is Not enabled");
- return false;
+ return mAdapter.isEnabled();
}
private static boolean isValidDevice(BluetoothDevice device) {
@@ -257,7 +280,11 @@ public final class BluetoothPbapClient implements BluetoothProfile {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -275,7 +302,11 @@ public final class BluetoothPbapClient implements BluetoothProfile {
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) {
@@ -288,7 +319,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -310,7 +341,11 @@ public final class BluetoothPbapClient implements BluetoothProfile {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -327,7 +362,11 @@ public final class BluetoothPbapClient implements BluetoothProfile {
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) {
log("getConnectionPolicy(" + device + ")");
@@ -335,7 +374,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java
index b76d6b8691..83a272fce3 100644
--- a/framework/java/android/bluetooth/BluetoothProfile.java
+++ b/framework/java/android/bluetooth/BluetoothProfile.java
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-
package android.bluetooth;
-import android.Manifest;
import android.annotation.IntDef;
-import android.annotation.RequiresPermission;
+import android.annotation.RequiresNoPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -307,7 +305,6 @@ public interface BluetoothProfile {
*
* @return List of devices. The list will be empty on error.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
public List<BluetoothDevice> getConnectedDevices();
/**
@@ -321,7 +318,6 @@ public interface BluetoothProfile {
* #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
* @return List of devices. The list will be empty on error.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states);
/**
@@ -331,7 +327,6 @@ public interface BluetoothProfile {
* @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link
* #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
@BtProfileState int getConnectionState(BluetoothDevice device);
/**
@@ -346,6 +341,7 @@ public interface BluetoothProfile {
* @param profile - One of {@link #HEADSET} or {@link #A2DP}
* @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp}
*/
+ @RequiresNoPermission
public void onServiceConnected(int profile, BluetoothProfile proxy);
/**
@@ -354,6 +350,7 @@ public interface BluetoothProfile {
*
* @param profile - One of {@link #HEADSET} or {@link #A2DP}
*/
+ @RequiresNoPermission
public void onServiceDisconnected(int profile);
}
diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java
index 863fd3698c..a254291f57 100644
--- a/framework/java/android/bluetooth/BluetoothProfileConnector.java
+++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java
@@ -16,13 +16,17 @@
package android.bluetooth;
+import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.CloseGuard;
import android.util.Log;
/**
@@ -31,7 +35,9 @@ import android.util.Log;
* @param <T> The Bluetooth profile interface for this connection.
* @hide
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public abstract class BluetoothProfileConnector<T> {
+ private final CloseGuard mCloseGuard = new CloseGuard();
private final int mProfileId;
private BluetoothProfile.ServiceListener mServiceListener;
private final BluetoothProfile mProfileProxy;
@@ -78,10 +84,19 @@ public abstract class BluetoothProfileConnector<T> {
mServiceName = serviceName;
}
+ /** {@hide} */
+ @Override
+ public void finalize() {
+ mCloseGuard.warnIfOpen();
+ doUnbind();
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
private boolean doBind() {
synchronized (mConnection) {
if (mService == null) {
logDebug("Binding service...");
+ mCloseGuard.open("doUnbind");
try {
Intent intent = new Intent(mServiceName);
ComponentName comp = intent.resolveSystemService(
@@ -105,6 +120,7 @@ public abstract class BluetoothProfileConnector<T> {
synchronized (mConnection) {
if (mService != null) {
logDebug("Unbinding service...");
+ mCloseGuard.close();
try {
mContext.unbindService(mConnection);
} catch (IllegalArgumentException ie) {
@@ -120,6 +136,16 @@ public abstract class BluetoothProfileConnector<T> {
mContext = context;
mServiceListener = listener;
IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
+
+ // Preserve legacy compatibility where apps were depending on
+ // registerStateChangeCallback() performing a permissions check which
+ // has been relaxed in modern platform versions
+ if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+ && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Need BLUETOOTH permission");
+ }
+
if (mgr != null) {
try {
mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java
index 0d70dbdd84..fda19ed6d0 100644
--- a/framework/java/android/bluetooth/BluetoothSap.java
+++ b/framework/java/android/bluetooth/BluetoothSap.java
@@ -17,8 +17,15 @@
package android.bluetooth;
import android.Manifest;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
@@ -61,11 +68,12 @@ public final class BluetoothSap implements BluetoothProfile {
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
@@ -90,7 +98,8 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public static final int RESULT_CANCELED = 2;
- private BluetoothAdapter mAdapter;
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.SAP,
"BluetoothSap", IBluetoothSap.class.getName()) {
@@ -103,9 +112,11 @@ public final class BluetoothSap implements BluetoothProfile {
/**
* Create a BluetoothSap proxy object.
*/
- /*package*/ BluetoothSap(Context context, ServiceListener listener) {
+ /* package */ BluetoothSap(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
}
@@ -140,12 +151,14 @@ public final class BluetoothSap implements BluetoothProfile {
* connected to the Sap service.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getState() {
if (VDBG) log("getState()");
final IBluetoothSap service = getService();
if (service != null) {
try {
- return service.getState();
+ return service.getState(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -163,12 +176,15 @@ public final class BluetoothSap implements BluetoothProfile {
* this proxy object is not connected to the Sap service.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothSap service = getService();
if (service != null) {
try {
- return service.getClient();
+ return Attributable.setAttributionSource(
+ service.getClient(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -186,12 +202,14 @@ public final class BluetoothSap implements BluetoothProfile {
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothSap service = getService();
if (service != null) {
try {
- return service.isConnected(device);
+ return service.isConnected(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -208,6 +226,7 @@ public final class BluetoothSap implements BluetoothProfile {
*
* @hide
*/
+ @RequiresNoPermission
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
return false;
@@ -221,12 +240,14 @@ public final class BluetoothSap implements BluetoothProfile {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
+ return service.disconnect(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -242,12 +263,15 @@ public final class BluetoothSap implements BluetoothProfile {
* @return list of connected devices
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothSap service = getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -263,12 +287,16 @@ public final class BluetoothSap implements BluetoothProfile {
* @return list of matching devices
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothSap service = getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -284,12 +312,14 @@ public final class BluetoothSap implements BluetoothProfile {
* @return device connection state
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -310,7 +340,11 @@ public final class BluetoothSap implements BluetoothProfile {
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -328,7 +362,11 @@ public final class BluetoothSap implements BluetoothProfile {
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -339,7 +377,7 @@ public final class BluetoothSap implements BluetoothProfile {
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -359,7 +397,11 @@ public final class BluetoothSap implements BluetoothProfile {
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -376,13 +418,17 @@ public final class BluetoothSap implements BluetoothProfile {
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -397,17 +443,10 @@ public final class BluetoothSap implements BluetoothProfile {
}
private boolean isEnabled() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
- if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
- return true;
- }
- log("Bluetooth is Not enabled");
- return false;
+ return mAdapter.isEnabled();
}
private static boolean isValidDevice(BluetoothDevice device) {
return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
-
}
diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java
index 5c1bcaf313..bb4e35483f 100644
--- a/framework/java/android/bluetooth/BluetoothServerSocket.java
+++ b/framework/java/android/bluetooth/BluetoothServerSocket.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.ParcelUuid;
@@ -62,9 +63,6 @@ import java.io.IOException;
* safe. In particular, {@link #close} will always immediately abort ongoing
* operations and close the server socket.
*
- * <p class="note"><strong>Note:</strong>
- * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about using Bluetooth, read the
@@ -73,6 +71,7 @@ import java.io.IOException;
*
* {@see BluetoothSocket}
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class BluetoothServerSocket implements Closeable {
private static final String TAG = "BluetoothServerSocket";
diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java
index 65381dbb23..bb409d5360 100644
--- a/framework/java/android/bluetooth/BluetoothSocket.java
+++ b/framework/java/android/bluetooth/BluetoothSocket.java
@@ -16,6 +16,10 @@
package android.bluetooth;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.LocalSocket;
import android.os.Build;
@@ -70,9 +74,6 @@ import java.util.UUID;
* safe. In particular, {@link #close} will always immediately abort ongoing
* operations and close the socket.
*
- * <p class="note"><strong>Note:</strong>
- * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about using Bluetooth, read the
@@ -326,6 +327,7 @@ public final class BluetoothSocket implements Closeable {
*
* @return remote device
*/
+ @RequiresNoPermission
public BluetoothDevice getRemoteDevice() {
return mDevice;
}
@@ -338,6 +340,7 @@ public final class BluetoothSocket implements Closeable {
*
* @return InputStream
*/
+ @RequiresNoPermission
public InputStream getInputStream() throws IOException {
return mInputStream;
}
@@ -350,6 +353,7 @@ public final class BluetoothSocket implements Closeable {
*
* @return OutputStream
*/
+ @RequiresNoPermission
public OutputStream getOutputStream() throws IOException {
return mOutputStream;
}
@@ -360,6 +364,7 @@ public final class BluetoothSocket implements Closeable {
*
* @return true if connected false if not connected
*/
+ @RequiresNoPermission
public boolean isConnected() {
return mSocketState == SocketState.CONNECTED;
}
@@ -386,6 +391,8 @@ public final class BluetoothSocket implements Closeable {
*
* @throws IOException on error, for example connection failure
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void connect() throws IOException {
if (mDevice == null) throw new IOException("Connect is called on null device");
@@ -427,6 +434,7 @@ public final class BluetoothSocket implements Closeable {
* Currently returns unix errno instead of throwing IOException,
* so that BluetoothAdapter can check the error code for EADDRINUSE
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ int bindListen() {
int ret;
if (mSocketState == SocketState.CLOSED) return EBADFD;
@@ -635,6 +643,7 @@ public final class BluetoothSocket implements Closeable {
*
* @return the maximum supported Transmit packet size for the underlying transport.
*/
+ @RequiresNoPermission
public int getMaxTransmitPacketSize() {
return mMaxTxPacketSize;
}
@@ -647,6 +656,7 @@ public final class BluetoothSocket implements Closeable {
*
* @return the maximum supported Receive packet size for the underlying transport.
*/
+ @RequiresNoPermission
public int getMaxReceivePacketSize() {
return mMaxRxPacketSize;
}
@@ -656,6 +666,7 @@ public final class BluetoothSocket implements Closeable {
*
* @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
*/
+ @RequiresNoPermission
public int getConnectionType() {
if (mType == TYPE_L2CAP_LE) {
// Treat the LE CoC to be the same type as L2CAP.
@@ -672,6 +683,7 @@ public final class BluetoothSocket implements Closeable {
* generate SPP SDP record.
* @hide
*/
+ @RequiresNoPermission
public void setExcludeSdp(boolean excludeSdp) {
mExcludeSdp = excludeSdp;
}
@@ -682,6 +694,8 @@ public final class BluetoothSocket implements Closeable {
* connection. This function is currently used for testing only.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void requestMaximumTxDataLength() throws IOException {
if (mDevice == null) {
throw new IOException("requestMaximumTxDataLength is called on null device");
diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java
new file mode 100644
index 0000000000..31bb0f68c6
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+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.
+ * API-specific return values start at 1000. The exception to this is the "other" error code which
+ * occupies the max integer value.
+ */
+public final class BluetoothStatusCodes {
+
+ private BluetoothStatusCodes() {}
+
+ /**
+ * Indicates that the API call was successful
+ */
+ public static final int SUCCESS = 0;
+
+ /**
+ * 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
+ */
+ public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2;
+
+ /**
+ * 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
+ *
+ * @hide
+ */
+ public static final int ERROR_DEVICE_NOT_CONNECTED = 4;
+
+ /**
+ * Error code indicating that the caller does not have the
+ * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission
+ *
+ * @hide
+ */
+ public static final int ERROR_MISSING_BLUETOOTH_ADVERTISE_PERMISSION = 5;
+
+ /**
+ * Error code indicating that the caller does not have the
+ * {@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
+ *
+ * @hide
+ */
+ public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7;
+
+ /**
+ * If another application has already requested {@link OobData} then another fetch will be
+ * disallowed until the callback is removed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000;
+
+ /**
+ * Indicates that the ACL disconnected due to an explicit request from the local device.
+ * <p>
+ * Example cause: This is a normal disconnect reason, e.g., user/app initiates
+ * disconnection.
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_LOCAL_REQUEST = 1100;
+
+ /**
+ * Indicates that the ACL disconnected due to an explicit request from the remote device.
+ * <p>
+ * Example cause: This is a normal disconnect reason, e.g., user/app initiates
+ * disconnection.
+ * <p>
+ * Example solution: The app can also prompt the user to check their remote device.
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_REMOTE_REQUEST = 1101;
+
+ /**
+ * Generic disconnect reason indicating the ACL disconnected due to an error on the local
+ * device.
+ * <p>
+ * Example solution: Prompt the user to check their local device (e.g., phone, car
+ * headunit).
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_LOCAL = 1102;
+
+ /**
+ * Generic disconnect reason indicating the ACL disconnected due to an error on the remote
+ * device.
+ * <p>
+ * Example solution: Prompt the user to check their remote device (e.g., headset, car
+ * headunit, watch).
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_REMOTE = 1103;
+
+ /**
+ * Indicates that the ACL disconnected due to a timeout.
+ * <p>
+ * Example cause: remote device might be out of range.
+ * <p>
+ * Example solution: Prompt user to verify their remote device is on or in
+ * connection/pairing mode.
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_TIMEOUT = 1104;
+
+ /**
+ * Indicates that the ACL disconnected due to link key issues.
+ * <p>
+ * Example cause: Devices are either unpaired or remote device is refusing our pairing
+ * request.
+ * <p>
+ * Example solution: Prompt user to unpair and pair again.
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_SECURITY = 1105;
+
+ /**
+ * Indicates that the ACL disconnected due to the local device's system policy.
+ * <p>
+ * Example cause: privacy policy, power management policy, permissions, etc.
+ * <p>
+ * Example solution: Prompt the user to check settings, or check with their system
+ * administrator (e.g. some corp-managed devices do not allow OPP connection).
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_SYSTEM_POLICY = 1106;
+
+ /**
+ * Indicates that the ACL disconnected due to resource constraints, either on the local
+ * device or the remote device.
+ * <p>
+ * Example cause: controller is busy, memory limit reached, maximum number of connections
+ * reached.
+ * <p>
+ * Example solution: The app should wait and try again. If still failing, prompt the user
+ * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device.
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED = 1107;
+
+ /**
+ * Indicates that the ACL disconnected because another ACL connection already exists.
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS = 1108;
+
+ /**
+ * Indicates that the ACL disconnected due to incorrect parameters passed in from the app.
+ * <p>
+ * Example solution: Change parameters and try again. If error persists, the app can report
+ * telemetry and/or log the error in a bugreport.
+ *
+ * @hide
+ */
+ public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109;
+
+ /**
+ * Indicates that an unknown error has occurred has occurred.
+ */
+ public static final int ERROR_UNKNOWN = Integer.MAX_VALUE;
+}
diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java
index d82cf19e88..bc3754a2fc 100644
--- a/framework/java/android/bluetooth/BluetoothUuid.java
+++ b/framework/java/android/bluetooth/BluetoothUuid.java
@@ -18,6 +18,7 @@ package android.bluetooth;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.ParcelUuid;
@@ -34,6 +35,7 @@ import java.util.UUID;
* @hide
*/
@SystemApi
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class BluetoothUuid {
/* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
diff --git a/framework/java/android/bluetooth/BluetoothVolumeControl.java b/framework/java/android/bluetooth/BluetoothVolumeControl.java
index e5092833d7..678c11a59f 100644
--- a/framework/java/android/bluetooth/BluetoothVolumeControl.java
+++ b/framework/java/android/bluetooth/BluetoothVolumeControl.java
@@ -26,7 +26,10 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -72,12 +75,14 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
*/
@SystemApi
@SuppressLint("ActionValue")
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
private BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
private final BluetoothProfileConnector<IBluetoothVolumeControl> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG,
IBluetoothVolumeControl.class.getName()) {
@@ -94,6 +99,7 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
/*package*/ BluetoothVolumeControl(Context context, ServiceListener listener,
BluetoothAdapter adapter) {
mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
mProfileConnector.connect(context, listener);
mCloseGuard = new CloseGuard();
mCloseGuard.open("close");
@@ -122,13 +128,18 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothVolumeControl service = getService();
if (service != null && isEnabled()) {
try {
- return service.getConnectedDevices();
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -145,13 +156,16 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothVolumeControl service = getService();
if (service != null && isEnabled()) {
try {
- return service.getDevicesMatchingConnectionStates(states);
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
@@ -168,13 +182,14 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothVolumeControl service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
+ return service.getConnectionState(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
@@ -192,7 +207,11 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public void setVolume(@Nullable BluetoothDevice device,
@IntRange(from = 0, to = 255) int volume) {
if (DBG)
@@ -200,7 +219,7 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
final IBluetoothVolumeControl service = getService();
try {
if (service != null && isEnabled()) {
- service.setVolume(device, volume);
+ service.setVolume(device, volume, mAttributionSource);
return;
}
if (service == null)
@@ -223,7 +242,11 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -234,7 +257,7 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
return false;
}
try {
- return service.setConnectionPolicy(device, connectionPolicy);
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -256,13 +279,17 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothVolumeControl service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device);
+ return service.getConnectionPolicy(device, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
diff --git a/framework/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java b/framework/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java
new file mode 100644
index 0000000000..c508c2c9ca
--- /dev/null
+++ b/framework/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
+ * this requires the {@link Manifest.permission#BLUETOOTH_ADVERTISE}
+ * permission which can be gained with
+ * {@link android.app.Activity#requestPermissions(String[], int)}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresBluetoothAdvertisePermission {
+}
diff --git a/framework/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java b/framework/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java
new file mode 100644
index 0000000000..e159eaafe2
--- /dev/null
+++ b/framework/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
+ * this requires the {@link Manifest.permission#BLUETOOTH_CONNECT}
+ * permission which can be gained with
+ * {@link android.app.Activity#requestPermissions(String[], int)}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresBluetoothConnectPermission {
+}
diff --git a/framework/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java b/framework/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java
new file mode 100644
index 0000000000..2bb3204139
--- /dev/null
+++ b/framework/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc In addition, this requires either the
+ * {@link Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission or a strong assertion that you will never derive the
+ * physical location of the device. You can make this assertion by
+ * declaring {@code usesPermissionFlags="neverForLocation"} on the
+ * relevant {@code <uses-permission>} manifest tag, but it may
+ * restrict the types of Bluetooth devices you can interact with.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresBluetoothLocationPermission {
+}
diff --git a/framework/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java b/framework/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java
new file mode 100644
index 0000000000..800ff39933
--- /dev/null
+++ b/framework/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
+ * this requires the {@link Manifest.permission#BLUETOOTH_SCAN}
+ * permission which can be gained with
+ * {@link android.app.Activity#requestPermissions(String[], int)}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresBluetoothScanPermission {
+}
diff --git a/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java b/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java
new file mode 100644
index 0000000000..9adf695cde
--- /dev/null
+++ b/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this
+ * requires the {@link Manifest.permission#BLUETOOTH_ADMIN}
+ * permission which can be gained with a simple
+ * {@code <uses-permission>} manifest tag.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresLegacyBluetoothAdminPermission {
+}
diff --git a/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java b/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java
new file mode 100644
index 0000000000..79621c366f
--- /dev/null
+++ b/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this
+ * requires the {@link Manifest.permission#BLUETOOTH} permission
+ * which can be gained with a simple {@code <uses-permission>}
+ * manifest tag.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresLegacyBluetoothPermission {
+}
diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java
index fa7ac2b27c..cec658049c 100644
--- a/framework/java/android/bluetooth/le/AdvertiseData.java
+++ b/framework/java/android/bluetooth/le/AdvertiseData.java
@@ -124,7 +124,7 @@ public final class AdvertiseData implements Parcelable {
* @hide
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java
index 1df35e1e38..bbdb6953af 100644
--- a/framework/java/android/bluetooth/le/AdvertisingSet.java
+++ b/framework/java/android/bluetooth/le/AdvertisingSet.java
@@ -16,9 +16,14 @@
package android.bluetooth.le;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
+import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.content.AttributionSource;
import android.os.RemoteException;
import android.util.Log;
@@ -27,9 +32,6 @@ import android.util.Log;
* <p>
* To get an instance of {@link AdvertisingSet}, call the
* {@link BluetoothLeAdvertiser#startAdvertisingSet} method.
- * <p>
- * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
*
* @see AdvertiseData
*/
@@ -38,11 +40,12 @@ public final class AdvertisingSet {
private final IBluetoothGatt mGatt;
private int mAdvertiserId;
+ private AttributionSource mAttributionSource;
- /* package */ AdvertisingSet(int advertiserId,
- IBluetoothManager bluetoothManager) {
+ /* package */ AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager,
+ AttributionSource attributionSource) {
mAdvertiserId = advertiserId;
-
+ mAttributionSource = attributionSource;
try {
mGatt = bluetoothManager.getBluetoothGatt();
} catch (RemoteException e) {
@@ -58,8 +61,6 @@ public final class AdvertisingSet {
/**
* Enables Advertising. This method returns immediately, the operation status is
* delivered through {@code callback.onAdvertisingEnabled()}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param enable whether the advertising should be enabled (true), or disabled (false)
* @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535
@@ -68,11 +69,14 @@ public final class AdvertisingSet {
* controller shall attempt to send prior to terminating the extended advertising, even if the
* duration has not expired. Valid range is from 1 to 255.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void enableAdvertising(boolean enable, int duration,
int maxExtendedAdvertisingEvents) {
try {
mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration,
- maxExtendedAdvertisingEvents);
+ maxExtendedAdvertisingEvents, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -90,9 +94,12 @@ public final class AdvertisingSet {
* three bytes will be added for flags. If the update takes place when the advertising set is
* enabled, the data can be maximum 251 bytes long.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setAdvertisingData(AdvertiseData advertiseData) {
try {
- mGatt.setAdvertisingData(mAdvertiserId, advertiseData);
+ mGatt.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -107,9 +114,12 @@ public final class AdvertisingSet {
* exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place
* when the advertising set is enabled, the data can be maximum 251 bytes long.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setScanResponseData(AdvertiseData scanResponse) {
try {
- mGatt.setScanResponseData(mAdvertiserId, scanResponse);
+ mGatt.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -122,9 +132,12 @@ public final class AdvertisingSet {
*
* @param parameters advertising set parameters.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setAdvertisingParameters(AdvertisingSetParameters parameters) {
try {
- mGatt.setAdvertisingParameters(mAdvertiserId, parameters);
+ mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -135,9 +148,12 @@ public final class AdvertisingSet {
* periodic advertising is not enabled. This method returns immediately, the operation
* status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
try {
- mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters);
+ mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -153,9 +169,12 @@ public final class AdvertisingSet {
* BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place when the
* periodic advertising is enabled for this set, the data can be maximum 251 bytes long.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingData(AdvertiseData periodicData) {
try {
- mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData);
+ mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -168,9 +187,12 @@ public final class AdvertisingSet {
* @param enable whether the periodic advertising should be enabled (true), or disabled
* (false).
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingEnabled(boolean enable) {
try {
- mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable);
+ mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -181,13 +203,16 @@ public final class AdvertisingSet {
* This method is exposed only for Bluetooth PTS tests, no app or system service
* should ever use it.
*
- * This method requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission.
- *
* @hide
*/
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_ADVERTISE,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public void getOwnAddress() {
try {
- mGatt.getOwnAddress(mAdvertiserId);
+ mGatt.getOwnAddress(mAdvertiserId, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -198,6 +223,7 @@ public final class AdvertisingSet {
*
* @hide
*/
+ @RequiresNoPermission
public int getAdvertiserId() {
return mAdvertiserId;
}
diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 5f166f4a41..58029745ab 100644
--- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -16,11 +16,17 @@
package android.bluetooth.le;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
+import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.content.AttributionSource;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -30,6 +36,7 @@ import android.util.Log;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
/**
* This class provides a way to perform Bluetooth LE advertise operations, such as starting and
@@ -38,9 +45,6 @@ import java.util.Map;
* <p>
* To get an instance of {@link BluetoothLeAdvertiser}, call the
* {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
- * <p>
- * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
*
* @see AdvertiseData
*/
@@ -56,9 +60,11 @@ public final class BluetoothLeAdvertiser {
private static final int FLAGS_FIELD_BYTES = 3;
private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2;
+ private final BluetoothAdapter mBluetoothAdapter;
private final IBluetoothManager mBluetoothManager;
+ private final AttributionSource mAttributionSource;
+
private final Handler mHandler;
- private BluetoothAdapter mBluetoothAdapter;
private final Map<AdvertiseCallback, AdvertisingSetCallback>
mLegacyAdvertisers = new HashMap<>();
private final Map<AdvertisingSetCallback, IAdvertisingSetCallback>
@@ -72,22 +78,24 @@ public final class BluetoothLeAdvertiser {
* @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management
* @hide
*/
- public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) {
- mBluetoothManager = bluetoothManager;
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ public BluetoothLeAdvertiser(BluetoothAdapter bluetoothAdapter) {
+ mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
+ mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
+ mAttributionSource = mBluetoothAdapter.getAttributionSource();
mHandler = new Handler(Looper.getMainLooper());
}
/**
* Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted.
* Returns immediately, the operation status is delivered through {@code callback}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @param settings Settings for Bluetooth LE advertising.
* @param advertiseData Advertisement data to be broadcasted.
* @param callback Callback for advertising status.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertising(AdvertiseSettings settings,
AdvertiseData advertiseData, final AdvertiseCallback callback) {
startAdvertising(settings, advertiseData, null, callback);
@@ -98,14 +106,15 @@ public final class BluetoothLeAdvertiser {
* operation succeeds. The {@code scanResponse} is returned when a scanning device sends an
* active scan request. This method returns immediately, the operation status is delivered
* through {@code callback}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param settings Settings for Bluetooth LE advertising.
* @param advertiseData Advertisement data to be advertised in advertisement packet.
* @param scanResponse Scan response associated with the advertisement data.
* @param callback Callback for advertising status.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertising(AdvertiseSettings settings,
AdvertiseData advertiseData, AdvertiseData scanResponse,
final AdvertiseCallback callback) {
@@ -160,6 +169,10 @@ public final class BluetoothLeAdvertiser {
}
}
+ @SuppressLint({
+ "AndroidFrameworkBluetoothPermission",
+ "AndroidFrameworkRequiresPermission",
+ })
AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) {
return new AdvertisingSetCallback() {
@Override
@@ -192,11 +205,12 @@ public final class BluetoothLeAdvertiser {
/**
* Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
* {@link BluetoothLeAdvertiser#startAdvertising}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @param callback {@link AdvertiseCallback} identifies the advertising instance to stop.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void stopAdvertising(final AdvertiseCallback callback) {
synchronized (mLegacyAdvertisers) {
if (callback == null) {
@@ -232,6 +246,9 @@ public final class BluetoothLeAdvertiser {
* size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
* feature is made when it's not supported by the controller.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -262,6 +279,9 @@ public final class BluetoothLeAdvertiser {
* size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
* feature is made when it's not supported by the controller.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -297,6 +317,9 @@ public final class BluetoothLeAdvertiser {
* size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
* feature is made when it's not supported by the controller.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -337,6 +360,9 @@ public final class BluetoothLeAdvertiser {
* maxExtendedAdvertisingEvents is used on a controller that doesn't support the LE Extended
* Advertising
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -432,7 +458,8 @@ public final class BluetoothLeAdvertiser {
try {
gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
- periodicData, duration, maxExtendedAdvertisingEvents, wrapped);
+ periodicData, duration, maxExtendedAdvertisingEvents, wrapped,
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to start advertising set - ", e);
postStartSetFailure(handler, callback,
@@ -445,6 +472,9 @@ public final class BluetoothLeAdvertiser {
* Used to dispose of a {@link AdvertisingSet} object, obtained with {@link
* BluetoothLeAdvertiser#startAdvertisingSet}.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void stopAdvertisingSet(AdvertisingSetCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
@@ -458,7 +488,7 @@ public final class BluetoothLeAdvertiser {
IBluetoothGatt gatt;
try {
gatt = mBluetoothManager.getBluetoothGatt();
- gatt.stopAdvertisingSet(wrapped);
+ gatt.stopAdvertisingSet(wrapped, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to stop advertising - ", e);
}
@@ -469,6 +499,7 @@ public final class BluetoothLeAdvertiser {
*
* @hide
*/
+ @RequiresNoPermission
public void cleanup() {
mLegacyAdvertisers.clear();
mCallbackWrappers.clear();
@@ -476,6 +507,8 @@ public final class BluetoothLeAdvertiser {
}
// Compute the size of advertisement data or scan resp
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) {
if (data == null) return 0;
// Flags field is omitted if the advertising is not connectable.
@@ -546,8 +579,11 @@ public final class BluetoothLeAdvertiser {
if (data.getIncludeTxPowerLevel()) {
size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
}
- if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) {
- size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length();
+ if (data.getIncludeDeviceName()) {
+ final int length = mBluetoothAdapter.getNameLengthForAdvertise();
+ if (length >= 0) {
+ size += OVERHEAD_BYTES_PER_FIELD + length;
+ }
}
return size;
}
@@ -556,6 +592,7 @@ public final class BluetoothLeAdvertiser {
return array == null ? 0 : array.length;
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) {
return new IAdvertisingSetCallback.Stub() {
@Override
@@ -569,8 +606,8 @@ public final class BluetoothLeAdvertiser {
return;
}
- AdvertisingSet advertisingSet =
- new AdvertisingSet(advertiserId, mBluetoothManager);
+ AdvertisingSet advertisingSet = new AdvertisingSet(
+ advertiserId, mBluetoothManager, mAttributionSource);
mAdvertisingSets.put(advertiserId, advertisingSet);
callback.onAdvertisingSetStarted(advertisingSet, txPower, status);
}
@@ -680,6 +717,7 @@ public final class BluetoothLeAdvertiser {
};
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback,
final int error) {
handler.post(new Runnable() {
@@ -690,6 +728,7 @@ public final class BluetoothLeAdvertiser {
});
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private void postStartFailure(final AdvertiseCallback callback, final int error) {
mHandler.post(new Runnable() {
@Override
@@ -699,6 +738,7 @@ public final class BluetoothLeAdvertiser {
});
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private void postStartSuccess(final AdvertiseCallback callback,
final AdvertiseSettings settings) {
mHandler.post(new Runnable() {
diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java
index 2888fbd8a3..34aac8bfdb 100644
--- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -16,16 +16,22 @@
package android.bluetooth.le;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -36,6 +42,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* This class provides methods to perform scan related operations for Bluetooth LE devices. An
@@ -44,9 +51,6 @@ import java.util.Map;
* <p>
* Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
* {@link BluetoothLeScanner}.
- * <p>
- * <b>Note:</b> Most of the scan methods here require
- * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @see ScanFilter
*/
@@ -78,14 +82,13 @@ public final class BluetoothLeScanner {
*/
public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ private final BluetoothAdapter mBluetoothAdapter;
private final IBluetoothManager mBluetoothManager;
+ private final AttributionSource mAttributionSource;
+
private final Handler mHandler;
- private BluetoothAdapter mBluetoothAdapter;
private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
- private final String mOpPackageName;
- private final String mFeatureId;
-
/**
* Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
*
@@ -94,14 +97,12 @@ public final class BluetoothLeScanner {
* @param featureId The featureId of the context this object was created from
* @hide
*/
- public BluetoothLeScanner(IBluetoothManager bluetoothManager,
- @NonNull String opPackageName, @Nullable String featureId) {
- mBluetoothManager = bluetoothManager;
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ public BluetoothLeScanner(BluetoothAdapter bluetoothAdapter) {
+ mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
+ mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
+ mAttributionSource = mBluetoothAdapter.getAttributionSource();
mHandler = new Handler(Looper.getMainLooper());
mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
- mOpPackageName = opPackageName;
- mFeatureId = featureId;
}
/**
@@ -119,7 +120,10 @@ public final class BluetoothLeScanner {
* @param callback Callback used to deliver scan results.
* @throws IllegalArgumentException If {@code callback} is null.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void startScan(final ScanCallback callback) {
startScan(null, new ScanSettings.Builder().build(), callback);
}
@@ -141,7 +145,10 @@ public final class BluetoothLeScanner {
* @param callback Callback used to deliver scan results.
* @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void startScan(List<ScanFilter> filters, ScanSettings settings,
final ScanCallback callback) {
startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null);
@@ -170,7 +177,10 @@ public final class BluetoothLeScanner {
* could not be sent.
* @see #stopScan(PendingIntent)
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public int startScan(@Nullable List<ScanFilter> filters, @Nullable ScanSettings settings,
@NonNull PendingIntent callbackIntent) {
return startScan(filters,
@@ -188,8 +198,13 @@ public final class BluetoothLeScanner {
* @hide
*/
@SystemApi
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
@RequiresPermission(allOf = {
- Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS})
+ android.Manifest.permission.BLUETOOTH_SCAN,
+ android.Manifest.permission.UPDATE_DEVICE_STATS
+ })
public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) {
startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback);
}
@@ -206,13 +221,20 @@ public final class BluetoothLeScanner {
* @hide
*/
@SystemApi
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
@RequiresPermission(allOf = {
- Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS})
+ android.Manifest.permission.BLUETOOTH_SCAN,
+ android.Manifest.permission.UPDATE_DEVICE_STATS
+ })
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback) {
startScan(filters, settings, workSource, callback, null, null);
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
private int startScan(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback,
final PendingIntent callbackIntent,
@@ -256,8 +278,8 @@ public final class BluetoothLeScanner {
wrapper.startRegistration();
} else {
try {
- gatt.startScanForIntent(callbackIntent, settings, filters, mOpPackageName,
- mFeatureId);
+ gatt.startScanForIntent(callbackIntent, settings, filters,
+ mAttributionSource);
} catch (RemoteException e) {
return ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
}
@@ -271,7 +293,9 @@ public final class BluetoothLeScanner {
*
* @param callback
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void stopScan(ScanCallback callback) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
synchronized (mLeScanClients) {
@@ -292,13 +316,15 @@ public final class BluetoothLeScanner {
* @param callbackIntent The PendingIntent that was used to start the scan.
* @see #startScan(List, ScanSettings, PendingIntent)
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void stopScan(PendingIntent callbackIntent) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
IBluetoothGatt gatt;
try {
gatt = mBluetoothManager.getBluetoothGatt();
- gatt.stopScanForIntent(callbackIntent, mOpPackageName);
+ gatt.stopScanForIntent(callbackIntent, mAttributionSource);
} catch (RemoteException e) {
}
}
@@ -311,6 +337,9 @@ public final class BluetoothLeScanner {
* @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
* used to start scan.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void flushPendingScanResults(ScanCallback callback) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null) {
@@ -331,6 +360,8 @@ public final class BluetoothLeScanner {
* @hide
*/
@SystemApi
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings,
final ScanCallback callback) {
int filterSize = truncatedFilters.size();
@@ -349,6 +380,7 @@ public final class BluetoothLeScanner {
*
* @hide
*/
+ @RequiresNoPermission
public void cleanup() {
mLeScanClients.clear();
}
@@ -356,6 +388,7 @@ public final class BluetoothLeScanner {
/**
* Bluetooth GATT interface callbacks
*/
+ @SuppressLint("AndroidFrameworkRequiresPermission")
private class BleScanCallbackWrapper extends IScannerCallback.Stub {
private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
@@ -390,7 +423,7 @@ public final class BluetoothLeScanner {
// Scan stopped.
if (mScannerId == -1 || mScannerId == -2) return;
try {
- mBluetoothGatt.registerScanner(this, mWorkSource);
+ mBluetoothGatt.registerScanner(this, mWorkSource, mAttributionSource);
wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
} catch (InterruptedException | RemoteException e) {
Log.e(TAG, "application registeration exception", e);
@@ -412,6 +445,7 @@ public final class BluetoothLeScanner {
}
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void stopLeScan() {
synchronized (this) {
if (mScannerId <= 0) {
@@ -419,8 +453,8 @@ public final class BluetoothLeScanner {
return;
}
try {
- mBluetoothGatt.stopScan(mScannerId);
- mBluetoothGatt.unregisterScanner(mScannerId);
+ mBluetoothGatt.stopScan(mScannerId, mAttributionSource);
+ mBluetoothGatt.unregisterScanner(mScannerId, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to stop scan and unregister", e);
}
@@ -428,6 +462,7 @@ public final class BluetoothLeScanner {
}
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
void flushPendingBatchResults() {
synchronized (this) {
if (mScannerId <= 0) {
@@ -435,7 +470,7 @@ public final class BluetoothLeScanner {
return;
}
try {
- mBluetoothGatt.flushPendingBatchResults(mScannerId);
+ mBluetoothGatt.flushPendingBatchResults(mScannerId, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to get pending scan results", e);
}
@@ -454,11 +489,11 @@ public final class BluetoothLeScanner {
try {
if (mScannerId == -1) {
// Registration succeeds after timeout, unregister scanner.
- mBluetoothGatt.unregisterScanner(scannerId);
+ mBluetoothGatt.unregisterScanner(scannerId, mAttributionSource);
} else {
mScannerId = scannerId;
mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
- mResultStorages, mOpPackageName, mFeatureId);
+ mResultStorages, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "fail to start le scan: " + e);
@@ -482,6 +517,7 @@ public final class BluetoothLeScanner {
*/
@Override
public void onScanResult(final ScanResult scanResult) {
+ Attributable.setAttributionSource(scanResult, mAttributionSource);
if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
// Check null in case the scan has been stopped
@@ -499,6 +535,7 @@ public final class BluetoothLeScanner {
@Override
public void onBatchScanResults(final List<ScanResult> results) {
+ Attributable.setAttributionSource(results, mAttributionSource);
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
@@ -510,6 +547,7 @@ public final class BluetoothLeScanner {
@Override
public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) {
+ Attributable.setAttributionSource(scanResult, mAttributionSource);
if (VDBG) {
Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString());
}
@@ -558,6 +596,7 @@ public final class BluetoothLeScanner {
}
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private void postCallbackError(final ScanCallback callback, final int errorCode) {
mHandler.post(new Runnable() {
@Override
@@ -598,6 +637,7 @@ public final class BluetoothLeScanner {
return true;
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) {
final int callbackType = settings.getCallbackType();
if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java
index 0f1a8e913b..dea686d18e 100644
--- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java
+++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java
@@ -16,10 +16,17 @@
package android.bluetooth.le;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -27,6 +34,7 @@ import android.util.Log;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Objects;
/**
* This class provides methods to perform periodic advertising related
@@ -35,9 +43,6 @@ import java.util.Map;
* <p>
* Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an
* instance of {@link PeriodicAdvertisingManager}.
- * <p>
- * <b>Note:</b> Most of the methods here require
- * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @hide
*/
@@ -52,8 +57,9 @@ public final class PeriodicAdvertisingManager {
private static final int SYNC_STARTING = -1;
+ private final BluetoothAdapter mBluetoothAdapter;
private final IBluetoothManager mBluetoothManager;
- private BluetoothAdapter mBluetoothAdapter;
+ private final AttributionSource mAttributionSource;
/* maps callback, to callback wrapper and sync handle */
Map<PeriodicAdvertisingCallback,
@@ -65,9 +71,10 @@ public final class PeriodicAdvertisingManager {
* @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
* @hide
*/
- public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) {
- mBluetoothManager = bluetoothManager;
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ public PeriodicAdvertisingManager(BluetoothAdapter bluetoothAdapter) {
+ mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
+ mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
+ mAttributionSource = mBluetoothAdapter.getAttributionSource();
mCallbackWrappers = new IdentityHashMap<>();
}
@@ -89,6 +96,10 @@ public final class PeriodicAdvertisingManager {
* @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or
* {@code timeout} is invalid or {@code callback} is null.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void registerSync(ScanResult scanResult, int skip, int timeout,
PeriodicAdvertisingCallback callback) {
registerSync(scanResult, skip, timeout, callback, null);
@@ -113,6 +124,10 @@ public final class PeriodicAdvertisingManager {
* @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or
* {@code timeout} is invalid or {@code callback} is null.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void registerSync(ScanResult scanResult, int skip, int timeout,
PeriodicAdvertisingCallback callback, Handler handler) {
if (callback == null) {
@@ -156,7 +171,8 @@ public final class PeriodicAdvertisingManager {
mCallbackWrappers.put(callback, wrapped);
try {
- gatt.registerSync(scanResult, skip, timeout, wrapped);
+ gatt.registerSync(
+ scanResult, skip, timeout, wrapped, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register sync - ", e);
return;
@@ -170,6 +186,9 @@ public final class PeriodicAdvertisingManager {
* @throws IllegalArgumentException if {@code callback} is null, or not a properly registered
* callback.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void unregisterSync(PeriodicAdvertisingCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback can't be null");
@@ -189,19 +208,20 @@ public final class PeriodicAdvertisingManager {
}
try {
- gatt.unregisterSync(wrapper);
+ gatt.unregisterSync(wrapper, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to cancel sync creation - ", e);
return;
}
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback,
Handler handler) {
return new IPeriodicAdvertisingCallback.Stub() {
public void onSyncEstablished(int syncHandle, BluetoothDevice device,
int advertisingSid, int skip, int timeout, int status) {
-
+ Attributable.setAttributionSource(device, mAttributionSource);
handler.post(new Runnable() {
@Override
public void run() {
diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java
index 7a8c2c6716..54b953c25c 100644
--- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java
+++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java
@@ -148,7 +148,7 @@ public final class PeriodicAdvertisingReport implements Parcelable {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java
index ddc93327b6..cb3bf29767 100644
--- a/framework/java/android/bluetooth/le/ScanFilter.java
+++ b/framework/java/android/bluetooth/le/ScanFilter.java
@@ -507,7 +507,7 @@ public final class ScanFilter implements Parcelable {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java
index c0c1aa1634..9b8c2eaf4d 100644
--- a/framework/java/android/bluetooth/le/ScanRecord.java
+++ b/framework/java/android/bluetooth/le/ScanRecord.java
@@ -18,6 +18,7 @@ package android.bluetooth.le;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothUuid;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.ParcelUuid;
@@ -29,10 +30,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
/**
* Represents a scan record from Bluetooth LE scan.
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class ScanRecord {
private static final String TAG = "ScanRecord";
@@ -168,6 +171,27 @@ public final class ScanRecord {
return mBytes;
}
+ /**
+ * Test if any fields contained inside this scan record are matched by the
+ * given matcher.
+ *
+ * @hide
+ */
+ public boolean matchesAnyField(@NonNull Predicate<byte[]> matcher) {
+ int pos = 0;
+ while (pos < mBytes.length) {
+ final int length = mBytes[pos] & 0xFF;
+ if (length == 0) {
+ break;
+ }
+ if (matcher.test(Arrays.copyOfRange(mBytes, pos, pos + length + 1))) {
+ return true;
+ }
+ pos += length + 1;
+ }
+ return false;
+ }
+
private ScanRecord(List<ParcelUuid> serviceUuids,
List<ParcelUuid> serviceSolicitationUuids,
SparseArray<byte[]> manufacturerData,
diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java
index 855d345417..5228456284 100644
--- a/framework/java/android/bluetooth/le/ScanResult.java
+++ b/framework/java/android/bluetooth/le/ScanResult.java
@@ -16,8 +16,11 @@
package android.bluetooth.le;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothDevice;
+import android.content.Attributable;
+import android.content.AttributionSource;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,7 +29,7 @@ import java.util.Objects;
/**
* ScanResult for Bluetooth LE scan.
*/
-public final class ScanResult implements Parcelable {
+public final class ScanResult implements Parcelable, Attributable {
/**
* For chained advertisements, inidcates tha the data contained in this
@@ -195,6 +198,11 @@ public final class ScanResult implements Parcelable {
return 0;
}
+ /** {@hide} */
+ public void setAttributionSource(@NonNull AttributionSource attributionSource) {
+ Attributable.setAttributionSource(mDevice, attributionSource);
+ }
+
/**
* Returns the remote Bluetooth device identified by the Bluetooth device address.
*/
@@ -309,7 +317,7 @@ public final class ScanResult implements Parcelable {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/framework/java/android/bluetooth/le/TruncatedFilter.java b/framework/java/android/bluetooth/le/TruncatedFilter.java
index a753aa6fef..93f526bb9f 100644
--- a/framework/java/android/bluetooth/le/TruncatedFilter.java
+++ b/framework/java/android/bluetooth/le/TruncatedFilter.java
@@ -16,6 +16,7 @@
package android.bluetooth.le;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import java.util.List;
@@ -26,6 +27,7 @@ import java.util.List;
* @hide
*/
@SystemApi
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class TruncatedFilter {
private final ScanFilter mFilter;
private final List<ResultStorageDescriptor> mStorageDescriptors;