diff options
Diffstat (limited to 'framework/java/android')
53 files changed, 2883 insertions, 1022 deletions
diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 16413e1a1d..1fb7638a6f 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -20,11 +20,16 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -69,10 +74,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 +95,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 +119,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 +140,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 +265,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 +280,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 +317,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 +359,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 +382,15 @@ 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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -387,12 +404,15 @@ 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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -406,6 +426,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 +463,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 +473,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 +492,15 @@ 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 service.getActiveDevice(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; @@ -495,7 +521,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 +544,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 +560,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 +580,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 +590,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 +613,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 +640,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 +662,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 +680,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 +707,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 +733,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 +763,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 +777,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 +795,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 +812,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 +828,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 +856,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 +884,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 +913,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 +929,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 +951,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 +982,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 +1012,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..c0a2aa3db4 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -19,9 +19,15 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -67,11 +73,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 +94,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 +132,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 +175,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 +199,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -203,12 +223,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -224,12 +248,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 +269,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 +276,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 +305,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 +328,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 +343,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 +363,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 +385,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 +414,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 3802289dd6..8afc557ef8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -17,19 +17,26 @@ package android.bluetooth; -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; @@ -39,6 +46,7 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; @@ -66,6 +74,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 +105,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 +146,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 +280,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 +309,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 +331,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 +363,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 +518,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 +540,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 +575,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 +604,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 +619,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 +646,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 +663,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,14 +706,15 @@ 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(); @@ -698,6 +728,7 @@ public final class BluetoothAdapter { * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener * implementation. */ + @SuppressLint("AndroidFrameworkBluetoothPermission") private static final IBluetoothMetadataListener sBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override @@ -721,32 +752,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 +800,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 +818,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 +836,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 +856,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 +879,7 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresNoPermission public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { if (!getLeAccess()) { return null; @@ -839,27 +890,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 +920,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 +935,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresNoPermission public boolean isLeEnabled() { final int state = getLeState(); if (DBG) { @@ -920,13 +973,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 +1020,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 +1042,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 +1053,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableBluetoothGetStateCache() { mBluetoothGetStateCache.disableLocal(); } @@ -1038,7 +1097,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 +1134,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 +1182,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 +1193,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 +1221,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 +1236,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 +1259,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 +1277,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 +1311,13 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { mServiceLock.readLock().lock(); if (mService != null && mService.factoryReset() - && mManagerService != null && mManagerService.onFactoryReset()) { + && mManagerService != null + && mManagerService.onFactoryReset(mAttributionSource)) { return true; } Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); @@ -1252,7 +1337,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 +1347,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 +1371,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 +1381,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 +1399,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 +1409,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 +1430,7 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBluetoothClass(BluetoothClass bluetoothClass) { if (getState() != STATE_ON) { return false; @@ -1366,13 +1457,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,7 +1487,7 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { @@ -1417,13 +1510,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,7 +1540,7 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setLeIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { @@ -1474,7 +1569,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 +1580,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 +1618,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 +1629,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 +1669,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 +1679,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 +1691,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 +1700,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 +1712,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 +1721,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,7 +1739,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getDiscoveryEndMillis() { try { mServiceLock.readLock().lock(); @@ -1650,32 +1755,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 +1783,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 +1794,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 +1820,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 +1830,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 +1858,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 +1868,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 +1892,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 +1911,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 +1937,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 +1963,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 +1986,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 +2019,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 +2044,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 +2074,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresNoPermission public boolean isBleScanAlwaysAvailable() { try { return mManagerService.isBleScanAlwaysAvailable(); @@ -1982,6 +2090,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 +2108,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableIsOffloadedFilteringSupportedCache() { mBluetoothFilteringCache.disableLocal(); } @@ -2013,6 +2123,8 @@ public final class BluetoothAdapter { * * @return true if chipset supports on-chip filtering */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isOffloadedFilteringSupported() { if (!getLeAccess()) { return false; @@ -2025,6 +2137,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 +2161,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 +2185,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 +2209,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 +2233,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 +2258,8 @@ public final class BluetoothAdapter { * * @return the maximum LE advertising data length. */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public int getLeMaximumAdvertisingDataLength() { if (!getLeAccess()) { return 0; @@ -2158,6 +2282,7 @@ public final class BluetoothAdapter { * * @return true if phone supports Hearing Aid Profile */ + @RequiresNoPermission private boolean isHearingAidProfileSupported() { try { return mManagerService.isHearingAidProfileSupported(); @@ -2173,12 +2298,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 +2321,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 +2333,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 +2353,7 @@ public final class BluetoothAdapter { * instead. */ @Deprecated + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { SynchronousResultReceiver receiver = new SynchronousResultReceiver(); requestControllerActivityEnergyInfo(receiver); @@ -2249,6 +2379,7 @@ public final class BluetoothAdapter { * @param result The callback to which to send the activity info. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void requestControllerActivityEnergyInfo(ResultReceiver result) { try { mServiceLock.readLock().lock(); @@ -2276,7 +2407,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 +2417,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMostRecentlyConnectedDevices(); + return mService.getMostRecentlyConnectedDevices(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2304,7 +2437,9 @@ 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]); @@ -2312,7 +2447,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return toDeviceSet(mService.getBondedDevices()); + return toDeviceSet(mService.getBondedDevices(mAttributionSource)); } return toDeviceSet(new BluetoothDevice[0]); } catch (RemoteException e) { @@ -2333,6 +2468,7 @@ public final class BluetoothAdapter { * BluetoothProfile}. * @hide */ + @RequiresNoPermission public @NonNull List<Integer> getSupportedProfiles() { final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>(); @@ -2359,6 +2495,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 +2540,8 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public int getConnectionState() { if (getState() != STATE_ON) { return BluetoothAdapter.STATE_DISCONNECTED; @@ -2379,10 +2549,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 +2570,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 +2592,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableGetProfileConnectionStateCache() { mGetProfileConnectionStateCache.disableLocal(); } @@ -2438,7 +2614,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 +2632,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 +2639,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 +2653,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 +2666,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 +2709,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 +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 listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, false); @@ -2589,7 +2776,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 +2785,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 +2820,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 +2853,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 +2887,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 +2905,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 +2933,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 +2961,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,50 +2972,51 @@ 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; @@ -2830,6 +3035,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; @@ -2902,8 +3111,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); @@ -2959,11 +3170,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(); @@ -2998,7 +3209,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) { @@ -3007,7 +3220,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); + return mManagerService.enableNoAutoConnect(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -3050,7 +3263,7 @@ public final class BluetoothAdapter { /** * 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 @@ -3151,7 +3364,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void generateLocalOobData(@Transport int transport, @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) { if (transport != BluetoothDevice.TRANSPORT_BREDR && transport @@ -3195,12 +3408,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; @@ -3298,11 +3513,17 @@ public final class BluetoothAdapter { && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; } + /** {@hide} */ @UnsupportedAppUsage - /*package*/ IBluetoothManager getBluetoothManager() { + public IBluetoothManager getBluetoothManager() { return mManagerService; } + /** {@hide} */ + public AttributionSource getAttributionSource() { + return mAttributionSource; + } + private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks = new ArrayList<IBluetoothManagerCallback>(); @@ -3344,6 +3565,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 @@ -3355,7 +3631,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); } @@ -3374,7 +3653,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)); @@ -3408,6 +3690,7 @@ public final class BluetoothAdapter { return false; } + @SuppressLint("AndroidFrameworkBluetoothPermission") ScanCallback scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { @@ -3471,7 +3754,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()"); @@ -3512,7 +3797,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 = @@ -3558,7 +3845,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 = @@ -3603,7 +3892,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); @@ -3676,7 +3965,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, @NonNull OnMetadataChangedListener listener) { if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); @@ -3732,6 +4021,7 @@ public final class BluetoothAdapter { @Nullable byte[] value); } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothConnectionCallback mConnectionCallback = new IBluetoothConnectionCallback.Stub() { @Override @@ -3765,6 +4055,7 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if the callback is already registered * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor, @NonNull BluetoothConnectionCallback callback) { if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()"); @@ -3807,6 +4098,7 @@ public final class BluetoothAdapter { * @return true if the callback was unregistered successfully, false otherwise * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean unregisterBluetoothConnectionCallback( @NonNull BluetoothConnectionCallback callback) { if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()"); 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..0b43e71aba 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -16,6 +16,13 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -54,10 +61,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 +78,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 +103,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 +127,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -133,13 +150,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -153,13 +174,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 +197,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 +206,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 +219,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 +241,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 +250,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 a40bf34323..98823b096a 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,14 @@ 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.AttributionSource; import android.content.Context; import android.os.Build; import android.os.Handler; @@ -41,6 +47,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; import java.util.UUID; /** @@ -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> @@ -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,31 @@ public final class BluetoothDevice implements Parcelable { mAddress = address; mAddressType = ADDRESS_TYPE_PUBLIC; + mAttributionSource = BluetoothManager.resolveAttributionSource(null); + } + + void setAttributionSource(AttributionSource attributionSource) { + mAttributionSource = attributionSource; + } + + static BluetoothDevice setAttributionSource(BluetoothDevice device, + AttributionSource attributionSource) { + device.setAttributionSource(attributionSource); + return device; + } + + static List<BluetoothDevice> setAttributionSource(List<BluetoothDevice> devices, + AttributionSource attributionSource) { + if (devices != null) { + for (BluetoothDevice device : devices) { + device.setAttributionSource(attributionSource); + } + } + return devices; } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothDevice) { return mAddress.equals(((BluetoothDevice) o).getAddress()); } @@ -1165,6 +1250,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 +1270,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 +1280,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 +1301,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 +1311,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 +1319,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 +1335,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(); } @@ -1245,25 +1347,36 @@ public final class BluetoothDevice implements Parcelable { } /** - * 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. + * 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. * - * @return true on success, false on error - * @hide + * <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} permission. Alternatively, if the caller has the + * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can bypass the + * Companion Device Manager association requirement. + * + * @param alias is the new locally modifiable name for the remote Bluetooth device which must be + * non-null and not the empty string. + * @return {@code true} if the alias is successfully set, {@code false} on error + * @throws IllegalArgumentException if the alias is {@code null} or the empty string */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setAlias(@NonNull String alias) { + if (alias == null || alias.isEmpty()) { + throw new IllegalArgumentException("Cannot set the alias to null or the empty string"); + } final IBluetooth service = sService; if (service == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); return false; } try { - return service.setRemoteAlias(this, alias); + return service.setRemoteAlias(this, alias, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1279,7 +1392,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 +1402,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 +1419,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 +1442,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 +1472,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 +1483,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 +1492,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 +1508,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 +1518,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 +1532,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 +1543,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 +1560,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 +1571,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 +1584,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 +1613,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 +1636,40 @@ 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(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); + } 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 +1677,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 +1693,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 +1703,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 +1716,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 +1726,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 +1745,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 +1755,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 +1775,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. */ - @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 +1785,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 +1803,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 +1812,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 +1822,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 +1831,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 +1844,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 +1858,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 +1874,7 @@ public final class BluetoothDevice implements Parcelable { * * @return true confirmation has been sent out false for error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { final IBluetooth service = sService; if (service == null) { @@ -1727,7 +1897,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 +1907,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 +1931,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,8 +1965,6 @@ 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. @@ -1816,8 +1988,6 @@ 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 @@ -1867,14 +2037,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 +2063,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(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) { @@ -1916,14 +2088,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,7 +2114,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSimAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { @@ -1971,7 +2145,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 +2153,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 +2183,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 +2190,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 +2207,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 +2214,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 +2253,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 +2294,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 +2313,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 +2322,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 +2338,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 +2345,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 +2396,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 +2418,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 +2444,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 +2472,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 +2507,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 +2526,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 +2554,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 +2585,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 +2616,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) { final IBluetooth service = sService; if (service == null) { @@ -2432,7 +2644,7 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi @Nullable - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(@MetadataKey int key) { final IBluetooth service = sService; if (service == 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 381318b26d..aea82102ca 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); mDeviceBusy = false; @@ -1167,12 +1204,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; @@ -1184,7 +1222,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); mDeviceBusy = false; @@ -1202,11 +1241,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() @@ -1231,7 +1271,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); mDeviceBusy = false; @@ -1248,11 +1288,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; @@ -1273,7 +1314,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); mDeviceBusy = false; @@ -1289,11 +1330,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; @@ -1314,7 +1356,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); mDeviceBusy = false; @@ -1340,16 +1382,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; @@ -1367,10 +1410,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; @@ -1381,7 +1425,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); mDeviceBusy = false; @@ -1396,15 +1440,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); } @@ -1414,6 +1459,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(); } @@ -1426,12 +1473,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) { @@ -1448,7 +1496,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; @@ -1464,12 +1512,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; @@ -1484,16 +1534,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; @@ -1512,10 +1563,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() @@ -1524,7 +1576,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; @@ -1544,6 +1596,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) { @@ -1554,7 +1608,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; @@ -1571,6 +1626,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) { @@ -1586,9 +1643,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; @@ -1604,6 +1662,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @throws UnsupportedOperationException */ @Override + @RequiresNoPermission public int getConnectionState(BluetoothDevice device) { throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); } @@ -1615,6 +1674,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @throws UnsupportedOperationException */ @Override + @RequiresNoPermission public List<BluetoothDevice> getConnectedDevices() { throw new UnsupportedOperationException( "Use BluetoothManager#getConnectedDevices instead."); @@ -1628,6 +1688,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..3bf517c046 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -21,11 +21,17 @@ 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.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; @@ -70,10 +76,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 +96,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 +113,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 +153,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 +306,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"; @@ -331,8 +341,10 @@ public final class BluetoothHeadset implements BluetoothProfile { 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 +360,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) { @@ -432,15 +454,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 +498,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 +525,15 @@ 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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -521,12 +547,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -540,6 +570,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 +603,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 +619,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 +643,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 +659,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 +681,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 +712,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 +738,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 +761,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 +792,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 +825,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 +849,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 +887,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 +915,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 +938,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 +963,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 +983,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 +1024,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 +1053,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 +1091,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 +1126,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 +1158,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 +1184,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 +1214,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 +1221,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 +1235,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 +1261,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 +1280,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 +1300,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 +1310,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.getActiveDevice(); + return service.getActiveDevice(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1227,7 +1328,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 +1338,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 +1360,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 +1397,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..0059cdb9fb 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -16,10 +16,14 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -71,6 +75,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 +96,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 +115,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 +129,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 +142,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 +156,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 +170,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 +425,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 +439,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 +475,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 +503,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 +527,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -519,13 +554,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -542,13 +581,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 +610,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 +629,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 +642,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 +662,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 +681,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 +711,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 +737,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 +746,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 +766,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 +789,15 @@ 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 service.getCurrentCalls(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -757,13 +812,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 +839,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 +863,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 +892,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 +925,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 +956,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 +986,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 +1012,15 @@ 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 service.dial(device, number, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -968,13 +1039,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 +1068,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 +1091,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 +1117,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 +1142,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 +1170,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 +1197,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 +1221,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/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..3ff2ebd86b 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -21,9 +21,14 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -64,10 +69,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 +86,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 +131,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 +146,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 +175,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 +217,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 +241,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -244,13 +263,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -264,6 +287,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 +296,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 +320,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 +335,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 +355,15 @@ 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 service.getActiveDevices(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); @@ -357,7 +384,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 +407,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 +424,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 +444,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 +466,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 +478,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 +516,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 +530,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 +547,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 +566,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 +580,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 +591,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 +608,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 +619,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..11e5711eff 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -20,8 +20,12 @@ 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.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; +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"; @@ -342,48 +347,77 @@ public final class BluetoothHidDevice implements BluetoothProfile { @Override public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - clearCallingIdentity(); - mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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 +427,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 +443,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -425,11 +463,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -442,11 +484,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 +524,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, @@ -504,7 +550,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { if (service != null) { try { CallbackWrapper cbw = new CallbackWrapper(executor, callback); - result = service.registerApp(sdp, inQos, outQos, cbw); + result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -525,13 +571,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 +598,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 +626,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 +652,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 +677,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 +703,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 +729,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 +766,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 +782,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..0abe18c4d2 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -21,8 +21,12 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -65,11 +69,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 +81,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 +90,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 +99,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 +108,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 +117,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 +237,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 +252,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 +284,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 +326,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 +353,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -350,12 +377,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -372,7 +403,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 +413,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 +434,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 +457,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 +475,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 +495,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 +517,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 +530,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 +551,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 +578,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 +603,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 +628,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 +635,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 +647,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 +661,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 +688,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 +714,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 +739,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 3f00fa6f41..51bfd048fd 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -23,6 +23,10 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -65,10 +69,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 +86,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 +102,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 +117,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 +129,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { /** * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public void close() { mProfileConnector.disconnect(); } @@ -131,7 +137,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 +159,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 +199,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 +220,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -232,13 +242,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -252,14 +266,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 +305,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 +331,15 @@ 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 service.getActiveDevices(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); @@ -337,13 +356,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return group id that this device currently belongs to * @hide */ - @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; @@ -365,7 +386,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 + ")"); @@ -377,7 +402,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; @@ -398,14 +423,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..b13ccaf7e5 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -16,15 +16,18 @@ package android.bluetooth; -import android.Manifest; 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.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 +59,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 AttributionSource resolveAttributionSource(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 +101,7 @@ public final class BluetoothManager { * * @return the BLUETOOTH Adapter */ + @RequiresNoPermission public BluetoothAdapter getAdapter() { return mAdapter; } @@ -109,7 +120,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 +149,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 +177,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 +193,9 @@ public final class BluetoothManager { IBluetoothManager managerService = mAdapter.getBluetoothManager(); IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; - devices = iGatt.getDevicesMatchingConnectionStates(states); + devices = BluetoothDevice.setAttributionSource( + iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -210,6 +214,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 +235,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 +257,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 +280,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 +298,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..88505b51f8 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -18,10 +18,15 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -51,6 +56,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 +80,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 +94,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 +120,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 +137,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 +163,14 @@ 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 service.getClient(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -175,12 +188,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 +212,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 +227,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 +275,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -280,12 +303,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -302,12 +329,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 +357,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 +380,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 +395,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 +415,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 +437,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 +462,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..14804dbefe 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -20,9 +20,13 @@ 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.AttributionSource; import android.content.Context; import android.net.Uri; import android.os.Binder; @@ -48,16 +52,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 +81,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 +92,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 +173,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 +187,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 +216,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 +240,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 +269,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 +295,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); @@ -283,12 +320,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); @@ -305,12 +346,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 +374,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 +396,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 +411,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 +431,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 +452,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 +486,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 +499,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 +522,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 +550,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 +577,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 +607,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 +628,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..90c94de2f4 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -24,7 +24,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.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -74,10 +77,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 +106,10 @@ 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 + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED"; @@ -179,7 +184,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 +203,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 +244,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 +287,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 +317,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 +333,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 +349,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -351,13 +375,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -373,13 +404,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 +431,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 +457,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..8ce01a37cd 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -22,11 +22,15 @@ 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.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 +86,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 +100,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 +114,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 +132,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 +158,7 @@ public class BluetoothPbap implements BluetoothProfile { doBind(); } + @SuppressLint("AndroidFrameworkRequiresPermission") boolean doBind() { synchronized (mConnection) { try { @@ -216,6 +233,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 +243,8 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList<BluetoothDevice>(); } try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -238,13 +258,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 +286,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 +296,9 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList<BluetoothDevice>(); } try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -294,7 +322,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 +338,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 +356,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 +366,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 +374,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..3ebd8fe192 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -19,6 +19,11 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -39,6 +44,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 +58,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 +72,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 +114,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 +126,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 +146,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 +158,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 +178,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 +187,8 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -185,6 +206,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 +215,9 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -210,6 +235,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 +244,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 +261,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 +279,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 +301,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 +318,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 +340,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 +361,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 +373,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 201d6c495d..161c843f03 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; @@ -300,7 +298,6 @@ public interface BluetoothProfile { * * @return List of devices. The list will be empty on error. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getConnectedDevices(); /** @@ -314,7 +311,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); /** @@ -324,7 +320,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); /** @@ -339,6 +334,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); /** @@ -347,6 +343,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..beff841f2f 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -16,10 +16,13 @@ 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; @@ -31,6 +34,7 @@ import android.util.Log; * @param <T> The Bluetooth profile interface for this connection. * @hide */ +@SuppressLint("AndroidFrameworkBluetoothPermission") public abstract class BluetoothProfileConnector<T> { private final int mProfileId; private BluetoothProfile.ServiceListener mServiceListener; @@ -78,6 +82,7 @@ public abstract class BluetoothProfileConnector<T> { mServiceName = serviceName; } + @SuppressLint("AndroidFrameworkRequiresPermission") private boolean doBind() { synchronized (mConnection) { if (mService == null) { @@ -120,6 +125,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..0631abdada 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -17,8 +17,14 @@ 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.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -61,11 +67,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 +97,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 +111,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 +150,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 +175,14 @@ 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 service.getClient(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -186,12 +200,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 +224,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 +238,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 +261,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 BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -263,12 +285,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 BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -284,12 +310,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 +338,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 +360,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 +375,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 +395,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 +416,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 +441,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/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/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..caa91fb239 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,10 +203,9 @@ 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 */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void getOwnAddress() { try { mGatt.getOwnAddress(mAdvertiserId); @@ -198,6 +219,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..60d4e2d6d2 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -16,16 +16,21 @@ 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.AttributionSource; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -36,6 +41,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 +50,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 +81,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 +96,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 +119,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 +144,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 +176,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 +197,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 +220,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 +277,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 +292,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 +315,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 +336,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 +359,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 +379,7 @@ public final class BluetoothLeScanner { * * @hide */ + @RequiresNoPermission public void cleanup() { mLeScanClients.clear(); } @@ -356,6 +387,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 +422,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 +444,7 @@ public final class BluetoothLeScanner { } } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopLeScan() { synchronized (this) { if (mScannerId <= 0) { @@ -419,8 +452,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 +461,7 @@ public final class BluetoothLeScanner { } } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void flushPendingBatchResults() { synchronized (this) { if (mScannerId <= 0) { @@ -435,7 +469,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 +488,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); @@ -558,6 +592,7 @@ public final class BluetoothLeScanner { } } + @SuppressLint("AndroidFrameworkBluetoothPermission") private void postCallbackError(final ScanCallback callback, final int errorCode) { mHandler.post(new Runnable() { @Override @@ -598,6 +633,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..47f47bb8e3 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -16,10 +16,16 @@ 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.AttributionSource; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -27,6 +33,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 +42,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 +56,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 +70,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 +95,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 +123,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 +170,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 +185,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,13 +207,14 @@ 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() { 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 3c20dcac8c..c5c4277d9e 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -16,7 +16,6 @@ package android.bluetooth.le; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -507,7 +506,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..57dad1a025 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -309,7 +309,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; |
