diff options
Diffstat (limited to 'framework/java/android/bluetooth')
8 files changed, 682 insertions, 256 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c17bf8ef61..e00b0a94d0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -53,12 +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.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 +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; @@ -437,6 +438,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 +845,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 +907,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); @@ -1115,7 +1131,7 @@ public final class BluetoothAdapter { 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { @Override @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(Void query) { + public Integer recompute(Void query) { try { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); @@ -1265,7 +1281,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 +1333,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 +1767,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 +1778,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) { + public @Nullable Duration getDiscoverableTimeout() { 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() { - 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; } /** @@ -2248,7 +2265,7 @@ public final class BluetoothAdapter { 8, BLUETOOTH_FILTERING_CACHE_PROPERTY) { @Override @SuppressLint("AndroidFrameworkRequiresPermission") - protected Boolean recompute(Void query) { + public Boolean recompute(Void query) { try { mServiceLock.readLock().lock(); if (mService != null) { @@ -2445,17 +2462,17 @@ public final class BluetoothAdapter { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { - BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.FEATURE_SUPPORTED, BluetoothStatusCodes.ERROR_UNKNOWN, BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, - BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED, + BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, }) public @interface LeFeatureReturnValues {} /** - * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is - * supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if - * the feature is not supported or an error code. + * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio feature is + * supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not + * supported, or an error code. * * @return whether the LE audio is supported */ @@ -2483,9 +2500,9 @@ public final class BluetoothAdapter { } /** - * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio broadcast source - * feature is supported, {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the - * feature is not supported, or an error code. + * 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. * * @return whether the LE audio broadcast source is supported */ @@ -2514,9 +2531,9 @@ public final class BluetoothAdapter { } /** - * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio broadcast assistant - * feature is supported, {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the - * feature is not supported, or an error code. + * 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. * * @return whether the LE audio broadcast assistent is supported */ @@ -2801,7 +2818,7 @@ public final class BluetoothAdapter { 8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) { @Override @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(Void query) { + public Integer recompute(Void query) { try { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); @@ -2879,7 +2896,7 @@ public final class BluetoothAdapter { 8, BLUETOOTH_PROFILE_CACHE_PROPERTY) { @Override @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(Integer query) { + public Integer recompute(Integer query) { try { mServiceLock.readLock().lock(); if (mService != null) { @@ -3496,6 +3513,9 @@ public final class BluetoothAdapter { return true; } return false; + } else if (profile == BluetoothProfile.LE_AUDIO) { + BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this); + return true; } else if (profile == BluetoothProfile.VOLUME_CONTROL) { BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); return true; @@ -3599,6 +3619,10 @@ public final class BluetoothAdapter { BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); break; + case BluetoothProfile.LE_AUDIO: + BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy; + leAudio.close(); + break; case BluetoothProfile.VOLUME_CONTROL: BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; vcs.close(); diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index a9f2c60968..bd281fc236 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -331,21 +331,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; /** @@ -354,11 +359,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 170e8469b0..95e9abc37b 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1412,17 +1412,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; } /** @@ -1814,7 +1818,8 @@ public final class BluetoothDevice implements Parcelable, Attributable { 8, BLUETOOTH_BONDING_CACHE_PROPERTY) { @Override @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(BluetoothDevice query) { + public Integer recompute(BluetoothDevice query) { + final int defaultValue = BluetoothDevice.BOND_NONE; try { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); 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 c1103ac33a..4e57c59643 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. @@ -916,17 +889,36 @@ public final class BluetoothHeadset implements BluetoothProfile { } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + BluetoothHeadset.STATE_AUDIO_CONNECTING, + BluetoothHeadset.STATE_AUDIO_CONNECTED, + BluetoothStatusCodes.ERROR_TIMEOUT + }) + public @interface GetAudioStateReturnValues {} + /** * Get the current audio state of the Headset. - * Note: This is an internal function and shouldn't be exposed + * + * @param device is the Bluetooth device for which the audio state is being queried + * @return the audio state of the device or an error code + * @throws IllegalArgumentException if the device is null * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @SystemApi @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getAudioState(BluetoothDevice device) { + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @GetAudioStateReturnValues int getAudioState(@NonNull BluetoothDevice device) { if (VDBG) log("getAudioState"); + if (device == null) { + throw new IllegalArgumentException("device cannot be null"); + } final IBluetoothHeadset service = mService; final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; if (service == null) { @@ -937,8 +929,12 @@ public final class BluetoothHeadset implements BluetoothProfile { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.getAudioState(device, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + throw e.rethrowFromSystemServer(); + } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + return BluetoothStatusCodes.ERROR_TIMEOUT; } } return defaultValue; @@ -1065,106 +1061,121 @@ public final class BluetoothHeadset implements BluetoothProfile { } } - /** - * Check if at least one headset's SCO audio is connected or connecting - * - * @return true if at least one device's SCO audio is connected or connecting, false otherwise - * or on error - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isAudioOn() { - if (VDBG) log("isAudioOn()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isAudioOn(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + 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, + BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED, + BluetoothStatusCodes.ERROR_CALL_ACTIVE, + BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED + }) + public @interface ConnectAudioReturnValues {} /** - * Initiates a connection of headset audio to the current active device - * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. - * - * <p> {@link #EXTRA_STATE} will transition from - * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when - * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} - * in case of failure to establish the audio connection. - * - * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true - * before calling this method - * - * @return false if there was some error such as there is no active headset + * Initiates a connection of SCO audio to the current active HFP device. The active HFP device + * 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. + * + * @return whether the connection was successfully initiated or an error code on failure * @hide */ - @UnsupportedAppUsage + @SystemApi @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connectAudio() { + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @ConnectAudioReturnValues int connectAudio() { if (VDBG) log("connectAudio()"); final IBluetoothHeadset service = mService; - final boolean defaultValue = false; + final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; 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<Boolean> recv = new SynchronousResultReceiver(); + final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.connectAudio(mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { + } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + throw e.rethrowFromSystemServer(); + } catch (TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + return BluetoothStatusCodes.ERROR_TIMEOUT; } + } else { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } - return defaultValue; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + 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 + }) + public @interface DisconnectAudioReturnValues {} + /** - * Initiates a disconnection of HFP SCO audio. - * Tear down voice recognition or virtual voice call if any. + * Initiates a disconnection of HFP SCO audio from actively connected devices. It also tears + * down voice recognition or virtual voice call, if any exists. * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. + * <p> If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent + * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted with {@link #EXTRA_STATE} set to + * {@link #STATE_AUDIO_DISCONNECTED}. * - * @return false if audio is not connected, or on error, true otherwise + * @return whether the disconnection was initiated successfully or an error code on failure * @hide */ - @UnsupportedAppUsage + @SystemApi @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnectAudio() { + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @DisconnectAudioReturnValues int disconnectAudio() { if (VDBG) log("disconnectAudio()"); final IBluetoothHeadset service = mService; - final boolean defaultValue = false; + final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; 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<Boolean> recv = new SynchronousResultReceiver(); + final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.disconnectAudio(mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { + } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + throw e.rethrowFromSystemServer(); + } catch (TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + return BluetoothStatusCodes.ERROR_TIMEOUT; } + } else { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } - return defaultValue; } /** @@ -1446,7 +1457,10 @@ public final class BluetoothHeadset implements BluetoothProfile { @SystemApi @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean isInbandRingingEnabled() { if (DBG) log("isInbandRingingEnabled()"); final IBluetoothHeadset service = mService; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 87bd76114b..f1402d485d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -18,6 +18,7 @@ package android.bluetooth; import static android.bluetooth.BluetoothUtils.getSyncTimeout; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -31,6 +32,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; @@ -1615,6 +1618,218 @@ 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 + * (0 - Unknown, 1 - Poor, 2 - Fair, 3 - Good, 4 - Great, 5 - Excellent) + */ + 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 getOperatorName() { + return mOperatorName; + } + + /** + * Get the network's general signal strength + * + * @return The general signal strength (0 - None, 1 - Poor, 2 - Fair, 3 - Good, + * 4 - Great, 5 - Excellent) + * + * @hide + */ + @SystemApi + public 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; } 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 4f57e75583..ebaace621b 100644 --- a/framework/java/android/bluetooth/BluetoothStatusCodes.java +++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java @@ -20,7 +20,7 @@ import android.annotation.SystemApi; /** * A class with constants representing possible return values for Bluetooth APIs. General return - * values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999. + * values occupy the range 0 to 199. Profile-specific return values occupy the range 200-999. * API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which * occupies the max integer value. */ @@ -29,28 +29,28 @@ public final class BluetoothStatusCodes { private BluetoothStatusCodes() {} /** - * Indicates that the API call was successful + * Indicates that the API call was successful. */ public static final int SUCCESS = 0; /** - * Error code indicating that Bluetooth is not enabled + * Error code indicating that Bluetooth is not enabled. */ public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; /** * 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; /** - * Error code indicating that the Bluetooth Device specified is not bonded + * Error code indicating that the Bluetooth Device specified is not bonded. */ public static final int ERROR_DEVICE_NOT_BONDED = 3; /** - * Error code indicating that the Bluetooth Device specified is not connected, but is bonded + * Error code indicating that the Bluetooth Device specified is not connected, but is bonded. * * @hide */ @@ -58,7 +58,7 @@ public final class BluetoothStatusCodes { /** * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission + * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission. * * @hide */ @@ -66,13 +66,13 @@ public final class BluetoothStatusCodes { /** * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission + * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. */ public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; /** * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission + * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission. * * @hide */ @@ -80,20 +80,49 @@ public final class BluetoothStatusCodes { /** * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission + * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission. */ public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; /** * Error code indicating that the profile service is not bound. You can bind a profile service - * by calling {@link BluetoothAdapter#getProfileProxy} + * by calling {@link BluetoothAdapter#getProfileProxy}. */ public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; /** - * Error code indicating that the feature is not supported. + * Indicates that the feature is supported. */ - public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; + public static final int FEATURE_SUPPORTED = 10; + + /** + * Indicates that the feature is not supported. + */ + public static final int FEATURE_NOT_SUPPORTED = 11; + + /** + * Error code indicating that the device is not the active device for this profile. + * + * @hide + */ + @SystemApi + public static final int ERROR_NOT_ACTIVE_DEVICE = 12; + + /** + * Error code indicating that there are no active devices for the profile. + * + * @hide + */ + @SystemApi + public static final int ERROR_NO_ACTIVE_DEVICES = 13; + + /** + * Indicates that the Bluetooth profile is not connected to this device. + * + * @hide + */ + @SystemApi + public static final int ERROR_PROFILE_NOT_CONNECTED = 14; /** * Error code indicating that the requested operation timed out. @@ -203,12 +232,12 @@ public final class BluetoothStatusCodes { /** * A GATT writeCharacteristic request is not permitted on the remote device. */ - public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; + public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200; /** * A GATT writeCharacteristic request is issued to a busy remote device. */ - public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; + public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 201; /** * Indicates that the operation is allowed. @@ -346,6 +375,38 @@ public final class BluetoothStatusCodes { */ public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109; + /** + * Indicates that there is already one device for which SCO audio is connected or connecting. + * + * @hide + */ + @SystemApi + public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116; + + /** + * Indicates that SCO audio was already not connected for this device. + * + * @hide + */ + @SystemApi + public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117; + + /** + * Indicates that there audio route is currently blocked by the system. + * + * @hide + */ + @SystemApi + public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; + + /** + * Indicates that there is an active call preventing this operation from succeeding. + * + * @hide + */ + @SystemApi + public static final int ERROR_CALL_ACTIVE = 1119; + // LE audio related return codes reserved from 1200 to 1300 /** |