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

Commit 6d2a089f authored by Chienyuan Huang's avatar Chienyuan Huang
Browse files

Add framework APIs for Bluetooth Distance Measurement

Bug: 256055210
Test: atest DistanceMeasurementParamsTest
Test: atest DistanceMeasurementResultTest
Test: atest DistanceMeasurementSessionTest
Tag: #feature
Change-Id: Ib42684fb984b704bab397bdc2ee02f7167f89fb7
parent 82af27aa
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -3719,6 +3719,30 @@ public class AdapterService extends Service {
            return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
        }

        @Override
        public void isDistanceMeasurementSupported(AttributionSource source,
                SynchronousResultReceiver receiver) {
            try {
                receiver.send(isDistanceMeasurementSupported(source));
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }
        public int isDistanceMeasurementSupported(AttributionSource source) {
            AdapterService service = getService();
            if (service == null) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
            } else if (!callerIsSystemOrActiveOrManagedUser(service, TAG,
                    "isDistanceMeasurementSupported")) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
            } else if (!Utils.checkConnectPermissionForDataDelivery(
                    service, source, TAG)) {
                return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
            }
            enforceBluetoothPrivilegedPermission(service);
            return BluetoothStatusCodes.FEATURE_SUPPORTED;
        }

        @Override
        public void getLeMaximumAdvertisingDataLength(SynchronousResultReceiver receiver) {
            try {
+105 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.bluetooth.gatt;

import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
import static com.android.bluetooth.Utils.checkCallerTargetSdk;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;

@@ -41,7 +42,10 @@ import android.bluetooth.IBluetoothGattServerCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.DistanceMeasurementMethod;
import android.bluetooth.le.DistanceMeasurementParams;
import android.bluetooth.le.IAdvertisingSetCallback;
import android.bluetooth.le.IDistanceMeasurementCallback;
import android.bluetooth.le.IPeriodicAdvertisingCallback;
import android.bluetooth.le.IScannerCallback;
import android.bluetooth.le.PeriodicAdvertisingParameters;
@@ -1768,9 +1772,83 @@ public class GattService extends ProfileService {
            }
            return service.numHwTrackFiltersAvailable(attributionSource);
        }

        @Override
        public void getSupportedDistanceMeasurementMethods(AttributionSource attributionSource,
                SynchronousResultReceiver receiver) {
            try {
                receiver.send(getSupportedDistanceMeasurementMethods(attributionSource));
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

    ;
        private List<DistanceMeasurementMethod> getSupportedDistanceMeasurementMethods(
                AttributionSource attributionSource) {
            GattService service = getService();
            if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG,
                    "GattService getSupportedDistanceMeasurementMethods")
                    || !Utils.checkConnectPermissionForDataDelivery(
                    service, attributionSource, "GattService startDistanceMeasurement")) {
                return new ArrayList<>();
            }
            enforceBluetoothPrivilegedPermission(service);
            return Arrays.asList(service.getSupportedDistanceMeasurementMethods());
        }

        @Override
        public void startDistanceMeasurement(ParcelUuid uuid,
                DistanceMeasurementParams distanceMeasurementParams,
                IDistanceMeasurementCallback callback, AttributionSource attributionSource,
                SynchronousResultReceiver receiver) {
            try {
                startDistanceMeasurement(uuid, distanceMeasurementParams, callback,
                        attributionSource);
                receiver.send(null);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

        private void startDistanceMeasurement(ParcelUuid uuid,
                DistanceMeasurementParams distanceMeasurementParams,
                IDistanceMeasurementCallback callback, AttributionSource attributionSource) {
            GattService service = getService();
            if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG,
                    "startDistanceMeasurement") || !Utils.checkConnectPermissionForDataDelivery(
                    service, attributionSource, "GattService startDistanceMeasurement")) {
                return;
            }
            enforceBluetoothPrivilegedPermission(service);
            service.startDistanceMeasurement(uuid, distanceMeasurementParams, callback);
        }

        @Override
        public void stopDistanceMeasurement(ParcelUuid uuid, BluetoothDevice device, int method,
                AttributionSource attributionSource, SynchronousResultReceiver receiver) {
            try {
                receiver.send(stopDistanceMeasurement(uuid, device, method, attributionSource));
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

        private int stopDistanceMeasurement(ParcelUuid uuid, BluetoothDevice device, int method,
                AttributionSource attributionSource) {
            GattService service = getService();
            if (service == null) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
            } else if (!callerIsSystemOrActiveOrManagedUser(service, TAG,
                    "stopDistanceMeasurement")) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
            } else if (!Utils.checkConnectPermissionForDataDelivery(
                    service, attributionSource, "GattService stopDistanceMeasurement")) {
                return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
            }
            enforceBluetoothPrivilegedPermission(service);
            return service.stopDistanceMeasurement(uuid, device, method);
        }
    };

    /**************************************************************************
     * Callback functions - CLIENT
@@ -3488,6 +3566,31 @@ public class GattService extends ProfileService {
        mAdvertiseManager.setPeriodicAdvertisingEnable(advertiserId, enable);
    }

    /**************************************************************************
     * Distance Measurement
     *************************************************************************/

    DistanceMeasurementMethod[] getSupportedDistanceMeasurementMethods() {
        // TODO(b/256055210): Implement DistanceMeasurementManager in Bluetooth APP
        // return mDistanceMeasurementManager.getSupportedMethods();
        return new DistanceMeasurementMethod[0];
    }


    void startDistanceMeasurement(ParcelUuid uuid,
            DistanceMeasurementParams distanceMeasurementParams,
            IDistanceMeasurementCallback callback) {
        // TODO(b/256055210): Implement DistanceMeasurementManager in Bluetooth APP
        // mDistanceMeasurementManager.startDistanceMeasurement(uuid, distanceMeasurementParams,
        // callback);
    }

    int stopDistanceMeasurement(ParcelUuid uuid, BluetoothDevice device, int method) {
        // TODO(b/256055210): Implement DistanceMeasurementManager in Bluetooth APP
        // mDistanceMeasurementManager.stopDistanceMeasurement(uuid, device, method, false);
        return BluetoothStatusCodes.SUCCESS;
    }

    /**************************************************************************
     * GATT Service functions - CLIENT
     *************************************************************************/
