Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f80e4a0 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Update Bluetooth API annotations.

Recent work has introduced a new "Nearby devices" runtime permission
which protects all existing Bluetooth APIs; we've done this by
defining a <split-permission> to convert the old BLUETOOTH and
BLUETOOTH_ADMIN permissions into one of three new permissions:

* BLUETOOTH_ADVERTISE: Required to be able to advertise to nearby
                       Bluetooth devices.
* BLUETOOTH_CONNECT:   Allows applications to connect to paired
                       bluetooth devices.
* BLUETOOTH_SCAN:      Required to be able to discover and pair
                       nearby Bluetooth devices.

At its core, this change begins updating the Bluetooth APIs to have
correct @RequiresPermission indicating which permission is actually
enforced internally.  To ensure alignment across Binder, the newly
added "RequiresPermissionChecker" Error Prone checker was used to
discover any inconsistencies, ensuring correctness from server-side
enforcement up through to the public APIs.

In addition, since developers will continue building apps for both
modern and legacy platforms, this change introduces new auto-doc
annotations which will emit helpful consistent documentation
describing the behavior of older devices that are still using the
old permission model.

Bug: 183626724
Test: ./build/soong/soong_ui.bash --make-mode Bluetooth RUN_ERROR_PRONE=true
Change-Id: I02aa127e8e07f239561f4f2a3bbdfc6fccb82f7f
parent a95b9490
Loading
Loading
Loading
Loading
+72 −28
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ import android.annotation.Nullable;
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.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -69,10 +72,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,10 +93,10 @@ 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";
@@ -112,11 +115,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 +136,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 =
@@ -307,7 +310,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 +352,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,6 +375,8 @@ public final class BluetoothA2dp implements BluetoothProfile {
     * {@inheritDoc}
     */
    @Override
    @RequiresBluetoothConnectPermission
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
    public List<BluetoothDevice> getConnectedDevices() {
        if (VDBG) log("getConnectedDevices()");
        try {
@@ -387,6 +396,8 @@ 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 {
@@ -406,6 +417,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 +454,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 + ")");
@@ -468,7 +483,9 @@ 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 {
@@ -495,7 +512,10 @@ public final class BluetoothA2dp implements BluetoothProfile {
     * @return true if priority is set, false on error
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @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 +534,10 @@ public final class BluetoothA2dp implements BluetoothProfile {
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @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 + ")");
@@ -546,7 +569,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 + ")");
@@ -620,6 +645,8 @@ 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 {
@@ -636,10 +663,11 @@ 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();
@@ -662,6 +690,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
    public boolean shouldSendVolumeKeys(BluetoothDevice device) {
        if (isEnabled() && isValidDevice(device)) {
            ParcelUuid[] uuids = device.getUuids();
@@ -686,7 +715,9 @@ 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");
@@ -714,7 +745,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 + ")");
@@ -744,7 +777,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 +794,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,6 +810,7 @@ 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();
@@ -800,7 +838,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)
    @OptionalCodecsSupportStatus
    public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
        verifyDeviceNotNull(device, "isOptionalCodecsSupported");
@@ -826,7 +866,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)
    @OptionalCodecsPreferenceStatus
    public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
        verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
@@ -853,7 +895,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");
+15 −5
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
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.Context;
import android.os.Binder;
@@ -160,7 +163,9 @@ 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();
@@ -243,8 +248,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,6 +255,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
     *
     * @hide
     */
    @RequiresLegacyBluetoothPermission
    public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
        if (VDBG) log("getAudioConfig(" + device + ")");
        final IBluetoothA2dpSink service = getService();
@@ -278,7 +282,10 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
     * @return true if priority is set, false on error
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @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 +304,10 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @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 + ")");
+200 −69

File changed.

Preview size limit exceeded, changes collapsed.

+7 −3
Original line number Diff line number Diff line
@@ -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.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -54,10 +58,10 @@ 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)
    public static final String ACTION_CONNECTION_STATE_CHANGED =
            "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";

+164 −62

File changed.

Preview size limit exceeded, changes collapsed.

Loading