summaryrefslogtreecommitdiff
path: root/framework/java/android/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'framework/java/android/bluetooth')
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java272
-rwxr-xr-xframework/java/android/bluetooth/BluetoothClass.java21
-rw-r--r--framework/java/android/bluetooth/BluetoothDevice.java21
-rw-r--r--framework/java/android/bluetooth/BluetoothFrameworkInitializer.java104
-rw-r--r--framework/java/android/bluetooth/BluetoothHeadset.java206
-rw-r--r--framework/java/android/bluetooth/BluetoothHeadsetClient.java215
-rw-r--r--framework/java/android/bluetooth/BluetoothProfileConnector.java8
-rw-r--r--framework/java/android/bluetooth/BluetoothStatusCodes.java91
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
/**