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

Commit fe9e4d7a authored by Rahul Sabnis's avatar Rahul Sabnis Committed by Automerger Merge Worker
Browse files

Merge "Address API council feedback on GATT API changes." am: 56ddf94a am:...

Merge "Address API council feedback on GATT API changes." am: 56ddf94a am: f387f744 am: 45840994

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/2030693

Change-Id: Ia4e5d5503f7708c3749456ee408119025c30f9b8
parents 880c8807 45840994
Loading
Loading
Loading
Loading
+63 −51
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package com.android.bluetooth.gatt;

import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;

import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
@@ -52,6 +52,7 @@ import android.content.AttributionSource;
import android.content.Intent;
import android.net.MacAddress;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -424,40 +425,37 @@ public class GattService extends ProfileService {
        sGattService = instance;
    }

    // Suppressed since we're not actually enforcing here
    // Suppressed because we are conditionally enforcing
    @SuppressLint("AndroidFrameworkRequiresPermission")
    private boolean permissionCheck(UUID characteristicUuid) {
        return !isHidCharUuid(characteristicUuid)
                || (checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED)
                        == PERMISSION_GRANTED);
    private void permissionCheck(UUID characteristicUuid) {
        if (!isHidCharUuid(characteristicUuid)) {
            return;
        }
        enforceBluetoothPrivilegedPermission(this);
    }

    // Suppressed since we're not actually enforcing here
    // Suppressed because we are conditionally enforcing
    @SuppressLint("AndroidFrameworkRequiresPermission")
    private boolean permissionCheck(int connId, int handle) {
        Set<Integer> restrictedHandles = mRestrictedHandles.get(connId);
        if (restrictedHandles == null || !restrictedHandles.contains(handle)) {
            return true;
    private void permissionCheck(int connId, int handle) {
        if (!isHandleRestricted(connId, handle)) {
            return;
        }

        return (checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED)
                == PERMISSION_GRANTED);
        enforceBluetoothPrivilegedPermission(this);
    }

    // Suppressed since we're not actually enforcing here
    // Suppressed because we are conditionally enforcing
    @SuppressLint("AndroidFrameworkRequiresPermission")
    private boolean permissionCheck(ClientMap.App app, int connId, int handle) {
        Set<Integer> restrictedHandles = mRestrictedHandles.get(connId);
        if (restrictedHandles == null || !restrictedHandles.contains(handle)) {
            return true;
    private void permissionCheck(ClientMap.App app, int connId, int handle) {
        if (!isHandleRestricted(connId, handle) || app.hasBluetoothPrivilegedPermission) {
            return;
        }

        if (!app.hasBluetoothPrivilegedPermission
                && checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED)== PERMISSION_GRANTED) {
        enforceBluetoothPrivilegedPermission(this);
        app.hasBluetoothPrivilegedPermission = true;
    }

        return app.hasBluetoothPrivilegedPermission;
    private boolean isHandleRestricted(int connId, int handle) {
        Set<Integer> restrictedHandles = mRestrictedHandles.get(connId);
        return restrictedHandles != null && restrictedHandles.contains(handle);
    }

    @Override
@@ -2299,7 +2297,13 @@ public class GattService extends ProfileService {

        ClientMap.App app = mClientMap.getByConnId(connId);
        if (app != null) {
            if (!permissionCheck(app, connId, handle)) {
            try {
                permissionCheck(connId, handle);
            } catch (SecurityException ex) {
                // Only throws on T+ as this is an older API and did not throw prior to T
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    throw ex;
                }
                Log.w(TAG, "onNotify() - permission check failed!");
                return;
            }
@@ -3257,7 +3261,7 @@ public class GattService extends ProfileService {
                this, attributionSource, "GattService getOwnAddress")) {
            return;
        }
        enforcePrivilegedPermission();
        enforceBluetoothPrivilegedPermission(this);
        mAdvertiseManager.getOwnAddress(advertiserId);
    }

@@ -3541,7 +3545,13 @@ public class GattService extends ProfileService {
            return;
        }

        if (!permissionCheck(connId, handle)) {
        try {
            permissionCheck(connId, handle);
        } catch (SecurityException ex) {
            // Only throws on T+ as this is an older API and did not throw prior to T
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                throw ex;
            }
            Log.w(TAG, "readCharacteristic() - permission check failed!");
            return;
        }
@@ -3567,7 +3577,13 @@ public class GattService extends ProfileService {
            return;
        }

        if (!permissionCheck(uuid)) {
        try {
            permissionCheck(uuid);
        } catch (SecurityException ex) {
            // Only throws on T+ as this is an older API and did not throw prior to T
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                throw ex;
            }
            Log.w(TAG, "readUsingCharacteristicUuid() - permission check failed!");
            return;
        }
@@ -3597,11 +3613,7 @@ public class GattService extends ProfileService {
            Log.e(TAG, "writeCharacteristic() - No connection for " + address + "...");
            return BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED;
        }

        if (!permissionCheck(connId, handle)) {
            Log.w(TAG, "writeCharacteristic() - permission check failed!");
            return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION;
        }
        permissionCheck(connId, handle);

        Log.d(TAG, "writeCharacteristic() - trying to acquire permit.");
        // Lock the thread until onCharacteristicWrite callback comes back.
@@ -3642,7 +3654,13 @@ public class GattService extends ProfileService {
            return;
        }

        if (!permissionCheck(connId, handle)) {
        try {
            permissionCheck(connId, handle);
        } catch (SecurityException ex) {
            // Only throws on T+ as this is an older API and did not throw prior to T
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                throw ex;
            }
            Log.w(TAG, "readDescriptor() - permission check failed!");
            return;
        }
@@ -3666,11 +3684,7 @@ public class GattService extends ProfileService {
            Log.e(TAG, "writeDescriptor() - No connection for " + address + "...");
            return BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED;
        }

        if (!permissionCheck(connId, handle)) {
            Log.w(TAG, "writeDescriptor() - permission check failed!");
            return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION;
        }
        permissionCheck(connId, handle);

        gattClientWriteDescriptorNative(connId, handle, authReq, value);
        return BluetoothStatusCodes.SUCCESS;
@@ -3726,7 +3740,13 @@ public class GattService extends ProfileService {
            return;
        }

        if (!permissionCheck(connId, handle)) {
        try {
            permissionCheck(connId, handle);
        } catch (SecurityException ex) {
            // Only throws on T+ as this is an older API and did not throw prior to T
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                throw ex;
            }
            Log.w(TAG, "registerForNotification() - permission check failed!");
            return;
        }
@@ -4451,25 +4471,17 @@ public class GattService extends ProfileService {
                            == BluetoothDevice.ADDRESS_TYPE_PUBLIC && filter.getIrk() == null) {
                        // Do not enforce
                    } else {
                        enforcePrivilegedPermission();
                    }
                        enforceBluetoothPrivilegedPermission(this);
                    }
                }
            }
        }

    // Enforce caller has BLUETOOTH_PRIVILEGED permission. A {@link SecurityException} will be
    // thrown if the caller app does not have BLUETOOTH_PRIVILEGED permission.
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
    private void enforcePrivilegedPermission() {
        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
                "Need BLUETOOTH_PRIVILEGED permission");
    }

    @SuppressLint("AndroidFrameworkRequiresPermission")
    private void enforcePrivilegedPermissionIfNeeded(ScanSettings settings) {
        if (needsPrivilegedPermissionForScan(settings)) {
            enforcePrivilegedPermission();
            enforceBluetoothPrivilegedPermission(this);
        }
    }

