diff options
Diffstat (limited to 'framework/java/android')
10 files changed, 858 insertions, 469 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 46d19217dd..de3f570d96 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,12 +53,13 @@ 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.ResultReceiver; -import android.os.ServiceManager; import android.sysprop.BluetoothProperties; import android.util.Log; import android.util.Pair; @@ -69,6 +70,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; @@ -188,6 +190,16 @@ public final class BluetoothAdapter { STATE_BLE_TURNING_OFF }) @Retention(RetentionPolicy.SOURCE) + public @interface InternalAdapterState {} + + /** @hide */ + @IntDef(prefix = { "STATE_" }, value = { + STATE_OFF, + STATE_TURNING_ON, + STATE_ON, + STATE_TURNING_OFF, + }) + @Retention(RetentionPolicy.SOURCE) public @interface AdapterState {} /** @@ -268,14 +280,14 @@ public final class BluetoothAdapter { public @interface RfcommListenerResult {} /** - * Human-readable string helper for AdapterState + * Human-readable string helper for AdapterState and InternalAdapterState * * @hide */ @SystemApi @RequiresNoPermission @NonNull - public static String nameForState(@AdapterState int state) { + public static String nameForState(@InternalAdapterState int state) { switch (state) { case STATE_OFF: return "OFF"; @@ -437,6 +449,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 @@ -834,12 +856,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; } } @@ -890,8 +918,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); @@ -1107,68 +1134,72 @@ public final class BluetoothAdapter { return false; } - /* - private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state"; - - 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(); - } - } - }; + /** + * 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 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. */ - @AdapterState - private 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; + private @InternalAdapterState int getStateInternal() { + return mBluetoothGetStateCache.query(null); } /** @@ -1183,8 +1214,7 @@ public final class BluetoothAdapter { */ @RequiresLegacyBluetoothPermission @RequiresNoPermission - @AdapterState - public int getState() { + public @AdapterState int getState() { int state = getStateInternal(); // Consider all internal states as OFF @@ -1220,10 +1250,9 @@ public final class BluetoothAdapter { */ @RequiresLegacyBluetoothPermission @RequiresNoPermission - @AdapterState @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " + "whether you can use BLE & BT classic.") - public int getLeState() { + public @InternalAdapterState int getLeState() { int state = getStateInternal(); if (VDBG) { @@ -1265,7 +1294,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) @@ -1304,7 +1346,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) @@ -1725,8 +1780,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(); } @@ -1734,151 +1791,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; } /** @@ -2240,48 +2270,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 @@ -2294,22 +2318,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); } /** @@ -2454,7 +2463,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 @@ -2488,8 +2497,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 @@ -2523,8 +2532,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 @@ -2804,47 +2813,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. @@ -2864,72 +2872,51 @@ public final class BluetoothAdapter { 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. @@ -2951,22 +2938,7 @@ public final class BluetoothAdapter { 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); } /** @@ -4159,7 +4131,6 @@ public final class BluetoothAdapter { * @hide */ @RequiresNoPermission - @SystemApi public boolean registerServiceLifecycleCallback(@NonNull ServiceLifecycleCallback callback) { return getBluetoothService(callback.mRemote) != null; } @@ -4170,7 +4141,6 @@ public final class BluetoothAdapter { * @hide */ @RequiresNoPermission - @SystemApi public void unregisterServiceLifecycleCallback(@NonNull ServiceLifecycleCallback callback) { removeServiceStateCallback(callback.mRemote); } @@ -4180,7 +4150,6 @@ public final class BluetoothAdapter { * * @hide */ - @SystemApi public abstract static class ServiceLifecycleCallback { /** Called when the bluetooth stack is up */ @@ -4793,7 +4762,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"; diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index b3a8601cbe..146b627147 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -314,21 +315,26 @@ public final class BluetoothClass implements Parcelable { return Arrays.copyOfRange(bytes, 1, bytes.length); } - /** @hide */ - @UnsupportedAppUsage public static final int PROFILE_HEADSET = 0; - /** @hide */ - @UnsupportedAppUsage + public static final int PROFILE_A2DP = 1; + /** @hide */ + @SystemApi public static final int PROFILE_OPP = 2; - /** @hide */ + public static final int PROFILE_HID = 3; + /** @hide */ + @SystemApi public static final int PROFILE_PANU = 4; + /** @hide */ + @SystemApi public static final int PROFILE_NAP = 5; + /** @hide */ + @SystemApi public static final int PROFILE_A2DP_SINK = 6; /** @@ -337,11 +343,9 @@ public final class BluetoothClass implements Parcelable { * given class bits might support specified profile. It is not accurate for all * devices. It tries to err on the side of false positives. * - * @param profile The profile to be checked - * @return True if this device might support specified profile. - * @hide + * @param profile the profile to be checked + * @return whether this device supports specified profile */ - @UnsupportedAppUsage public boolean doesClassMatch(int profile) { if (profile == PROFILE_A2DP) { if (hasService(Service.RENDER)) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 8aef778b31..978c8ed4ac 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -25,7 +25,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.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; @@ -37,6 +37,7 @@ import android.content.AttributionSource; import android.content.Context; import android.os.Build; import android.os.Handler; +import android.os.IpcDataCache; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -1435,17 +1436,21 @@ public final class BluetoothDevice implements Parcelable, Attributable { android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public @Nullable String getIdentityAddress() { + if (DBG) log("getIdentityAddress()"); final IBluetooth service = sService; + final String defaultValue = null; if (service == null) { Log.e(TAG, "BT not enabled. Cannot get identity address"); - return null; - } - try { - return service.getIdentityAddress(mAddress); - } catch (RemoteException e) { - Log.e(TAG, "", e); + } else { + try { + final SynchronousResultReceiver<String> recv = new SynchronousResultReceiver(); + service.getIdentityAddress(mAddress, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } } - return null; + return defaultValue; } /** @@ -1829,41 +1834,71 @@ public final class BluetoothDevice implements Parcelable, Attributable { return defaultValue; } - /* - private static final String BLUETOOTH_BONDING_CACHE_PROPERTY = - "cache_key.bluetooth.get_bond_state"; - private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache = - new PropertyInvalidatedCache<BluetoothDevice, Integer>( - 8, BLUETOOTH_BONDING_CACHE_PROPERTY) { + /** + * 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); + } + + private final + IpcDataCache.QueryHandler<BluetoothDevice, Integer> mBluetoothBondQuery = + new IpcDataCache.QueryHandler<>() { + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(BluetoothDevice query) { - try { - final SynchronousResultReceiver<Integer> recv = - new SynchronousResultReceiver(); - sService.getBondState(query, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); + public Integer apply(BluetoothDevice query) { + if (DBG) log("getBondState() uncached"); + final IBluetooth service = sService; + final int defaultValue = BOND_NONE; + if (service == null) { + Log.e(TAG, "BT not enabled. Cannot get bond state"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else { + try { + final SynchronousResultReceiver<Integer> recv = + new SynchronousResultReceiver(); + service.getBondState(BluetoothDevice.this, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()) + .getValue(defaultValue); + } catch (TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + + Log.getStackTraceString(new Throwable())); + } catch (RemoteException e) { + Log.e(TAG, "failed to ", e); + e.rethrowFromSystemServer(); + } } return defaultValue; } }; - */ + + private static final String GET_BOND_STATE_API = "getBondState"; + + private final BluetoothCache<BluetoothDevice, Integer> mBluetoothBondCache = + new BluetoothCache<BluetoothDevice, Integer>(GET_BOND_STATE_API, mBluetoothBondQuery); /** @hide */ - /* public void disableBluetoothGetBondStateCache() { - mBluetoothBondCache.disableLocal(); - } */ + public void disableBluetoothGetBondStateCache() { + mBluetoothBondCache.disableForCurrentProcess(); + } /** @hide */ - /* public static void invalidateBluetoothGetBondStateCache() { - PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY); + invalidateCache(GET_BOND_STATE_API); } - */ /** * Get the bond state of the remote device. @@ -1880,25 +1915,7 @@ public final class BluetoothDevice implements Parcelable, Attributable { @SuppressLint("AndroidFrameworkRequiresPermission") public int getBondState() { if (DBG) log("getBondState()"); - final IBluetooth service = sService; - final int defaultValue = BOND_NONE; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot get bond state"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - //return mBluetoothBondCache.query(this); - service.getBondState(this, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } catch (RemoteException e) { - Log.e(TAG, "failed to ", e); - e.rethrowFromSystemServer(); - } - } - return defaultValue; + return mBluetoothBondCache.query(null); } /** diff --git a/framework/java/android/bluetooth/BluetoothFrameworkInitializer.java b/framework/java/android/bluetooth/BluetoothFrameworkInitializer.java new file mode 100644 index 0000000000..dc8b862539 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothFrameworkInitializer.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.bluetooth; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.SystemApi.Client; +import android.app.SystemServiceRegistry; +import android.content.Context; +import android.os.BluetoothServiceManager; + +import java.util.function.Consumer; + +/** + * Class for performing registration for Bluetooth service. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class BluetoothFrameworkInitializer { + private BluetoothFrameworkInitializer() {} + + private static volatile BluetoothServiceManager sBluetoothServiceManager; + private static volatile Consumer<Context> sBinderCallsStatsInitializer; + + /** + * Sets an instance of {@link BluetoothServiceManager} that allows + * the bluetooth mainline module to register/obtain bluetooth binder services. This is called + * by the platform during the system initialization. + * + * @param bluetoothServiceManager instance of {@link BluetoothServiceManager} that allows + * the bluetooth mainline module to register/obtain bluetoothd binder services. + */ + public static void setBluetoothServiceManager( + @NonNull BluetoothServiceManager bluetoothServiceManager) { + if (sBluetoothServiceManager != null) { + throw new IllegalStateException("setBluetoothServiceManager called twice!"); + } + + if (bluetoothServiceManager == null) { + throw new IllegalArgumentException("bluetoothServiceManager must not be null"); + } + + sBluetoothServiceManager = bluetoothServiceManager; + } + + /** @hide */ + public static BluetoothServiceManager getBluetoothServiceManager() { + return sBluetoothServiceManager; + } + + /** + * Called by {@link ActivityThread}'s static initializer to set the callback enabling Bluetooth + * {@link BinderCallsStats} registeration. + * + * @param binderCallsStatsConsumer called by bluetooth service to create a new binder calls + * stats observer + */ + public static void setBinderCallsStatsInitializer( + @NonNull Consumer<Context> binderCallsStatsConsumer) { + if (sBinderCallsStatsInitializer != null) { + throw new IllegalStateException("setBinderCallsStatsInitializer called twice!"); + } + + if (binderCallsStatsConsumer == null) { + throw new IllegalArgumentException("binderCallsStatsConsumer must not be null"); + } + + sBinderCallsStatsInitializer = binderCallsStatsConsumer; + } + + /** @hide */ + public static void initializeBinderCallsStats(Context context) { + if (sBinderCallsStatsInitializer == null) { + throw new IllegalStateException("sBinderCallsStatsInitializer has not been set"); + } + sBinderCallsStatsInitializer.accept(context); + } + + /** + * Called by {@link SystemServiceRegistry}'s static initializer and registers BT service + * to {@link Context}, so that {@link Context#getSystemService} can return them. + * + * @throws IllegalStateException if this is called from anywhere besides + * {@link SystemServiceRegistry} + */ + public static void registerServiceWrappers() { + SystemServiceRegistry.registerContextAwareService(Context.BLUETOOTH_SERVICE, + BluetoothManager.class, context -> new BluetoothManager(context)); + } +} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 9df07724cc..c8d0a50eee 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -630,33 +630,6 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or - * {@link BluetoothProfile#PRIORITY_OFF} - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)} - * @removed - */ - @Deprecated - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** * Set connection policy of the profile * * <p> The device should already be paired. @@ -915,6 +888,7 @@ public final class BluetoothHeadset implements BluetoothProfile { return defaultValue; } + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { @@ -1105,6 +1079,7 @@ public final class BluetoothHeadset implements BluetoothProfile { BluetoothStatusCodes.ERROR_UNKNOWN, BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, BluetoothStatusCodes.ERROR_TIMEOUT, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED, BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES, BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE, @@ -1119,11 +1094,11 @@ public final class BluetoothHeadset implements BluetoothProfile { * can be identified with {@link BluetoothAdapter#getActiveDevices(int)}. * <p> * If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent - * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted twice. First with {@link #EXTRA_STATE} - * set to {@link #STATE_AUDIO_CONNECTING}. This will be followed by a broadcast with - * {@link #EXTRA_STATE} set to either {@link #STATE_AUDIO_CONNECTED} if the audio connection is - * established or {@link #STATE_AUDIO_DISCONNECTED} if there was a failure in establishing the - * audio connection. + * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted twice. First with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. This will be followed by a + * broadcast with {@link #EXTRA_STATE} set to either {@link #STATE_AUDIO_CONNECTED} if the audio + * connection is established or {@link #STATE_AUDIO_DISCONNECTED} if there was a failure in + * establishing the audio connection. * * @return whether the connection was successfully initiated or an error code on failure * @hide @@ -1141,6 +1116,7 @@ public final class BluetoothHeadset implements BluetoothProfile { if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; } else if (isEnabled()) { try { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); @@ -1153,8 +1129,9 @@ public final class BluetoothHeadset implements BluetoothProfile { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); return BluetoothStatusCodes.ERROR_TIMEOUT; } + } else { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } - return defaultValue; } /** @hide */ @@ -1164,6 +1141,7 @@ public final class BluetoothHeadset implements BluetoothProfile { BluetoothStatusCodes.ERROR_UNKNOWN, BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, BluetoothStatusCodes.ERROR_TIMEOUT, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED, BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED }) @@ -1193,6 +1171,7 @@ public final class BluetoothHeadset implements BluetoothProfile { if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; } else if (isEnabled()) { try { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); @@ -1205,8 +1184,9 @@ public final class BluetoothHeadset implements BluetoothProfile { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); return BluetoothStatusCodes.ERROR_TIMEOUT; } + } else { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } - return defaultValue; } /** diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 87bd76114b..d47e41a0b1 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -17,7 +17,9 @@ package android.bluetooth; import static android.bluetooth.BluetoothUtils.getSyncTimeout; +import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -31,6 +33,8 @@ import android.content.Context; import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; @@ -56,6 +60,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose private static final boolean DBG = true; private static final boolean VDBG = false; private final CloseGuard mCloseGuard; + /** * Intent used to broadcast the change in connection state of the HFP Client profile. * @@ -82,6 +87,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; + /** * Intent sent whenever audio state changes. * @@ -99,13 +105,13 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose * * @hide */ - @SystemApi @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SuppressLint("ActionValue") public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; + /** * Intent sending updates of the Audio Gateway state. * Each extra is being sent only when value it @@ -126,6 +132,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AG_EVENT = "android.bluetooth.headsetclient.profile.action.AG_EVENT"; + /** * Intent sent whenever state of a call changes. * @@ -141,6 +148,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CALL_CHANGED = "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED"; + /** * Intent that notifies about the result of the last issued action. * Please note that not every action results in explicit action result code being sent. @@ -155,6 +163,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_RESULT = "android.bluetooth.headsetclient.profile.action.RESULT"; + /** * Intent that notifies about vendor specific event arrival. Events not defined in * HFP spec will be matched with supported vendor event list and this intent will @@ -169,6 +178,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT = "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT"; + /** * Intent that notifies about the number attached to the last voice tag * recorded on AG. @@ -184,21 +194,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LAST_VTAG = "android.bluetooth.headsetclient.profile.action.LAST_VTAG"; + /** * @hide */ - @SystemApi public static final int STATE_AUDIO_DISCONNECTED = 0; + /** * @hide */ - @SystemApi public static final int STATE_AUDIO_CONNECTING = 1; + /** * @hide */ - @SystemApi public static final int STATE_AUDIO_CONNECTED = 2; + /** * Extra with information if connected audio is WBS. * <p>Possible values: <code>true</code>, @@ -208,6 +219,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AUDIO_WBS = "android.bluetooth.headsetclient.extra.AUDIO_WBS"; + /** * Extra for AG_EVENT indicates network status. * <p>Value: 0 - network unavailable, @@ -217,6 +229,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_NETWORK_STATUS = "android.bluetooth.headsetclient.extra.NETWORK_STATUS"; + /** * Extra for AG_EVENT intent indicates network signal strength. * <p>Value: <code>Integer</code> representing signal strength.</p> @@ -225,6 +238,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_NETWORK_SIGNAL_STRENGTH = "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH"; + /** * Extra for AG_EVENT intent indicates roaming state. * <p>Value: 0 - no roaming @@ -234,6 +248,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_NETWORK_ROAMING = "android.bluetooth.headsetclient.extra.NETWORK_ROAMING"; + /** * Extra for AG_EVENT intent indicates the battery level. * <p>Value: <code>Integer</code> representing signal strength.</p> @@ -242,6 +257,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_BATTERY_LEVEL = "android.bluetooth.headsetclient.extra.BATTERY_LEVEL"; + /** * Extra for AG_EVENT intent indicates operator name. * <p>Value: <code>String</code> representing operator name.</p> @@ -250,6 +266,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_OPERATOR_NAME = "android.bluetooth.headsetclient.extra.OPERATOR_NAME"; + /** * Extra for AG_EVENT intent indicates voice recognition state. * <p>Value: @@ -260,6 +277,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_VOICE_RECOGNITION = "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION"; + /** * Extra for AG_EVENT intent indicates in band ring state. * <p>Value: @@ -270,6 +288,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_IN_BAND_RING = "android.bluetooth.headsetclient.extra.IN_BAND_RING"; + /** * Extra for AG_EVENT intent indicates subscriber info. * <p>Value: <code>String</code> containing subscriber information.</p> @@ -278,6 +297,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_SUBSCRIBER_INFO = "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO"; + /** * Extra for AG_CALL_CHANGED intent indicates the * {@link BluetoothHeadsetClientCall} object that has changed. @@ -286,6 +306,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_CALL = "android.bluetooth.headsetclient.extra.CALL"; + /** * Extra for ACTION_LAST_VTAG intent. * <p>Value: <code>String</code> representing phone number @@ -295,6 +316,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_NUMBER = "android.bluetooth.headsetclient.extra.NUMBER"; + /** * Extra for ACTION_RESULT intent that shows the result code of * last issued action. @@ -312,6 +334,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_RESULT_CODE = "android.bluetooth.headsetclient.extra.RESULT_CODE"; + /** * Extra for ACTION_RESULT intent that shows the extended result code of * last issued action. @@ -321,6 +344,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_CME_CODE = "android.bluetooth.headsetclient.extra.CME_CODE"; + /** * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that * indicates vendor ID. @@ -329,7 +353,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_VENDOR_ID = "android.bluetooth.headsetclient.extra.VENDOR_ID"; - /** + + /** * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that * indicates vendor event code. * @@ -337,7 +362,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_VENDOR_EVENT_CODE = "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE"; - /** + + /** * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that * contains full vendor event including event code and full arguments. * @@ -345,6 +371,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_VENDOR_EVENT_FULL_ARGS = "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS"; + /* Extras for AG_FEATURES, extras type is boolean */ // TODO verify if all of those are actually useful /** @@ -354,6 +381,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_3WAY_CALLING = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; + /** * AG feature: voice recognition. * @@ -361,6 +389,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_VOICE_RECOGNITION = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; + /** * AG feature: fetching phone number for voice tagging procedure. * @@ -368,6 +397,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; + /** * AG feature: ability to reject incoming call. * @@ -375,6 +405,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_REJECT_CALL = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; + /** * AG feature: enhanced call handling (terminate specific call, private consultation). * @@ -382,6 +413,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_ECC = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC"; + /** * AG feature: response and hold. * @@ -389,6 +421,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; + /** * AG call handling feature: accept held or waiting call in three way calling scenarios. * @@ -396,6 +429,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; + /** * AG call handling feature: release held or waiting call in three way calling scenarios. * @@ -403,6 +437,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; + /** * AG call handling feature: release active call and accept held or waiting call in three way * calling scenarios. @@ -411,6 +446,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; + /** * AG call handling feature: merge two calls, held and active - multi party conference mode. * @@ -418,6 +454,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_MERGE = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE"; + /** * AG call handling feature: merge calls and disconnect from multi party * conversation leaving peers connected to each other. @@ -428,152 +465,189 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose */ public static final String EXTRA_AG_FEATURE_MERGE_AND_DETACH = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; + /* Action result codes */ /** * @hide */ public static final int ACTION_RESULT_OK = 0; + /** * @hide */ public static final int ACTION_RESULT_ERROR = 1; + /** * @hide */ public static final int ACTION_RESULT_ERROR_NO_CARRIER = 2; + /** * @hide */ public static final int ACTION_RESULT_ERROR_BUSY = 3; + /** * @hide */ public static final int ACTION_RESULT_ERROR_NO_ANSWER = 4; + /** * @hide */ public static final int ACTION_RESULT_ERROR_DELAYED = 5; + /** * @hide */ public static final int ACTION_RESULT_ERROR_BLACKLISTED = 6; + /** * @hide */ public static final int ACTION_RESULT_ERROR_CME = 7; + /* Detailed CME error codes */ /** * @hide */ public static final int CME_PHONE_FAILURE = 0; + /** * @hide */ public static final int CME_NO_CONNECTION_TO_PHONE = 1; + /** * @hide */ public static final int CME_OPERATION_NOT_ALLOWED = 3; + /** * @hide */ public static final int CME_OPERATION_NOT_SUPPORTED = 4; + /** * @hide */ public static final int CME_PHSIM_PIN_REQUIRED = 5; + /** * @hide */ public static final int CME_PHFSIM_PIN_REQUIRED = 6; + /** * @hide */ public static final int CME_PHFSIM_PUK_REQUIRED = 7; + /** * @hide */ public static final int CME_SIM_NOT_INSERTED = 10; + /** * @hide */ public static final int CME_SIM_PIN_REQUIRED = 11; + /** * @hide */ public static final int CME_SIM_PUK_REQUIRED = 12; + /** * @hide */ public static final int CME_SIM_FAILURE = 13; + /** * @hide */ public static final int CME_SIM_BUSY = 14; + /** * @hide */ public static final int CME_SIM_WRONG = 15; + /** * @hide */ public static final int CME_INCORRECT_PASSWORD = 16; + /** * @hide */ public static final int CME_SIM_PIN2_REQUIRED = 17; + /** * @hide */ public static final int CME_SIM_PUK2_REQUIRED = 18; + /** * @hide */ public static final int CME_MEMORY_FULL = 20; + /** * @hide */ public static final int CME_INVALID_INDEX = 21; + /** * @hide */ public static final int CME_NOT_FOUND = 22; + /** * @hide */ public static final int CME_MEMORY_FAILURE = 23; + /** * @hide */ public static final int CME_TEXT_STRING_TOO_LONG = 24; + /** * @hide */ public static final int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; + /** * @hide */ public static final int CME_DIAL_STRING_TOO_LONG = 26; + /** * @hide */ public static final int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; + /** * @hide */ public static final int CME_NO_NETWORK_SERVICE = 30; + /** * @hide */ public static final int CME_NETWORK_TIMEOUT = 31; + /** * @hide */ public static final int CME_EMERGENCY_SERVICE_ONLY = 32; + /** * @hide */ public static final int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; + /** * @hide */ @@ -582,63 +656,78 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose * @hide */ public static final int CME_SIP_RESPONSE_CODE = 35; + /** * @hide */ public static final int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; + /** * @hide */ public static final int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; + /** * @hide */ public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; + /** * @hide */ public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; + /** * @hide */ public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; + /** * @hide */ public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; + /** * @hide */ public static final int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; + /** * @hide */ public static final int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; + /** * @hide */ public static final int CME_HIDDEN_KEY_REQUIRED = 48; + /** * @hide */ public static final int CME_EAP_NOT_SUPPORTED = 49; + /** * @hide */ public static final int CME_INCORRECT_PARAMETERS = 50; + /* Action policy for other calls when accepting call */ /** * @hide */ public static final int CALL_ACCEPT_NONE = 0; + /** * @hide */ public static final int CALL_ACCEPT_HOLD = 1; + /** * @hide */ public static final int CALL_ACCEPT_TERMINATE = 2; + private final BluetoothAdapter mAdapter; private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector = @@ -649,6 +738,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose return IBluetoothHeadsetClient.Stub.asInterface(service); } }; + /** * Create a BluetoothHeadsetClient proxy object. */ @@ -660,6 +750,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); } + /** * Close the connection to the backing service. * Other public functions of BluetoothHeadsetClient will return default error @@ -675,9 +766,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose mCloseGuard.close(); } } + private IBluetoothHeadsetClient getService() { return mProfileConnector.getService(); } + /** @hide */ protected void finalize() { if (mCloseGuard != null) { @@ -685,6 +778,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } close(); } + /** * Connects to remote device. * @@ -719,6 +813,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Disconnects remote device * @@ -749,11 +844,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** - * Return the list of connected remote devices - * - * @return list of connected devices; empty list if nothing is connected. - * + * {@inheritDoc} * @hide */ @SystemApi @@ -787,13 +880,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** - * Returns list of remote devices in a particular state - * - * @param states collection of states - * @return list of devices that state matches the states listed in <code>states</code>; empty - * list if nothing matches the <code>states</code> - * + * {@inheritDoc} * @hide */ @SystemApi @@ -828,12 +917,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** - * Returns state of the <code>device</code> - * - * @param device a remote device - * @return the state of connection of the device - * + * {@inheritDoc} * @hide */ @SystemApi @@ -864,6 +950,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Set priority of the profile * @@ -881,6 +968,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose if (DBG) log("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); } + /** * Set connection policy of the profile * @@ -923,6 +1011,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Get the priority of the profile. * @@ -940,6 +1029,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); } + /** * Get the connection policy of the profile. * @@ -979,6 +1069,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Starts voice recognition. * @@ -1012,6 +1103,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Send vendor specific AT command. * @@ -1043,6 +1135,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Stops voice recognition. * @@ -1076,6 +1169,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Returns list of all calls in any state. * @@ -1107,6 +1201,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Returns list of current values of AG indicators. * @@ -1135,6 +1230,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Accepts a call * @@ -1167,6 +1263,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Holds a call. * @@ -1196,6 +1293,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Rejects a call. * @@ -1230,6 +1328,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Terminates a specified call. * @@ -1268,6 +1367,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Enters private mode with a specified call. * @@ -1304,6 +1404,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Performs explicit call transfer. * @@ -1339,6 +1440,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Places a call with specified number. * @@ -1373,6 +1475,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Sends DTMF code. * @@ -1405,6 +1508,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Get a number corresponding to last voice tag recorded on AG. * @@ -1439,6 +1543,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Returns current audio state of Audio Gateway. * @@ -1469,6 +1574,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; } + /** * Sets whether audio routing is allowed. * @@ -1496,6 +1602,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } } } + /** * Returns whether audio routing is allowed. * @@ -1525,6 +1632,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Initiates a connection of audio channel. * @@ -1556,6 +1664,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Disconnects audio channel. * @@ -1587,6 +1696,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + /** * Get Audio Gateway features * @@ -1615,12 +1725,237 @@ public final class BluetoothHeadsetClient implements BluetoothProfile, AutoClose } return defaultValue; } + + /** + * A class that contains the network service info provided by the HFP Client profile + * + * @hide + */ + @SystemApi + public static final class NetworkServiceState implements Parcelable { + /** The device associated with this service state */ + private final BluetoothDevice mDevice; + + /** True if there is service available, False otherwise */ + private final boolean mIsServiceAvailable; + + /** The name of the operator associated with the remote device's current network */ + private final String mOperatorName; + + /** + * The general signal strength, from 0 to 5 + */ + private final int mSignalStrength; + + /** True if we are network roaming, False otherwise */ + private final boolean mIsRoaming; + + /** + * Create a NetworkServiceState Object + * + * @param device The device associated with this network signal state + * @param isServiceAvailable True if there is service available, False otherwise + * @param operatorName The name of the operator associated with the remote device's current + * network. Use Null if the value is unknown + * @param signalStrength The general signal strength + * @param isRoaming True if we are network roaming, False otherwise + * + * @hide + */ + public NetworkServiceState(BluetoothDevice device, boolean isServiceAvailable, + String operatorName, int signalStrength, boolean isRoaming) { + mDevice = device; + mIsServiceAvailable = isServiceAvailable; + mOperatorName = operatorName; + mSignalStrength = signalStrength; + mIsRoaming = isRoaming; + } + + /** + * Get the device associated with this network service state + * + * @return a BluetoothDevice associated with this state + * + * @hide + */ + @SystemApi + public @NonNull BluetoothDevice getDevice() { + return mDevice; + } + + /** + * Get the network service availablility state + * + * @return True if there is service available, False otherwise + * + * @hide + */ + @SystemApi + public boolean isServiceAvailable() { + return mIsServiceAvailable; + } + + /** + * Get the network operator name + * + * @return A string representing the name of the operator the remote device is on, or null + * if unknown. + * + * @hide + */ + @SystemApi + public @Nullable String getNetworkOperatorName() { + return mOperatorName; + } + + /** + * The HFP Client defined signal strength, from 0 to 5. + * + * Bluetooth HFP v1.8 specifies that the signal strength of a device can be [0, 5]. It does + * not place any requirements on how a device derives those values. While they're typically + * derived from signal quality/RSSI buckets, there's no way to be certain on the exact + * meaning. Derivation methods can even change between wireless cellular technologies. + * + * That said, you can "generally" interpret the values relative to each other as follows: + * - Level 0: None/Unknown + * - Level 1: Very Poor + * - Level 2: Poor + * - Level 3: Fair + * - Level 4: Good + * - Level 5: Great + * + * @return the HFP Client defined signal strength, range [0, 5] + * + * @hide + */ + @SystemApi + public @IntRange(from = 0, to = 5) int getSignalStrength() { + return mSignalStrength; + } + + /** + * Get the network service roaming status + * + * * @return True if we are network roaming, False otherwise + * + * @hide + */ + @SystemApi + public boolean isRoaming() { + return mIsRoaming; + } + + /** + * {@link Parcelable.Creator} interface implementation. + */ + public static final @NonNull Parcelable.Creator<NetworkServiceState> CREATOR = + new Parcelable.Creator<NetworkServiceState>() { + public NetworkServiceState createFromParcel(Parcel in) { + return new NetworkServiceState((BluetoothDevice) in.readParcelable(null), + in.readInt() == 1, in.readString(), in.readInt(), in.readInt() == 1); + } + + public @NonNull NetworkServiceState[] newArray(int size) { + return new NetworkServiceState[size]; + } + }; + + /** + * @hide + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(mDevice, 0); + out.writeInt(mIsServiceAvailable ? 1 : 0); + out.writeString(mOperatorName); + out.writeInt(mSignalStrength); + out.writeInt(mIsRoaming ? 1 : 0); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + } + + /** + * Intent used to broadcast the change in network service state of an HFP Client device + * + * <p>This intent will have 2 extras: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * <li> {@link EXTRA_NETWORK_SERVICE_STATE} - A {@link NetworkServiceState} object. </li> + * </ul> + * + * @hide + */ + @SuppressLint("ActionValue") + @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NETWORK_SERVICE_STATE_CHANGED = + "android.bluetooth.headsetclient.profile.action.NETWORK_SERVICE_STATE_CHANGED"; + + /** + * Extra for the network service state changed intent. + * + * This extra represents the current network service state of a connected Bluetooth device. + * + * @hide + */ + @SuppressLint("ActionValue") + @SystemApi + public static final String EXTRA_NETWORK_SERVICE_STATE = + "android.bluetooth.headsetclient.extra.EXTRA_NETWORK_SERVICE_STATE"; + + /** + * Get the network service state for a device + * + * @param device The {@link BluetoothDevice} you want the network service state for + * @return A {@link NetworkServiceState} representing the network service state of the device, + * or null if the device is not connected + * @hide + */ + @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @Nullable NetworkServiceState getNetworkServiceState(@NonNull BluetoothDevice device) { + if (device == null) { + return null; + } + + Bundle agEvents = getCurrentAgEvents(device); + if (agEvents == null) { + return null; + } + + boolean isServiceAvailable = (agEvents.getInt(EXTRA_NETWORK_STATUS, 0) == 1); + int signalStrength = agEvents.getInt(EXTRA_NETWORK_SIGNAL_STRENGTH, 0); + String operatorName = agEvents.getString(EXTRA_OPERATOR_NAME, null); + boolean isRoaming = (agEvents.getInt(EXTRA_NETWORK_ROAMING, 0) == 1); + + return new NetworkServiceState(device, isServiceAvailable, operatorName, signalStrength, + isRoaming); + } + private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } + private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index b29d7b0413..be3e48a0d9 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -340,9 +340,7 @@ public final class BluetoothMapClient implements BluetoothProfile, AutoCloseable } /** - * Get the list of connected devices. Currently at most one. - * - * @return list of connected devices + * {@inheritDoc} * @hide */ @SystemApi @@ -378,10 +376,7 @@ public final class BluetoothMapClient implements BluetoothProfile, AutoCloseable } /** - * Get the list of devices matching specified states. Currently at most one. - * - * @param states The connection states to match for. - * @return list of matching devices + * {@inheritDoc} * @hide */ @SystemApi @@ -418,10 +413,7 @@ public final class BluetoothMapClient implements BluetoothProfile, AutoCloseable } /** - * Get connection state of device - * - * @param device The remote device whose connection state is to be ascertained. - * @return device connection state + * {@inheritDoc} * @hide */ @SystemApi diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 8d1946c9a6..e10f5e5e49 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -218,11 +218,7 @@ public final class BluetoothPbapClient implements BluetoothProfile, AutoCloseabl } /** - * Get the list of connected devices. - * Currently at most one. - * - * @return list of connected devices - * + * {@inheritDoc} * @hide */ @SystemApi @@ -260,11 +256,7 @@ public final class BluetoothPbapClient implements BluetoothProfile, AutoCloseabl } /** - * Get the list of devices matching specified states. Currently at most one. - * - * @param states The connection states to match for. - * @return list of matching devices - * + * {@inheritDoc} * @hide */ @SystemApi @@ -303,11 +295,7 @@ public final class BluetoothPbapClient implements BluetoothProfile, AutoCloseabl } /** - * Get connection state of device - * - * @param device The remote device whose connection state is to be ascertained. - * @return device connection state - * + * {@inheritDoc} * @hide */ @SystemApi diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index 12d95ccb90..dfc35eabe8 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -66,8 +66,9 @@ public abstract class BluetoothProfileConnector<T> { }; private @Nullable ComponentName resolveSystemService(@NonNull Intent intent, - @NonNull PackageManager pm, @PackageManager.ComponentInfoFlags int flags) { - List<ResolveInfo> results = pm.queryIntentServices(intent, flags); + @NonNull PackageManager pm) { + List<ResolveInfo> results = pm.queryIntentServices(intent, + PackageManager.ResolveInfoFlags.of(0)); if (results == null) { return null; } @@ -130,8 +131,7 @@ public abstract class BluetoothProfileConnector<T> { mCloseGuard.open("doUnbind"); try { Intent intent = new Intent(mServiceName); - ComponentName comp = resolveSystemService(intent, mContext.getPackageManager(), - 0); + ComponentName comp = resolveSystemService(intent, mContext.getPackageManager()); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, USER_HANDLE_CURRENT_OR_SELF)) { diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java index 49b0578f8c..ebaace621b 100644 --- a/framework/java/android/bluetooth/BluetoothStatusCodes.java +++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java @@ -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; |