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

Commit e2763d87 authored by Etienne Ruffieux's avatar Etienne Ruffieux
Browse files

Refactor Bluetooth scan mode APIs

Removed setScanMode(int, long), setScanMode(int mode) is
now a SystemApi. Made getDiscoverableTimeout a public API.
setDiscoverableTimeout is now SystemApi and takes
java.time.Duration as parameter.

Tag: #feature
Bug: 195150096
Test: Manual
Change-Id: I824b24464987e1db87efc193d30452a6d9d0411e
parent b5a46280
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8718,6 +8718,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
@@ -1999,6 +1999,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
@@ -69,6 +69,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;
@@ -402,6 +403,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
@@ -1618,7 +1629,7 @@ public final class BluetoothAdapter {
                return mService.getScanMode(mAttributionSource);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            throw e.rethrowFromSystemServer();
        } finally {
            mServiceLock.readLock().unlock();
        }
@@ -1626,143 +1637,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