diff options
Diffstat (limited to 'framework/java/android/bluetooth/BluetoothAdapter.java')
| -rw-r--r-- | framework/java/android/bluetooth/BluetoothAdapter.java | 613 |
1 files changed, 290 insertions, 323 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9d391a03fd..4e950e31f8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -30,7 +30,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; -import android.annotation.SystemApi; //import android.app.PropertyInvalidatedCache; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.bluetooth.BluetoothDevice.AddressType; import android.bluetooth.BluetoothDevice.Transport; @@ -53,11 +53,12 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; +import android.os.BluetoothServiceManager; import android.os.Build; import android.os.IBinder; +import android.os.IpcDataCache; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.ServiceManager; import android.sysprop.BluetoothProperties; import android.util.Log; import android.util.Pair; @@ -68,6 +69,7 @@ import com.android.modules.utils.SynchronousResultReceiver; 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; @@ -446,6 +448,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 @@ -736,6 +748,16 @@ public final class BluetoothAdapter { 3; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING; /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "STATE_" }, value = { + STATE_DISCONNECTED, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_DISCONNECTING, + }) + public @interface ConnectionState {} + + /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; private final IBinder mToken; @@ -946,12 +968,18 @@ public final class BluetoothAdapter { /** {@hide} */ public static BluetoothAdapter createAdapter(AttributionSource attributionSource) { - IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); - if (binder != null) { - return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder), - attributionSource); + BluetoothServiceManager manager = + BluetoothFrameworkInitializer.getBluetoothServiceManager(); + if (manager == null) { + Log.e(TAG, "BluetoothServiceManager is null"); + return null; + } + IBluetoothManager service = IBluetoothManager.Stub.asInterface( + manager.getBluetoothManagerServiceRegisterer().get()); + if (service != null) { + return new BluetoothAdapter(service, attributionSource); } else { - Log.e(TAG, "Bluetooth binder is null"); + Log.e(TAG, "Bluetooth service is null"); return null; } } @@ -1002,8 +1030,7 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if address is invalid */ @RequiresNoPermission - public - @NonNull BluetoothDevice getRemoteLeDevice(@NonNull String address, + public @NonNull BluetoothDevice getRemoteLeDevice(@NonNull String address, @AddressType int addressType) { final BluetoothDevice res = new BluetoothDevice(address, addressType); res.setAttributionSource(mAttributionSource); @@ -1219,67 +1246,72 @@ public final class BluetoothAdapter { return false; } - /* - private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state"; + /** + * There are several instances of IpcDataCache used in this class. + * BluetoothCache wraps up the common code. All caches are created with a maximum of + * eight entries, and the key is in the bluetooth module. The name is set to the api. + */ + private static class BluetoothCache<Q, R> extends IpcDataCache<Q, R> { + BluetoothCache(String api, IpcDataCache.QueryHandler query) { + super(8, IpcDataCache.MODULE_BLUETOOTH, api, api, query); + }}; + + /** + * Invalidate a bluetooth cache. This method is just a short-hand wrapper that + * enforces the bluetooth module. + */ + private static void invalidateCache(@NonNull String api) { + IpcDataCache.invalidateCache(IpcDataCache.MODULE_BLUETOOTH, api); + } + + /** + * The binder cache for getState(). + */ + private static final String GET_STATE_API = "getState"; - private final PropertyInvalidatedCache<Void, Integer> mBluetoothGetStateCache = - new PropertyInvalidatedCache<Void, Integer>( - 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(Void query) { - try { - final SynchronousResultReceiver<Integer> recv = - new SynchronousResultReceiver(); - mService.getState(recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()) - .getValue(BluetoothAdapter.STATE_OFF); - } catch (TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - return BluetoothAdapter.STATE_OFF; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + private final IpcDataCache.QueryHandler<Void, Integer> mBluetoothGetStateQuery = + new IpcDataCache.QueryHandler<>() { + @RequiresLegacyBluetoothPermission + @RequiresNoPermission + @Override + public @InternalAdapterState Integer apply(Void query) { + int state = BluetoothAdapter.STATE_OFF; + mServiceLock.readLock().lock(); + try { + if (mService != null) { + final SynchronousResultReceiver<Integer> recv = + new SynchronousResultReceiver(); + mService.getState(recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(state); } - }; - */ + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } finally { + mServiceLock.readLock().unlock(); + } + return state; + }}; + + private final IpcDataCache<Void, Integer> mBluetoothGetStateCache = + new BluetoothCache<Void, Integer>(GET_STATE_API, mBluetoothGetStateQuery); /** @hide */ - /* @RequiresNoPermission public void disableBluetoothGetStateCache() { - mBluetoothGetStateCache.disableLocal(); + mBluetoothGetStateCache.disableForCurrentProcess(); } - */ /** @hide */ - /* public static void invalidateBluetoothGetStateCache() { - PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY); + invalidateCache(GET_STATE_API); } - */ /** * Fetch the current bluetooth state. If the service is down, return * OFF. */ private @InternalAdapterState int getStateInternal() { - int state = BluetoothAdapter.STATE_OFF; - try { - mServiceLock.readLock().lock(); - if (mService != null) { - //state = mBluetoothGetStateCache.query(null); - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - //return mBluetoothBondCache.query(this); - mService.getState(recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(state); - } - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); - } - return state; + return mBluetoothGetStateCache.query(null); } /** @@ -1374,7 +1406,20 @@ public final class BluetoothAdapter { * such as Airplane mode, or the adapter is already turned on. * * @return true to indicate adapter startup has begun, or false on immediate error + * + * @deprecated Starting with {@link android.os.Build.VERSION_CODES#TIRAMISU}, applications + * are not allowed to enable/disable Bluetooth. + * <b>Compatibility Note:</b> For applications targeting + * {@link android.os.Build.VERSION_CODES#TIRAMISU} or above, this API will always fail and return + * {@code false}. If apps are targeting an older SDK ({@link android.os.Build.VERSION_CODES#S} + * or below), they can continue to use this API. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ + @Deprecated @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @@ -1413,7 +1458,20 @@ public final class BluetoothAdapter { * such as the adapter already being turned off. * * @return true to indicate adapter shutdown has begun, or false on immediate error + * + * @deprecated Starting with {@link android.os.Build.VERSION_CODES#TIRAMISU}, applications + * are not allowed to enable/disable Bluetooth. + * <b>Compatibility Note:</b> For applications targeting + * {@link android.os.Build.VERSION_CODES#TIRAMISU} or above, this API will always fail and return + * {@code false}. If apps are targeting an older SDK ({@link android.os.Build.VERSION_CODES#S} + * or below), they can continue to use this API. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ + @Deprecated @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @@ -1859,8 +1917,10 @@ public final class BluetoothAdapter { mService.getScanMode(mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(SCAN_MODE_NONE); } - } catch (RemoteException | TimeoutException e) { + } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } finally { mServiceLock.readLock().unlock(); } @@ -1868,151 +1928,124 @@ 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); - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - mService.setScanMode(mode, durationSeconds, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); + final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); + mService.setScanMode(mode, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()) + .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); } - } catch (RemoteException | TimeoutException e) { + } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } 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"); + } catch (RemoteException e) { + 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) { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); - } - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** @hide */ - @UnsupportedAppUsage @RequiresBluetoothScanPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public int getDiscoverableTimeout() { + public @Nullable Duration getDiscoverableTimeout() { if (getState() != STATE_ON) { - return -1; + return null; } try { mServiceLock.readLock().lock(); if (mService != null) { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); + final SynchronousResultReceiver<Long> recv = new SynchronousResultReceiver(); mService.getDiscoverableTimeout(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + long timeout = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue((long) -1); + return (timeout == -1) ? null : Duration.ofSeconds(timeout); } - } catch (RemoteException | TimeoutException e) { + } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } finally { mServiceLock.readLock().unlock(); } - return -1; + 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 void setDiscoverableTimeout(int timeout) { + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_SCAN, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + @ScanModeStatusCode + public int setDiscoverableTimeout(@NonNull Duration timeout) { if (getState() != STATE_ON) { - return; + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; + } + 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) { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - mService.setDiscoverableTimeout(timeout, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); + mService.setDiscoverableTimeout(timeout.toSeconds(), mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()) + .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); } - } catch (RemoteException | TimeoutException e) { + } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } finally { mServiceLock.readLock().unlock(); } + return BluetoothStatusCodes.ERROR_UNKNOWN; } /** @@ -2374,48 +2407,42 @@ public final class BluetoothAdapter { } } - /* - private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY = - "cache_key.bluetooth.is_offloaded_filtering_supported"; - private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache = - new PropertyInvalidatedCache<Void, Boolean>( - 8, BLUETOOTH_FILTERING_CACHE_PROPERTY) { - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Boolean recompute(Void query) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - final SynchronousResultReceiver<Boolean> recv = - new SynchronousResultReceiver(); - mService.isOffloadedFilteringSupported(recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); - } - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - + private final IpcDataCache.QueryHandler<Void, Boolean> mBluetoothFilteringQuery = + new IpcDataCache.QueryHandler<>() { + @RequiresLegacyBluetoothPermission + @RequiresNoPermission + @Override + public Boolean apply(Void query) { + mServiceLock.readLock().lock(); + try { + if (mService != null) { + final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); + mService.isOffloadedFilteringSupported(recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); } - }; - */ + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + }}; + + private static final String FILTERING_API = "isOffloadedFilteringSupported"; + + private final IpcDataCache<Void, Boolean> mBluetoothFilteringCache = + new BluetoothCache<Void, Boolean>(FILTERING_API, mBluetoothFilteringQuery); /** @hide */ - /* @RequiresNoPermission public void disableIsOffloadedFilteringSupportedCache() { - mBluetoothFilteringCache.disableLocal(); + mBluetoothFilteringCache.disableForCurrentProcess(); } - */ /** @hide */ - /* public static void invalidateIsOffloadedFilteringSupportedCache() { - PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY); + invalidateCache(FILTERING_API); } - */ /** * Return true if offloaded filters are supported @@ -2428,22 +2455,7 @@ public final class BluetoothAdapter { if (!getLeAccess()) { return false; } - //return mBluetoothFilteringCache.query(null); - try { - mServiceLock.readLock().lock(); - if (mService != null) { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - mService.isOffloadedFilteringSupported(recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); - } - } catch (TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } catch (RemoteException e) { - Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; + return mBluetoothFilteringCache.query(null); } /** @@ -2588,7 +2600,7 @@ public final class BluetoothAdapter { /** * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio feature is - * supported, returns {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not + * supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not * supported, or an error code. * * @return whether the LE audio is supported @@ -2622,8 +2634,8 @@ public final class BluetoothAdapter { /** * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio broadcast source - * feature is supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the - * feature is not supported, or an error code. + * feature is supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature + * is not supported, or an error code. * * @return whether the LE audio broadcast source is supported * @throws IllegalStateException if the bluetooth service is null @@ -2657,8 +2669,8 @@ public final class BluetoothAdapter { /** * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio broadcast assistant - * feature is supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the - * feature is not supported, or an error code. + * feature is supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is + * not supported, or an error code. * * @return whether the LE audio broadcast assistent is supported * @throws IllegalStateException if the bluetooth service is null @@ -2945,47 +2957,46 @@ public final class BluetoothAdapter { return supportedProfiles; } - /* - private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY = - "cache_key.bluetooth.get_adapter_connection_state"; - private final PropertyInvalidatedCache<Void, Integer> - mBluetoothGetAdapterConnectionStateCache = - new PropertyInvalidatedCache<Void, Integer> ( - 8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) { - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(Void query) { - try { - final SynchronousResultReceiver<Integer> recv = - new SynchronousResultReceiver(); - mService.getAdapterConnectionState(recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()) - .getValue(BluetoothAdapter.STATE_DISCONNECTED); - } catch (TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } - return BluetoothAdapter.STATE_DISCONNECTED; + private final IpcDataCache.QueryHandler<Void, Integer> mBluetoothGetAdapterQuery = + new IpcDataCache.QueryHandler<>() { + @RequiresLegacyBluetoothPermission + @RequiresNoPermission + @Override + public Integer apply(Void query) { + mServiceLock.readLock().lock(); + try { + if (mService != null) { + final SynchronousResultReceiver<Integer> recv = + new SynchronousResultReceiver(); + mService.getAdapterConnectionState(recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()) + .getValue(STATE_DISCONNECTED); } - }; - */ + } catch (TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } catch (RemoteException e) { + Log.e(TAG, "failed to getConnectionState, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothAdapter.STATE_DISCONNECTED; + }}; + + private static final String GET_CONNECTION_API = "getAdapterConnectionState"; + private final IpcDataCache<Void, Integer> + mBluetoothGetAdapterConnectionStateCache = + new BluetoothCache<Void, Integer>(GET_CONNECTION_API, mBluetoothGetAdapterQuery); /** @hide */ - /* @RequiresNoPermission public void disableGetAdapterConnectionStateCache() { - mBluetoothGetAdapterConnectionStateCache.disableLocal(); + mBluetoothGetAdapterConnectionStateCache.disableForCurrentProcess(); } - */ /** @hide */ - /* public static void invalidateGetAdapterConnectionStateCache() { - PropertyInvalidatedCache.invalidateCache( - BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY); + invalidateCache(GET_CONNECTION_API); } - */ /** * Get the current connection state of the local Bluetooth adapter. @@ -2995,82 +3006,60 @@ public final class BluetoothAdapter { * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED} * intent to get the connection state of the adapter. * - * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED}, {@link - * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED} + * @return the connection state * @hide */ @SystemApi @RequiresNoPermission - public int getConnectionState() { + public @ConnectionState int getConnectionState() { if (getState() != STATE_ON) { return BluetoothAdapter.STATE_DISCONNECTED; } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - mService.getAdapterConnectionState(recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(STATE_DISCONNECTED); - } - //return mBluetoothGetAdapterConnectionStateCache.query(null); - } catch (TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } catch (RemoteException e) { - Log.e(TAG, "failed to getConnectionState, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothAdapter.STATE_DISCONNECTED; + return mBluetoothGetAdapterConnectionStateCache.query(null); } - /* - private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY = - "cache_key.bluetooth.get_profile_connection_state"; - private final PropertyInvalidatedCache<Integer, Integer> - mGetProfileConnectionStateCache = - new PropertyInvalidatedCache<Integer, Integer>( - 8, BLUETOOTH_PROFILE_CACHE_PROPERTY) { - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(Integer query) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - final SynchronousResultReceiver<Integer> recv = - new SynchronousResultReceiver(); - mService.getProfileConnectionState(query, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()) - .getValue(BluetoothProfile.STATE_DISCONNECTED); - } - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothProfile.STATE_DISCONNECTED; - } - @Override - public String queryToString(Integer query) { - return String.format("getProfileConnectionState(profile=\"%d\")", - query); + private final IpcDataCache.QueryHandler<Integer, Integer> mBluetoothProfileQuery = + new IpcDataCache.QueryHandler<>() { + @RequiresNoPermission + @Override + public Integer apply(Integer query) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); + mService.getProfileConnectionState(query, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()) + .getValue(BluetoothProfile.STATE_DISCONNECTED); } - }; - */ + } catch (TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } catch (RemoteException e) { + Log.e(TAG, "failed to getProfileConnectionState, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothProfile.STATE_DISCONNECTED; + }}; - /** @hide */ - /* + private static final String PROFILE_API = "getProfileConnectionState"; + private final IpcDataCache<Integer, Integer> + mGetProfileConnectionStateCache = + new BluetoothCache<Integer, Integer>(PROFILE_API, mBluetoothProfileQuery); + + /** + * @hide + */ @RequiresNoPermission public void disableGetProfileConnectionStateCache() { - mGetProfileConnectionStateCache.disableLocal(); + mGetProfileConnectionStateCache.disableForCurrentProcess(); } - */ - /** @hide */ - /* + /** + * @hide + */ public static void invalidateGetProfileConnectionStateCache() { - PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY); + invalidateCache(PROFILE_API); } - */ /** * Get the current connection state of a profile. @@ -3078,36 +3067,17 @@ public final class BluetoothAdapter { * is connected to any remote device for a specific profile. * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. * - * <p> Return value can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED}, - * {@link BluetoothProfile#STATE_CONNECTING}, - * {@link BluetoothProfile#STATE_CONNECTED}, - * {@link BluetoothProfile#STATE_DISCONNECTING} + * <p> Return the profile connection state */ @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SuppressLint("AndroidFrameworkRequiresPermission") - public int getProfileConnectionState(int profile) { + public @ConnectionState int getProfileConnectionState(int profile) { if (getState() != STATE_ON) { return BluetoothProfile.STATE_DISCONNECTED; } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - mService.getProfileConnectionState(profile, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(STATE_DISCONNECTED); - } - //return mGetProfileConnectionStateCache.query(new Integer(profile)); - } catch (TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } catch (RemoteException e) { - Log.e(TAG, "failed to getProfileConnectionState, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothProfile.STATE_DISCONNECTED; + return mGetProfileConnectionStateCache.query(profile); } /** @@ -4300,7 +4270,6 @@ public final class BluetoothAdapter { * @hide */ @RequiresNoPermission - @SystemApi public boolean registerServiceLifecycleCallback(@NonNull ServiceLifecycleCallback callback) { return getBluetoothService(callback.mRemote) != null; } @@ -4311,7 +4280,6 @@ public final class BluetoothAdapter { * @hide */ @RequiresNoPermission - @SystemApi public void unregisterServiceLifecycleCallback(@NonNull ServiceLifecycleCallback callback) { removeServiceStateCallback(callback.mRemote); } @@ -4321,7 +4289,6 @@ public final class BluetoothAdapter { * * @hide */ - @SystemApi public abstract static class ServiceLifecycleCallback { /** Called when the bluetooth stack is up */ @@ -4934,7 +4901,7 @@ public final class BluetoothAdapter { * Returns human-readable strings corresponding to {@link DisconnectReason}. */ @NonNull - public static String disconnectReasonText(@DisconnectReason int reason) { + public static String disconnectReasonToString(@DisconnectReason int reason) { switch (reason) { case BluetoothStatusCodes.ERROR_UNKNOWN: return "Reason unknown"; |