+0 −1
Original line number Diff line number Diff line
@@ -1061,7 +1061,6 @@ package android.bluetooth {
    field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200; // 0xc8
    field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 201; // 0xc9
    field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
    field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8
    field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9
    field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
    field public static final int FEATURE_NOT_SUPPORTED = 11; // 0xb
+6 −13
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothGattCharacteristic.WriteType;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
@@ -423,9 +424,6 @@ public final class BluetoothGatt implements BluetoothProfile {
                                if (status == 0) characteristic.setValue(value);
                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
                                        value, status);
                                // Keep calling deprecated callback to maintain app compatibility
                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
                                        status);
                            }
                        }
                    });
@@ -526,9 +524,6 @@ public final class BluetoothGatt implements BluetoothProfile {
                                characteristic.setValue(value);
                                callback.onCharacteristicChanged(BluetoothGatt.this,
                                        characteristic, value);
                                // Keep calling deprecated callback to maintain app compatibility
                                callback.onCharacteristicChanged(BluetoothGatt.this,
                                        characteristic);
                            }
                        }
                    });
@@ -585,8 +580,6 @@ public final class BluetoothGatt implements BluetoothProfile {
                                if (status == 0) descriptor.setValue(value);
                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status,
                                        value);
                                // Keep calling deprecated callback to maintain app compatibility
                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
                            }
                        }
                    });
