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

Commit 64528ba8 authored by Etienne Ruffieux's avatar Etienne Ruffieux Committed by Android (Google) Code Review
Browse files

Merge "Refactor Bluetooth scan mode APIs"

parents afb52eb8 e2763d87
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8742,6 +8742,7 @@ package android.bluetooth {
    method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
    method @Deprecated public static android.bluetooth.BluetoothAdapter getDefaultAdapter();
    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public java.time.Duration getDiscoverableTimeout();
    method public int getLeMaximumAdvertisingDataLength();
    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName();
    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getProfileConnectionState(int);
+2 −0
Original line number Diff line number Diff line
@@ -2002,6 +2002,8 @@ package android.bluetooth {
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean removeActiveDevice(int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setActiveDevice(@NonNull android.bluetooth.BluetoothDevice, int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int setDiscoverableTimeout(@NonNull java.time.Duration);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int setScanMode(int);
    field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
    field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
    field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
+71 −93
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import com.android.internal.annotations.GuardedBy;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -399,6 +400,16 @@ public final class BluetoothAdapter {
    @Retention(RetentionPolicy.SOURCE)
    public @interface ScanMode {}

    /** @hide */
    @IntDef(value = {
            BluetoothStatusCodes.SUCCESS,
            BluetoothStatusCodes.ERROR_UNKNOWN,
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ScanModeStatusCode {}

    /**
     * Indicates that both inquiry scan and page scan are disabled on the local
     * Bluetooth adapter. Therefore this device is neither discoverable
@@ -1615,7 +1626,7 @@ public final class BluetoothAdapter {
                return mService.getScanMode(mAttributionSource);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            throw e.rethrowFromSystemServer();
        } finally {
            mServiceLock.readLock().unlock();
        }
@@ -1623,143 +1634,110 @@ public final class BluetoothAdapter {
    }

    /**
     * Set the Bluetooth scan mode of the local Bluetooth adapter.
     * <p>The Bluetooth scan mode determines if the local adapter is
     * connectable and/or discoverable from remote Bluetooth devices.
     * <p>For privacy reasons, discoverable mode is automatically turned off
     * after <code>durationMillis</code> milliseconds. For example, 120000 milliseconds should be
     * enough for a remote device to initiate and complete its discovery process.
     * <p>Valid scan mode values are:
     * {@link #SCAN_MODE_NONE},
     * {@link #SCAN_MODE_CONNECTABLE},
     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
     * will return false. After turning on Bluetooth,
     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
     * to get the updated value.
     * Set the local Bluetooth adapter connectablility and discoverability.
     * <p>If the scan mode is set to {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
     * it will change to {@link #SCAN_MODE_CONNECTABLE} after the discoverable timeout.
     * The discoverable timeout can be set with {@link #setDiscoverableTimeout} and
     * checked with {@link #getDiscoverableTimeout}. By default, the timeout is usually
     * 120 seconds on phones which is enough for a remote device to initiate and complete
     * its discovery process.
     * <p>Applications cannot set the scan mode. They should use
     * <code>startActivityForResult(
     * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
     * </code>instead.
     *
     * @param mode valid scan mode
     * @param durationMillis time in milliseconds to apply scan mode, only used for {@link
     * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}
     * @return true if the scan mode was set, false otherwise
     * {@link #ACTION_REQUEST_DISCOVERABLE} instead.
     *
     * @param mode represents the desired state of the local device scan mode
     *
     * @return status code indicating whether the scan mode was successfully set
     * @hide
     */
    @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which "
            + "shows UI that confirms the user wants to go into discoverable mode.")
    @RequiresLegacyBluetoothPermission
    @SystemApi
    @RequiresBluetoothScanPermission
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
    public boolean setScanMode(@ScanMode int mode, long durationMillis) {
    @RequiresPermission(allOf = {
            android.Manifest.permission.BLUETOOTH_SCAN,
            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
    })
    @ScanModeStatusCode
    public int setScanMode(@ScanMode int mode) {
        if (getState() != STATE_ON) {
            return false;
            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
        }
        try {
            mServiceLock.readLock().lock();
            if (mService != null) {
                int durationSeconds = Math.toIntExact(durationMillis / 1000);
                return mService.setScanMode(mode, durationSeconds, mAttributionSource);
                return mService.setScanMode(mode, mAttributionSource);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
        } catch (ArithmeticException ex) {
            Log.e(TAG, "setScanMode: Duration in seconds outside of the bounds of an int");
            throw new IllegalArgumentException("Duration not in bounds. In seconds, the "
                    + "durationMillis must be in the range of an int");
            throw e.rethrowFromSystemServer();
        } finally {
            mServiceLock.readLock().unlock();
        }
        return false;
        return BluetoothStatusCodes.ERROR_UNKNOWN;
    }

    /**
     * Set the Bluetooth scan mode of the local Bluetooth adapter.
     * <p>The Bluetooth scan mode determines if the local adapter is
     * connectable and/or discoverable from remote Bluetooth devices.
     * <p>For privacy reasons, discoverable mode is automatically turned off
     * after <code>duration</code> seconds. For example, 120 seconds should be
     * enough for a remote device to initiate and complete its discovery
     * process.
     * <p>Valid scan mode values are:
     * {@link #SCAN_MODE_NONE},
     * {@link #SCAN_MODE_CONNECTABLE},
     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
     * will return false. After turning on Bluetooth,
     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
     * to get the updated value.
     * <p>Applications cannot set the scan mode. They should use
     * <code>startActivityForResult(
     * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
     * </code>instead.
     * Get the timeout duration of the {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
     *
     * @param mode valid scan mode
     * @return true if the scan mode was set, false otherwise
     * @hide
     * @return the duration of the discoverable timeout or null if an error has occurred
     */
    @UnsupportedAppUsage
    @RequiresLegacyBluetoothPermission
    @RequiresBluetoothScanPermission
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
    public boolean setScanMode(@ScanMode int mode) {
    public @Nullable Duration getDiscoverableTimeout() {
        if (getState() != STATE_ON) {
            return false;
            return null;
        }
        try {
            mServiceLock.readLock().lock();
            if (mService != null) {
                return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource);
                long timeout = mService.getDiscoverableTimeout(mAttributionSource);
                return (timeout == -1) ? null : Duration.ofSeconds(timeout);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            throw e.rethrowFromSystemServer();
        } finally {
            mServiceLock.readLock().unlock();
        }
        return false;
        return null;
    }

    /** @hide */
    @UnsupportedAppUsage
    /**
     * Set the total time the Bluetooth local adapter will stay discoverable when
     * {@link #setScanMode} is called with {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE} mode.
     * After this timeout, the scan mode will fallback to {@link #SCAN_MODE_CONNECTABLE}.
     * <p>If <code>timeout</code> is set to 0, no timeout will occur and the scan mode will
     * be persisted until a subsequent call to {@link #setScanMode}.
     *
     * @param timeout represents the total duration the local Bluetooth adapter will remain
     *                discoverable, or no timeout if set to 0
     * @return whether the timeout was successfully set
     * @throws IllegalArgumentException if <code>timeout</code> duration in seconds is more
     *         than {@link Integer#MAX_VALUE}
     * @hide
     */
    @SystemApi
    @RequiresBluetoothScanPermission
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
    public int getDiscoverableTimeout() {
    @RequiresPermission(allOf = {
            android.Manifest.permission.BLUETOOTH_SCAN,
            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
    })
    @ScanModeStatusCode
    public int setDiscoverableTimeout(@NonNull Duration timeout) {
        if (getState() != STATE_ON) {
            return -1;
        }
        try {
            mServiceLock.readLock().lock();
            if (mService != null) {
                return mService.getDiscoverableTimeout(mAttributionSource);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
        } finally {
            mServiceLock.readLock().unlock();
            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
        }
        return -1;
    }

    /** @hide */
    @UnsupportedAppUsage
    @RequiresBluetoothScanPermission
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
    public void setDiscoverableTimeout(int timeout) {
        if (getState() != STATE_ON) {
            return;
        if (timeout.toSeconds() > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("Timeout in seconds must be less or equal to "
                    + Integer.MAX_VALUE);
        }
        try {
            mServiceLock.readLock().lock();
            if (mService != null) {
                mService.setDiscoverableTimeout(timeout, mAttributionSource);
                return mService.setDiscoverableTimeout(timeout.toSeconds(), mAttributionSource);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            throw e.rethrowFromSystemServer();
        } finally {
            mServiceLock.readLock().unlock();
        }
        return BluetoothStatusCodes.ERROR_UNKNOWN;
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ public final class BluetoothStatusCodes {

    /**
     * Error code indicating that the API call was initiated by neither the system nor the active
     * Zuser
     * user
     */
    public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2;

+4 −2
Original line number Diff line number Diff line
@@ -587,7 +587,8 @@ public class BluetoothTestUtils extends Assert {

        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
        mContext.registerReceiver(receiver, filter);
        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
        assertEquals(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE),
                BluetoothStatusCodes.SUCCESS);
        boolean success = false;
        try {
            success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
@@ -637,7 +638,8 @@ public class BluetoothTestUtils extends Assert {

        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
        mContext.registerReceiver(receiver, filter);
        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
        assertEquals(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE),
                BluetoothStatusCodes.SUCCESS);
        boolean success = false;
        try {
            success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
Loading