+76 −0
Original line number Diff line number Diff line
@@ -61,11 +61,13 @@ package android.bluetooth {
    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getBluetoothHciSnoopLoggingMode();
    method public int getConnectionState();
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public long getDiscoveryEndMillis();
    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.le.DistanceMeasurementManager getDistanceMeasurementManager();
    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getMostRecentlyConnectedDevices();
    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.os.Bundle getPreferredAudioProfiles(@NonNull android.bluetooth.BluetoothDevice);
    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<java.lang.Integer> getSupportedProfiles();
    method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.os.ParcelUuid> getUuidsList();
    method public boolean isBleScanAlwaysAvailable();
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int isDistanceMeasurementSupported();
    method public boolean isLeEnabled();
    method @NonNull public static String nameForState(int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int notifyPreferredAudioProfileChangeApplied(@NonNull android.bluetooth.BluetoothDevice);
@@ -754,6 +756,7 @@ package android.bluetooth {

  public final class BluetoothStatusCodes {
    field public static final int ALLOWED = 400; // 0x190
    field public static final int DISTANCE_MEASUREMENT_ERROR_INTERNAL = 1301; // 0x515
    field public static final int ERROR_ALREADY_IN_TARGET_STATE = 26; // 0x1a
    field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
    field public static final int ERROR_ANOTHER_ACTIVE_REQUEST = 29; // 0x1d
@@ -779,6 +782,7 @@ package android.bluetooth {
    field public static final int ERROR_LOCAL_NOT_ENOUGH_RESOURCES = 22; // 0x16
    field public static final int ERROR_NOT_ACTIVE_DEVICE = 12; // 0xc
    field public static final int ERROR_NO_ACTIVE_DEVICES = 13; // 0xd
    field public static final int ERROR_NO_LE_CONNECTION = 1300; // 0x514
    field public static final int ERROR_PROFILE_NOT_CONNECTED = 14; // 0xe
    field public static final int ERROR_REMOTE_LINK_ERROR = 25; // 0x19
    field public static final int ERROR_REMOTE_NOT_ENOUGH_RESOURCES = 23; // 0x17
@@ -964,6 +968,78 @@ package android.bluetooth.le {
    method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
  }

  public final class DistanceMeasurementManager {
    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.le.DistanceMeasurementMethod> getSupportedMethods();
    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.os.CancellationSignal startMeasurementSession(@NonNull android.bluetooth.le.DistanceMeasurementParams, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.le.DistanceMeasurementSession.Callback);
  }

  public final class DistanceMeasurementMethod implements android.os.Parcelable {
    method public double getId();
    method public boolean isAltitudeAngleSupported();
    method public boolean isAzimuthAngleSupported();
    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.DistanceMeasurementMethod> CREATOR;
    field public static final int DISTANCE_MEASUREMENT_METHOD_AUTO = 0; // 0x0
    field public static final int DISTANCE_MEASUREMENT_METHOD_RSSI = 1; // 0x1
  }

  public static final class DistanceMeasurementMethod.Builder {
    ctor public DistanceMeasurementMethod.Builder(int);
    method @NonNull public android.bluetooth.le.DistanceMeasurementMethod build();
    method @NonNull public android.bluetooth.le.DistanceMeasurementMethod.Builder setAltitudeAngleSupported(boolean);
    method @NonNull public android.bluetooth.le.DistanceMeasurementMethod.Builder setAzimuthAngleSupported(boolean);
  }

  public final class DistanceMeasurementParams implements android.os.Parcelable {
    method public static int getDefaultDuration();
    method @NonNull public android.bluetooth.BluetoothDevice getDevice();
    method public int getDuration();
    method public int getFrequency();
    method public static int getMaxDuration();
    method public int getMethod();
    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.DistanceMeasurementParams> CREATOR;
    field public static final int REPORT_FREQUENCY_HIGH = 2; // 0x2
    field public static final int REPORT_FREQUENCY_LOW = 0; // 0x0
    field public static final int REPORT_FREQUENCY_MEDIUM = 1; // 0x1
  }

  public static final class DistanceMeasurementParams.Builder {
    ctor public DistanceMeasurementParams.Builder(@NonNull android.bluetooth.BluetoothDevice);
    method @NonNull public android.bluetooth.le.DistanceMeasurementParams build();
    method @NonNull public android.bluetooth.le.DistanceMeasurementParams.Builder setDuration(@IntRange(from=0, to=3600) int);
    method @NonNull public android.bluetooth.le.DistanceMeasurementParams.Builder setFrequency(int);
    method @NonNull public android.bluetooth.le.DistanceMeasurementParams.Builder setMethod(int);
  }

  public final class DistanceMeasurementResult implements android.os.Parcelable {
    method @FloatRange(from=-90.0, to=90.0) public double getAltitudeAngle();
    method @FloatRange(from=0.0, to=360.0) public double getAzimuthAngle();
    method public double getErrorAltitudeAngle();
    method public double getErrorAzimuthAngle();
    method @FloatRange(from=0.0) public double getErrorMeters();
    method public double getMeters();
    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.DistanceMeasurementResult> CREATOR;
  }

  public static final class DistanceMeasurementResult.Builder {
    ctor public DistanceMeasurementResult.Builder(@FloatRange(from=0.0) double, @FloatRange(from=0.0) double);
    method @NonNull public android.bluetooth.le.DistanceMeasurementResult build();
    method @NonNull public android.bluetooth.le.DistanceMeasurementResult.Builder setAltitudeAngle(@FloatRange(from=-90.0, to=90.0) double);
    method @NonNull public android.bluetooth.le.DistanceMeasurementResult.Builder setAzimuthAngle(@FloatRange(from=0.0, to=360.0) double);
    method @NonNull public android.bluetooth.le.DistanceMeasurementResult.Builder setErrorAltitudeAngle(@FloatRange(from=0.0, to=180.0) double);
    method @NonNull public android.bluetooth.le.DistanceMeasurementResult.Builder setErrorAzimuthAngle(@FloatRange(from=0.0, to=360.0) double);
  }

  public final class DistanceMeasurementSession {
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int stopSession();
  }

  public static interface DistanceMeasurementSession.Callback {
    method public void onResult(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.le.DistanceMeasurementResult);
    method public void onStartFail(@NonNull int);
    method public void onStarted(@NonNull android.bluetooth.le.DistanceMeasurementSession);
    method public void onStopped(@NonNull android.bluetooth.le.DistanceMeasurementSession, @NonNull int);
  }

  @Deprecated public final class ResultStorageDescriptor implements android.os.Parcelable {
    ctor @Deprecated public ResultStorageDescriptor(int, int, int);
    method @Deprecated public int describeContents();
+74 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.DistanceMeasurementManager;
import android.bluetooth.le.PeriodicAdvertisingManager;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
@@ -853,6 +854,7 @@ public final class BluetoothAdapter {
    private BluetoothLeScanner mBluetoothLeScanner;
    private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
    private PeriodicAdvertisingManager mPeriodicAdvertisingManager;
    private DistanceMeasurementManager mDistanceMeasurementManager;

    private final IBluetoothManager mManagerService;
    private final AttributionSource mAttributionSource;
@@ -1198,6 +1200,40 @@ public final class BluetoothAdapter {
        }
    }

     /**
     * Get a {@link DistanceMeasurementManager} object for distance measurement operations.
     * <p>
     * Use {@link #isDistanceMeasurementSupported()} to check whether distance
     * measurement is supported on this device before calling this method.
     *
     * @return a new instance of {@link DistanceMeasurementManager}, or {@code null} if Bluetooth is
     * turned off
     * @throws UnsupportedOperationException if distance measurement is not supported on this device
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(allOf = {
            android.Manifest.permission.BLUETOOTH_CONNECT,
            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
    })
    public @Nullable DistanceMeasurementManager getDistanceMeasurementManager() {
        if (!getLeAccess()) {
            return null;
        }

        if (isDistanceMeasurementSupported() != BluetoothStatusCodes.FEATURE_SUPPORTED) {
            throw new UnsupportedOperationException("Distance measurement is unsupported");
        }

        synchronized (mLock) {
            if (mDistanceMeasurementManager == null) {
                mDistanceMeasurementManager = new DistanceMeasurementManager(this);
            }
            return mDistanceMeasurementManager;
        }
    }

    /**
     * Return true if Bluetooth is currently enabled and ready for use.
     * <p>Equivalent to:
@@ -2789,6 +2825,44 @@ public final class BluetoothAdapter {
        return BluetoothStatusCodes.ERROR_UNKNOWN;
    }

    /**
     * Returns whether the distance measurement feature is supported.
     *
     * @return whether the Bluetooth distance measurement is supported
     * @throws IllegalStateException if the bluetooth service is null
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(allOf = {
            android.Manifest.permission.BLUETOOTH_CONNECT,
            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
    })
    public @LeFeatureReturnValues int isDistanceMeasurementSupported() {
        if (!getLeAccess()) {
            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
        }
        mServiceLock.readLock().lock();
        try {
            if (mService != null) {
                final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
                mService.isDistanceMeasurementSupported(mAttributionSource, recv);
                return recv.awaitResultNoInterrupt(getSyncTimeout())
                    .getValue(BluetoothStatusCodes.ERROR_UNKNOWN);
            } else {
                throw new IllegalStateException(
                        "LE state is on, but there is no bluetooth service.");
            }
        } catch (TimeoutException e) {
            Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        } finally {
            mServiceLock.readLock().unlock();
        }
        return BluetoothStatusCodes.ERROR_UNKNOWN;
    }

    /**
     * Return the maximum LE advertising data length in bytes,
     * if LE Extended Advertising feature is supported, 0 otherwise.
+19 −0
Original line number Diff line number Diff line
@@ -505,6 +505,25 @@ public final class BluetoothStatusCodes {
    @SystemApi
    public static final int ERROR_HAP_INVALID_PRESET_INDEX = 1211;

    /**
     * Indicates that LE connection is required but not exist or disconnected.
     * <p>
     * Example solution: create LE connection then retry again.
     *
     * @hide
     */
    @SystemApi
    public static final int ERROR_NO_LE_CONNECTION = 1300;

    /**
     * Indicates internal error of distance measurement, such as read RSSI data fail.
     *
     * @hide
     */
    @SystemApi
    public static final int DISTANCE_MEASUREMENT_ERROR_INTERNAL = 1301;


    /**
     * Indicates that the RFCOMM listener could not be started due to the requested UUID already
     * being in use.
Loading