@@ -1305,7 +1298,6 @@ public final class BluetoothGatt implements BluetoothProfile {
        return true;
    }


    /**
     * Writes a given characteristic and its values to the associated remote device.
     *
@@ -1318,7 +1310,8 @@ public final class BluetoothGatt implements BluetoothProfile {
     * @throws IllegalArgumentException if characteristic or its value are null
     *
     * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[],
     * int)} as this is not memory safe.
     * int)} as this is not memory safe because it relies on a {@link BluetoothGattCharacteristic}
     * object whose underlying fields are subject to change outside this method.
     */
    @Deprecated
    @RequiresLegacyBluetoothPermission
@@ -1338,7 +1331,6 @@ public final class BluetoothGatt implements BluetoothProfile {
    @IntDef(value = {
            BluetoothStatusCodes.SUCCESS,
            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
            BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
            BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
            BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
@@ -1362,7 +1354,7 @@ public final class BluetoothGatt implements BluetoothProfile {
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
    @WriteOperationReturnValues
    public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic,
            @NonNull byte[] value, int writeType) {
            @NonNull byte[] value, @WriteType int writeType) {
        if (characteristic == null) {
            throw new IllegalArgumentException("characteristic must not be null");
        }
@@ -1487,7 +1479,8 @@ public final class BluetoothGatt implements BluetoothProfile {
     * @throws IllegalArgumentException if descriptor or its value are null
     *
     * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as
     * this is not memory safe.
     * this is not memory safe because it relies on a {@link BluetoothGattDescriptor} object
     * whose underlying fields are subject to change outside this method.
     */
    @Deprecated
    @RequiresLegacyBluetoothPermission
+3 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ public abstract class BluetoothGattCallback {
     */
    public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull
            BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) {
        onCharacteristicRead(gatt, characteristic, status);
    }

    /**
@@ -155,6 +156,7 @@ public abstract class BluetoothGattCallback {
     */
    public void onCharacteristicChanged(@NonNull BluetoothGatt gatt,
            @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
        onCharacteristicChanged(gatt, characteristic);
    }

    /**
@@ -184,6 +186,7 @@ public abstract class BluetoothGattCallback {
     */
    public void onDescriptorRead(@NonNull BluetoothGatt gatt,
            @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) {
        onDescriptorRead(gatt, descriptor, status);
    }

    /**
+13 −1
Original line number Diff line number Diff line
@@ -15,11 +15,14 @@
 */
package android.bluetooth;

import android.annotation.IntDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -115,8 +118,17 @@ public class BluetoothGattCharacteristic implements Parcelable {
     */
    public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = "WRITE_TYPE_", value = {
            WRITE_TYPE_DEFAULT,
            WRITE_TYPE_NO_RESPONSE,
            WRITE_TYPE_SIGNED
    })
    public @interface WriteType{}

    /**
     * Write characteristic, requesting acknoledgement by the remote device
     * Write characteristic, requesting acknowledgement by the remote device
     */
    public static final int WRITE_TYPE_DEFAULT = 0x02;

Loading