summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java2
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterService.java2338
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterServiceBinder.java2266
-rw-r--r--android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java12
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java1
-rw-r--r--apex/Android.bp1
-rw-r--r--system/gd/hal/hci_hal.h7
-rw-r--r--system/gd/hal/hci_hal_host.cc36
-rw-r--r--system/gd/hci/hci_layer.cc41
-rw-r--r--system/gd/hci/hci_layer.h2
-rw-r--r--system/gd/rust/linux/service/src/interface_manager.rs48
-rw-r--r--system/gd/rust/linux/service/src/main.rs136
-rw-r--r--system/gd/rust/linux/stack/src/bluetooth.rs27
-rw-r--r--system/gd/rust/linux/stack/src/lib.rs8
15 files changed, 2533 insertions, 2394 deletions
diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java b/android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java
index 517e810d8a..3c235cce76 100644
--- a/android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java
+++ b/android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java
@@ -197,7 +197,7 @@ class MediaBrowserWrapper {
mSubscribedIds.put(
rootId, new ArrayList<>(Arrays.asList(callback)));
mWrappedBrowser.subscribe(
- rootId, new BrowserSubscriptionCallback(mediaId));
+ rootId, new BrowserSubscriptionCallback(rootId));
});
} else {
mSubscribedIds.put(mediaId, new ArrayList<>(Arrays.asList(callback)));
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index eff7a6e132..0eeeb7f6bd 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -20,16 +20,12 @@ package com.android.bluetooth.btservice;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.Manifest.permission.BLUETOOTH_SCAN;
-import static android.Manifest.permission.DUMP;
-import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
-import static android.Manifest.permission.MODIFY_PHONE_STATE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.nameForState;
import static android.bluetooth.BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
import static android.bluetooth.BluetoothDevice.BOND_NONE;
-import static android.bluetooth.BluetoothDevice.TRANSPORT_AUTO;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
@@ -42,9 +38,6 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-import static com.android.bluetooth.ChangeIds.ENFORCE_CONNECT;
-import static com.android.bluetooth.Utils.callerIsSystem;
-import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
import static com.android.bluetooth.Utils.getBytesFromAddress;
import static com.android.bluetooth.Utils.isDualModeAudioEnabled;
import static com.android.bluetooth.Utils.isPackageNameAccurate;
@@ -60,7 +53,6 @@ import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.admin.DevicePolicyManager;
-import android.app.compat.CompatChanges;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
@@ -72,7 +64,6 @@ import android.bluetooth.BluetoothFrameworkInitializer;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.BluetoothQualityReport;
import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothServerSocket;
@@ -82,16 +73,12 @@ import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUtils;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.BufferConstraints;
-import android.bluetooth.IBluetooth;
-import android.bluetooth.IBluetoothActivityEnergyInfoListener;
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.IBluetoothConnectionCallback;
-import android.bluetooth.IBluetoothHciVendorSpecificCallback;
import android.bluetooth.IBluetoothMetadataListener;
import android.bluetooth.IBluetoothOobDataCallback;
import android.bluetooth.IBluetoothPreferredAudioProfilesCallback;
import android.bluetooth.IBluetoothQualityReportReadyCallback;
-import android.bluetooth.IBluetoothSocketManager;
import android.bluetooth.IncomingRfcommSocketInfo;
import android.bluetooth.OobData;
import android.bluetooth.UidTraffic;
@@ -115,7 +102,6 @@ import android.os.Message;
import android.os.ParcelUuid;
import android.os.Parcelable;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -125,7 +111,6 @@ import android.provider.DeviceConfig;
import android.provider.Settings;
import android.sysprop.BluetoothProperties;
import android.text.TextUtils;
-import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -172,16 +157,10 @@ import com.android.bluetooth.telephony.BluetoothInCallService;
import com.android.bluetooth.vc.VolumeControlService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.expresslog.Counter;
import com.android.modules.utils.BackgroundThread;
import com.android.modules.utils.BytesMatcher;
-import libcore.util.SneakyThrow;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.FileVisitResult;
@@ -195,7 +174,6 @@ import java.time.Instant;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -206,14 +184,12 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
public class AdapterService extends Service {
private static final String TAG =
@@ -225,9 +201,6 @@ public class AdapterService extends Service {
private static final int MESSAGE_PREFERRED_AUDIO_PROFILES_AUDIO_FRAMEWORK_TIMEOUT = 4;
private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 100;
- private static final int MIN_ADVT_INSTANCES_FOR_MA = 5;
- private static final int MIN_OFFLOADED_FILTERS = 10;
- private static final int MIN_OFFLOADED_SCAN_STORAGE_BYTES = 1024;
private static final Duration PENDING_SOCKET_HANDOFF_TIMEOUT = Duration.ofMinutes(1);
private static final Duration GENERATE_LOCAL_OOB_DATA_TIMEOUT = Duration.ofSeconds(2);
@@ -309,7 +282,7 @@ public class AdapterService extends Service {
new ArrayList<>();
private BluetoothAdapter mAdapter;
- @VisibleForTesting AdapterProperties mAdapterProperties;
+ private AdapterProperties mAdapterProperties;
private AdapterState mAdapterStateMachine;
private BondStateMachine mBondStateMachine;
private RemoteDevices mRemoteDevices;
@@ -422,10 +395,6 @@ public class AdapterService extends Service {
return sAdapterService;
}
- AdapterNativeInterface getNative() {
- return mNativeInterface;
- }
-
/** Allow test to set an AdapterService to be return by AdapterService.getAdapterService() */
@VisibleForTesting
public static synchronized void setAdapterService(AdapterService instance) {
@@ -791,10 +760,76 @@ public class AdapterService extends Service {
return mActiveDeviceManager;
}
+ public RemoteDevices getRemoteDevices() {
+ return mRemoteDevices;
+ }
+
public SilenceDeviceManager getSilenceDeviceManager() {
return mSilenceDeviceManager;
}
+ AdapterNativeInterface getNative() {
+ return mNativeInterface;
+ }
+
+ AdapterServiceHandler getHandler() {
+ return mHandler;
+ }
+
+ DatabaseManager getDatabaseManager() {
+ return mDatabaseManager;
+ }
+
+ AdapterProperties getAdapterProperties() {
+ return mAdapterProperties;
+ }
+
+ Map<BluetoothDevice, RemoteCallbackList<IBluetoothMetadataListener>> getMetadataListeners() {
+ return mMetadataListeners;
+ }
+
+ Map<String, CallerInfo> getBondAttemptCallerInfo() {
+ return mBondAttemptCallerInfo;
+ }
+
+ Optional<PhonePolicy> getPhonePolicy() {
+ return mPhonePolicy;
+ }
+
+ BondStateMachine getBondStateMachine() {
+ return mBondStateMachine;
+ }
+
+ CompanionDeviceManager getCompanionDeviceManager() {
+ return mCompanionDeviceManager;
+ }
+
+ BluetoothSocketManagerBinder getBluetoothSocketManagerBinder() {
+ return mBluetoothSocketManagerBinder;
+ }
+
+ RemoteCallbackList<IBluetoothConnectionCallback> getBluetoothConnectionCallbacks() {
+ return mBluetoothConnectionCallbacks;
+ }
+
+ RemoteCallbackList<IBluetoothPreferredAudioProfilesCallback>
+ getPreferredAudioProfilesCallbacks() {
+ return mPreferredAudioProfilesCallbacks;
+ }
+
+ RemoteCallbackList<IBluetoothQualityReportReadyCallback>
+ getBluetoothQualityReportReadyCallbacks() {
+ return mBluetoothQualityReportReadyCallbacks;
+ }
+
+ BluetoothHciVendorSpecificDispatcher getBluetoothHciVendorSpecificDispatcher() {
+ return mBluetoothHciVendorSpecificDispatcher;
+ }
+
+ BluetoothHciVendorSpecificNativeInterface getBluetoothHciVendorSpecificNativeInterface() {
+ return mBluetoothHciVendorSpecificNativeInterface;
+ }
+
/**
* Log L2CAP CoC Server Connection Metrics
*
@@ -1932,7 +1967,7 @@ public class AdapterService extends Service {
@BluetoothAdapter.RfcommListenerResult
@RequiresPermission(BLUETOOTH_CONNECT)
- private int startRfcommListener(
+ int startRfcommListener(
String name, ParcelUuid uuid, PendingIntent pendingIntent, AttributionSource source) {
if (mBluetoothServerSockets.containsKey(uuid.getUuid())) {
Log.d(TAG, "Cannot start RFCOMM listener: UUID " + uuid.getUuid() + "already in use.");
@@ -1949,7 +1984,6 @@ public class AdapterService extends Service {
}
@BluetoothAdapter.RfcommListenerResult
- @VisibleForTesting
int stopRfcommListener(ParcelUuid uuid, AttributionSource source) {
RfcommListenerData listenerData = mBluetoothServerSockets.get(uuid.getUuid());
@@ -1968,7 +2002,6 @@ public class AdapterService extends Service {
return listenerData.closeServerAndPendingSockets(mHandler);
}
- @VisibleForTesting
IncomingRfcommSocketInfo retrievePendingSocketForServiceRecord(
ParcelUuid uuid, AttributionSource source) {
IncomingRfcommSocketInfo socketInfo = new IncomingRfcommSocketInfo();
@@ -2124,7 +2157,6 @@ public class AdapterService extends Service {
}
}
- @VisibleForTesting
boolean isAvailable() {
return !mCleaningUp;
}
@@ -2160,2223 +2192,6 @@ public class AdapterService extends Service {
}
/**
- * There is no leak of this binder since it is never re-used and the process is systematically
- * killed
- */
- @VisibleForTesting
- public static class AdapterServiceBinder extends IBluetooth.Stub {
- private final AdapterService mService;
-
- AdapterServiceBinder(AdapterService svc) {
- mService = svc;
- }
-
- public AdapterService getService() {
- if (!mService.isAvailable()) {
- return null;
- }
- return mService;
- }
-
- @Override
- public int getState() {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothAdapter.STATE_OFF;
- }
-
- return service.getState();
- }
-
- @Override
- public void killBluetoothProcess() {
- mService.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
-
- Runnable killAction =
- () -> {
- if (Flags.killInsteadOfExit()) {
- Log.i(TAG, "killBluetoothProcess: Calling killProcess(myPid())");
- Process.killProcess(Process.myPid());
- } else {
- Log.i(TAG, "killBluetoothProcess: Calling System.exit");
- System.exit(0);
- }
- };
-
- // Post on the main handler to let the cleanup complete before calling exit
- mService.mHandler.post(killAction);
-
- try {
- // Wait for Bluetooth to be killed from its main thread
- Thread.sleep(1_000); // SystemServer is waiting 2000 ms, we need to wait less here
- } catch (InterruptedException e) {
- Log.e(TAG, "killBluetoothProcess: Interrupted while waiting for kill");
- }
-
- // Bluetooth cannot be killed on the main thread; it is in a deadLock.
- // Trying to recover by killing the Bluetooth from the binder thread.
- // This is bad :(
- Counter.logIncrement("bluetooth.value_kill_from_binder_thread");
- Log.wtf(TAG, "Failed to kill Bluetooth using its main thread. Trying from binder");
- killAction.run();
- }
-
- @Override
- public void offToBleOn(boolean quietMode, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "offToBleOn")) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.offToBleOn(quietMode);
- }
-
- @Override
- public void onToBleOn(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "onToBleOn")) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.onToBleOn();
- }
-
- @Override
- public String getAddress(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getAddress")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getAddress")) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(LOCAL_MAC_ADDRESS, null);
-
- return Utils.getAddressStringFromByte(service.mAdapterProperties.getAddress());
- }
-
- @Override
- public List<ParcelUuid> getUuids(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getUuids")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getUuids")) {
- return Collections.emptyList();
- }
-
- ParcelUuid[] parcels = service.mAdapterProperties.getUuids();
- if (parcels == null) {
- parcels = new ParcelUuid[0];
- }
- return Arrays.asList(parcels);
- }
-
- @Override
- public String getIdentityAddress(String address) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getIdentityAddress")
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- Utils.getCallingAttributionSource(mService),
- "AdapterService getIdentityAddress")) {
- return null;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getIdentityAddress(address);
- }
-
- @Override
- @NonNull
- public BluetoothAddress getIdentityAddressWithType(@NonNull String address) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "getIdentityAddressWithType")
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- Utils.getCallingAttributionSource(mService),
- "AdapterService getIdentityAddressWithType")) {
- return new BluetoothAddress(null, BluetoothDevice.ADDRESS_TYPE_UNKNOWN);
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getIdentityAddressWithType(address);
- }
-
- @Override
- public String getName(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getName")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getName")) {
- return null;
- }
-
- return service.getName();
- }
-
- @Override
- public int getNameLengthForAdvertise(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "getNameLengthForAdvertise")
- || !Utils.checkAdvertisePermissionForDataDelivery(service, source, TAG)) {
- return -1;
- }
-
- return service.getNameLengthForAdvertise();
- }
-
- @Override
- public boolean setName(String name, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setName")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService setName")) {
- return false;
- }
-
- if (Flags.emptyNamesAreInvalid()) {
- requireNonNull(name);
- name = name.trim();
- if (name.isEmpty()) {
- throw new IllegalArgumentException("Empty names are not valid");
- }
- }
-
- Log.d(TAG, "AdapterServiceBinder.setName(" + name + ")");
- return service.mAdapterProperties.setName(name);
- }
-
- @Override
- public int getScanMode(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getScanMode")
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, "AdapterService getScanMode")) {
- return SCAN_MODE_NONE;
- }
-
- return service.getScanMode();
- }
-
- @Override
- public int setScanMode(int mode, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setScanMode")
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, "AdapterService setScanMode")) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- String logCaller = Utils.getUidPidString() + " packageName=" + source.getPackageName();
- CompletableFuture<Boolean> future = new CompletableFuture<>();
- mService.mHandler.post(
- () ->
- future.complete(
- service.getState() == BluetoothAdapter.STATE_ON
- && service.setScanMode(mode, logCaller)));
- return future.join()
- ? BluetoothStatusCodes.SUCCESS
- : BluetoothStatusCodes.ERROR_UNKNOWN;
- }
-
- @Override
- public long getDiscoverableTimeout(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getDiscoverableTimeout")
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, "AdapterService getDiscoverableTimeout")) {
- return -1;
- }
-
- return service.mAdapterProperties.getDiscoverableTimeout();
- }
-
- @Override
- public int setDiscoverableTimeout(long timeout, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setDiscoverableTimeout")
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, "AdapterService setDiscoverableTimeout")) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.mAdapterProperties.setDiscoverableTimeout((int) timeout)
- ? BluetoothStatusCodes.SUCCESS
- : BluetoothStatusCodes.ERROR_UNKNOWN;
- }
-
- @Override
- public boolean startDiscovery(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "startDiscovery")) {
- return false;
- }
-
- if (!Utils.checkScanPermissionForDataDelivery(service, source, "Starting discovery.")) {
- return false;
- }
-
- Log.i(TAG, "startDiscovery: from " + Utils.getUidPidString());
- return service.startDiscovery(source);
- }
-
- @Override
- public boolean cancelDiscovery(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "cancelDiscovery")
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, "AdapterService cancelDiscovery")) {
- return false;
- }
-
- Log.i(TAG, "cancelDiscovery: from " + Utils.getUidPidString());
- return service.mNativeInterface.cancelDiscovery();
- }
-
- @Override
- public boolean isDiscovering(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "isDiscovering")
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, "AdapterService isDiscovering")) {
- return false;
- }
-
- return service.mAdapterProperties.isDiscovering();
- }
-
- @Override
- public long getDiscoveryEndMillis(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getDiscoveryEndMillis")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return -1;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.mAdapterProperties.discoveryEndMillis();
- }
-
- @Override
- public List<BluetoothDevice> getMostRecentlyConnectedDevices(AttributionSource source) {
- // don't check caller, may be called from system UI
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getMostRecentlyConnectedDevices")) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.mDatabaseManager.getMostRecentlyConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getBondedDevices(AttributionSource source) {
- // don't check caller, may be called from system UI
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getBondedDevices")) {
- return Collections.emptyList();
- }
-
- return Arrays.asList(service.getBondedDevices());
- }
-
- @Override
- public int getAdapterConnectionState() {
- // don't check caller, may be called from system UI
- AdapterService service = getService();
- if (service == null) {
- return BluetoothAdapter.STATE_DISCONNECTED;
- }
-
- return service.mAdapterProperties.getConnectionState();
- }
-
- /**
- * This method has an associated binder cache. The invalidation methods must be changed if
- * the logic behind this method changes.
- */
- @Override
- public int getProfileConnectionState(int profile, AttributionSource source) {
- AdapterService service = getService();
- boolean checkConnect = false;
- final int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- try {
- checkConnect = CompatChanges.isChangeEnabled(ENFORCE_CONNECT, callingUid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "getProfileConnectionState")
- || (checkConnect
- && !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getProfileConnectionState"))) {
- return STATE_DISCONNECTED;
- }
-
- return service.mAdapterProperties.getProfileConnectionState(profile);
- }
-
- @Override
- public boolean createBond(
- BluetoothDevice device,
- int transport,
- AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "createBond")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService createBond")) {
- return false;
- }
-
- Log.i(
- TAG,
- "createBond:"
- + (" device=" + device)
- + (" transport=" + transport)
- + (" from " + Utils.getUidPidString()));
- return service.createBond(
- device, transport, null, null, source.getPackageName());
- }
-
- @Override
- public boolean createBondOutOfBand(
- BluetoothDevice device,
- int transport,
- OobData remoteP192Data,
- OobData remoteP256Data,
- AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "createBond")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService createBond")) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- Log.i(
- TAG,
- "createBondOutOfBand:"
- + (" device=" + device)
- + (" transport=" + transport)
- + (" from " + Utils.getUidPidString()));
- return service.createBond(
- device, transport, remoteP192Data, remoteP256Data, source.getPackageName());
- }
-
- @Override
- public boolean cancelBondProcess(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "cancelBondProcess")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService cancelBondProcess")) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- Log.i(TAG, "cancelBondProcess: device=" + device + ", from " + Utils.getUidPidString());
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- if (deviceProp != null) {
- deviceProp.setBondingInitiatedLocally(false);
- }
-
- service.logUserBondResponse(device, false, source);
- return service.mNativeInterface.cancelBond(getBytesFromAddress(device.getAddress()));
- }
-
- @Override
- public boolean removeBond(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "removeBond")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService removeBond")) {
- return false;
- }
-
- Log.i(TAG, "removeBond: device=" + device + ", from " + Utils.getUidPidString());
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDED) {
- Log.w(
- TAG,
- device
- + " cannot be removed since "
- + ((deviceProp == null)
- ? "properties are empty"
- : "bond state is " + deviceProp.getBondState()));
- return false;
- }
- service.logUserBondResponse(device, false, source);
- service.mBondAttemptCallerInfo.remove(device.getAddress());
- service.mPhonePolicy.ifPresent(policy -> policy.onRemoveBondRequest(device));
- deviceProp.setBondingInitiatedLocally(false);
-
- Message msg = service.mBondStateMachine.obtainMessage(BondStateMachine.REMOVE_BOND);
- msg.obj = device;
- service.mBondStateMachine.sendMessage(msg);
- return true;
- }
-
- @Override
- public int getBondState(BluetoothDevice device, AttributionSource source) {
- // don't check caller, may be called from system UI
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getBondState")) {
- return BluetoothDevice.BOND_NONE;
- }
-
- return service.getBondState(device);
- }
-
- @Override
- public boolean isBondingInitiatedLocally(BluetoothDevice device, AttributionSource source) {
- // don't check caller, may be called from system UI
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService isBondingInitiatedLocally")) {
- return false;
- }
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- return deviceProp != null && deviceProp.isBondingInitiatedLocally();
- }
-
- @Override
- public void generateLocalOobData(
- int transport, IBluetoothOobDataCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "generateLocalOobData")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.generateLocalOobData(transport, callback);
- }
-
- @Override
- public long getSupportedProfiles(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return 0;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return Config.getSupportedProfilesBitMask();
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getConnectionState")) {
- return BluetoothDevice.CONNECTION_STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public int getConnectionHandle(
- BluetoothDevice device, int transport, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getConnectionHandle")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothDevice.ERROR;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectionHandle(device, transport);
- }
-
- @Override
- public boolean canBondWithoutDialog(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.canBondWithoutDialog(device);
- }
-
- @Override
- public String getPackageNameOfBondingApplication(
- BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
-
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getPackageNameOfBondingApplication(device);
- }
-
- @Override
- public boolean removeActiveDevice(@ActiveDeviceUse int profiles, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "removeActiveDevice")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- Log.i(
- TAG,
- "removeActiveDevice: profiles="
- + profiles
- + ", from "
- + Utils.getUidPidString());
- return service.setActiveDevice(null, profiles);
- }
-
- @Override
- public boolean setActiveDevice(
- BluetoothDevice device, @ActiveDeviceUse int profiles, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setActiveDevice")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- Log.i(
- TAG,
- "setActiveDevice: device="
- + device
- + ", profiles="
- + profiles
- + ", from "
- + Utils.getUidPidString());
-
- return service.setActiveDevice(device, profiles);
- }
-
- @Override
- public List<BluetoothDevice> getActiveDevices(
- @ActiveDeviceProfile int profile, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getActiveDevices")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getActiveDevices(profile);
- }
-
- @Override
- public int connectAllEnabledProfiles(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null || !service.isEnabled()) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "connectAllEnabledProfiles")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- if (device == null) {
- throw new IllegalArgumentException("device cannot be null");
- }
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
-
- service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- Log.i(
- TAG,
- "connectAllEnabledProfiles: device="
- + device
- + ", from "
- + Utils.getUidPidString());
- MetricsLogger.getInstance()
- .logBluetoothEvent(
- device,
- BluetoothStatsLog
- .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__INITIATOR_CONNECTION,
- BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__START,
- source.getUid());
-
- try {
- return service.connectAllEnabledProfiles(device);
- } catch (Exception e) {
- Log.v(TAG, "connectAllEnabledProfiles() failed", e);
- SneakyThrow.sneakyThrow(e);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public int disconnectAllEnabledProfiles(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "disconnectAllEnabledProfiles")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- if (device == null) {
- throw new IllegalArgumentException("device cannot be null");
- }
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- Log.i(
- TAG,
- "disconnectAllEnabledProfiles: device="
- + device
- + ", from "
- + Utils.getUidPidString());
-
- try {
- return service.disconnectAllEnabledProfiles(device);
- } catch (Exception e) {
- Log.v(TAG, "disconnectAllEnabledProfiles() failed", e);
- SneakyThrow.sneakyThrow(e);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public String getRemoteName(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteName")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getRemoteName")) {
- return null;
- }
-
- return service.getRemoteName(device);
- }
-
- @Override
- public int getRemoteType(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteType")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getRemoteType")) {
- return BluetoothDevice.DEVICE_TYPE_UNKNOWN;
- }
-
- return service.getRemoteType(device);
- }
-
- @Override
- public String getRemoteAlias(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteAlias")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getRemoteAlias")) {
- return null;
- }
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- return deviceProp != null ? deviceProp.getAlias() : null;
- }
-
- @Override
- public int setRemoteAlias(BluetoothDevice device, String name, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setRemoteAlias")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- if (name != null && name.isEmpty()) {
- throw new IllegalArgumentException("alias cannot be the empty string");
- }
-
- if (!Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService setRemoteAlias")) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
-
- Utils.enforceCdmAssociationIfNotBluetoothPrivileged(
- service, service.mCompanionDeviceManager, source, device);
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- if (deviceProp == null) {
- return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
- }
- deviceProp.setAlias(device, name);
- return BluetoothStatusCodes.SUCCESS;
- }
-
- @Override
- public int getRemoteClass(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteClass")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getRemoteClass")) {
- return 0;
- }
-
- return service.getRemoteClass(device);
- }
-
- @Override
- public List<ParcelUuid> getRemoteUuids(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteUuids")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getRemoteUuids")) {
- return Collections.emptyList();
- }
-
- final ParcelUuid[] parcels = service.getRemoteUuids(device);
- if (parcels == null) {
- return null;
- }
- return Arrays.asList(parcels);
- }
-
- @Override
- public boolean fetchRemoteUuids(
- BluetoothDevice device, int transport, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "fetchRemoteUuids")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService fetchRemoteUuids")) {
- return false;
- }
- if (transport != TRANSPORT_AUTO) {
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
-
- Log.i(
- TAG,
- "fetchRemoteUuids: device="
- + device
- + ", transport="
- + transport
- + ", from "
- + Utils.getUidPidString());
-
- service.mRemoteDevices.fetchUuids(device, transport);
- MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_FETCH_UUID_REQUEST, 1);
- return true;
- }
-
- @Override
- public boolean setPin(
- BluetoothDevice device,
- boolean accept,
- int len,
- byte[] pinCode,
- AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPin")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService setPin")) {
- return false;
- }
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- // Only allow setting a pin in bonding state, or bonded state in case of security
- // upgrade.
- if (deviceProp == null || !deviceProp.isBondingOrBonded()) {
- Log.e(TAG, "setPin: device=" + device + ", not bonding");
- return false;
- }
- if (pinCode.length != len) {
- android.util.EventLog.writeEvent(
- 0x534e4554, "139287605", -1, "PIN code length mismatch");
- return false;
- }
- service.logUserBondResponse(device, accept, source);
- Log.i(
- TAG,
- "setPin: device="
- + device
- + ", accept="
- + accept
- + ", from "
- + Utils.getUidPidString());
- return service.mNativeInterface.pinReply(
- getBytesFromAddress(device.getAddress()), accept, len, pinCode);
- }
-
- @Override
- public boolean setPasskey(
- BluetoothDevice device,
- boolean accept,
- int len,
- byte[] passkey,
- AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPasskey")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService setPasskey")) {
- return false;
- }
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- if (deviceProp == null || !deviceProp.isBonding()) {
- Log.e(TAG, "setPasskey: device=" + device + ", not bonding");
- return false;
- }
- if (passkey.length != len) {
- android.util.EventLog.writeEvent(
- 0x534e4554, "139287605", -1, "Passkey length mismatch");
- return false;
- }
- service.logUserBondResponse(device, accept, source);
- Log.i(
- TAG,
- "setPasskey: device="
- + device
- + ", accept="
- + accept
- + ", from "
- + Utils.getUidPidString());
-
- return service.mNativeInterface.sspReply(
- getBytesFromAddress(device.getAddress()),
- AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY,
- accept,
- Utils.byteArrayToInt(passkey));
- }
-
- @Override
- public boolean setPairingConfirmation(
- BluetoothDevice device, boolean accept, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPairingConfirmation")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- if (deviceProp == null || !deviceProp.isBonding()) {
- Log.e(TAG, "setPairingConfirmation: device=" + device + ", not bonding");
- return false;
- }
- service.logUserBondResponse(device, accept, source);
- Log.i(
- TAG,
- "setPairingConfirmation: device="
- + device
- + ", accept="
- + accept
- + ", from "
- + Utils.getUidPidString());
-
- return service.mNativeInterface.sspReply(
- getBytesFromAddress(device.getAddress()),
- AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
- accept,
- 0);
- }
-
- @Override
- public boolean getSilenceMode(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getSilenceMode")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.mSilenceDeviceManager.getSilenceMode(device);
- }
-
- @Override
- public boolean setSilenceMode(
- BluetoothDevice device, boolean silence, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setSilenceMode")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.mSilenceDeviceManager.setSilenceMode(device, silence);
- return true;
- }
-
- @Override
- public int getPhonebookAccessPermission(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "getPhonebookAccessPermission")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getPhonebookAccessPermission")) {
- return BluetoothDevice.ACCESS_UNKNOWN;
- }
-
- return service.getPhonebookAccessPermission(device);
- }
-
- @Override
- public boolean setPhonebookAccessPermission(
- BluetoothDevice device, int value, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "setPhonebookAccessPermission")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.setPhonebookAccessPermission(device, value);
- return true;
- }
-
- @Override
- public int getMessageAccessPermission(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "getMessageAccessPermission")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getMessageAccessPermission")) {
- return BluetoothDevice.ACCESS_UNKNOWN;
- }
-
- return service.getMessageAccessPermission(device);
- }
-
- @Override
- public boolean setMessageAccessPermission(
- BluetoothDevice device, int value, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "setMessageAccessPermission")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.setMessageAccessPermission(device, value);
- return true;
- }
-
- @Override
- public int getSimAccessPermission(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getSimAccessPermission")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getSimAccessPermission")) {
- return BluetoothDevice.ACCESS_UNKNOWN;
- }
-
- return service.getSimAccessPermission(device);
- }
-
- @Override
- public boolean setSimAccessPermission(
- BluetoothDevice device, int value, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setSimAccessPermission")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.setSimAccessPermission(device, value);
- return true;
- }
-
- @Override
- public void logL2capcocServerConnection(
- BluetoothDevice device,
- int port,
- boolean isSecured,
- int result,
- long socketCreationTimeMillis,
- long socketCreationLatencyMillis,
- long socketConnectionTimeMillis,
- long timeoutMillis) {
- AdapterService service = getService();
- if (service == null) {
- return;
- }
- service.logL2capcocServerConnection(
- device,
- port,
- isSecured,
- result,
- socketCreationTimeMillis,
- socketCreationLatencyMillis,
- socketConnectionTimeMillis,
- timeoutMillis,
- Binder.getCallingUid());
- }
-
- @Override
- public IBluetoothSocketManager getSocketManager() {
- AdapterService service = getService();
- if (service == null) {
- return null;
- }
-
- return IBluetoothSocketManager.Stub.asInterface(service.mBluetoothSocketManagerBinder);
- }
-
- @Override
- public void logL2capcocClientConnection(
- BluetoothDevice device,
- int port,
- boolean isSecured,
- int result,
- long socketCreationTimeNanos,
- long socketCreationLatencyNanos,
- long socketConnectionTimeNanos) {
- AdapterService service = getService();
- if (service == null) {
- return;
- }
- service.logL2capcocClientConnection(
- device,
- port,
- isSecured,
- result,
- socketCreationTimeNanos,
- socketCreationLatencyNanos,
- socketConnectionTimeNanos,
- Binder.getCallingUid());
- }
-
- @Override
- public void logRfcommConnectionAttempt(
- BluetoothDevice device,
- boolean isSecured,
- int resultCode,
- long socketCreationTimeNanos,
- boolean isSerialPort) {
- AdapterService service = getService();
- if (service == null) {
- return;
- }
- service.logRfcommConnectionAttempt(
- device,
- isSecured,
- resultCode,
- socketCreationTimeNanos,
- isSerialPort,
- Binder.getCallingUid());
- }
-
- @Override
- public boolean sdpSearch(
- BluetoothDevice device, ParcelUuid uuid, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "sdpSearch")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService sdpSearch")) {
- return false;
- }
- return service.sdpSearch(device, uuid);
- }
-
- @Override
- public int getBatteryLevel(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getBatteryLevel")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getBatteryLevel")) {
- return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
- }
-
- DeviceProperties deviceProp = service.mRemoteDevices.getDeviceProperties(device);
- if (deviceProp == null) {
- return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
- }
- return deviceProp.getBatteryLevel();
- }
-
- @Override
- public int getMaxConnectedAudioDevices(AttributionSource source) {
- // don't check caller, may be called from system UI
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getMaxConnectedAudioDevices")) {
- return -1;
- }
-
- return service.getMaxConnectedAudioDevices();
- }
-
- @Override
- public boolean factoryReset(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.factoryReset();
- }
-
- @Override
- public void registerBluetoothConnectionCallback(
- IBluetoothConnectionCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "registerBluetoothConnectionCallback")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.mBluetoothConnectionCallbacks.register(callback);
- }
-
- @Override
- public void unregisterBluetoothConnectionCallback(
- IBluetoothConnectionCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "unregisterBluetoothConnectionCallback")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.mBluetoothConnectionCallbacks.unregister(callback);
- }
-
- @Override
- public void registerCallback(IBluetoothCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "registerCallback")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.registerRemoteCallback(callback);
- }
-
- @Override
- public void unregisterCallback(IBluetoothCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "unregisterCallback")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.unregisterRemoteCallback(callback);
- }
-
- @Override
- public boolean isMultiAdvertisementSupported() {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
-
- int val = service.mAdapterProperties.getNumOfAdvertisementInstancesSupported();
- return val >= MIN_ADVT_INSTANCES_FOR_MA;
- }
-
- /**
- * This method has an associated binder cache. The invalidation methods must be changed if
- * the logic behind this method changes.
- */
- @Override
- public boolean isOffloadedFilteringSupported() {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
-
- int val = service.getNumOfOffloadedScanFilterSupported();
- return val >= MIN_OFFLOADED_FILTERS;
- }
-
- @Override
- public boolean isOffloadedScanBatchingSupported() {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
-
- int val = service.getOffloadedScanResultStorage();
- return val >= MIN_OFFLOADED_SCAN_STORAGE_BYTES;
- }
-
- @Override
- public boolean isLe2MPhySupported() {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
-
- return service.isLe2MPhySupported();
- }
-
- @Override
- public boolean isLeCodedPhySupported() {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
-
- return service.isLeCodedPhySupported();
- }
-
- @Override
- public boolean isLeExtendedAdvertisingSupported() {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
-
- return service.isLeExtendedAdvertisingSupported();
- }
-
- @Override
- public boolean isLePeriodicAdvertisingSupported() {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
-
- return service.isLePeriodicAdvertisingSupported();
- }
-
- @Override
- public int isLeAudioSupported() {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
-
- Set<Integer> supportedProfileServices =
- Arrays.stream(Config.getSupportedProfiles())
- .boxed()
- .collect(Collectors.toSet());
- int[] leAudioUnicastProfiles = Config.getLeAudioUnicastProfiles();
-
- if (Arrays.stream(leAudioUnicastProfiles)
- .allMatch(supportedProfileServices::contains)) {
- return BluetoothStatusCodes.FEATURE_SUPPORTED;
- }
-
- return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
- }
-
- @Override
- public int isLeAudioBroadcastSourceSupported() {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
-
- long supportBitMask = Config.getSupportedProfilesBitMask();
- if ((supportBitMask & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) != 0) {
- return BluetoothStatusCodes.FEATURE_SUPPORTED;
- }
-
- return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
- }
-
- @Override
- public int isLeAudioBroadcastAssistantSupported() {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
-
- int[] supportedProfileServices = Config.getSupportedProfiles();
-
- if (Arrays.stream(supportedProfileServices)
- .anyMatch(
- profileId ->
- profileId == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)) {
- return BluetoothStatusCodes.FEATURE_SUPPORTED;
- }
-
- return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
- }
-
- @Override
- public int isDistanceMeasurementSupported(AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- } else if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "isDistanceMeasurementSupported")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- } else if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return BluetoothStatusCodes.FEATURE_SUPPORTED;
- }
-
- @Override
- public int getLeMaximumAdvertisingDataLength() {
- AdapterService service = getService();
- if (service == null) {
- return 0;
- }
-
- return service.getLeMaximumAdvertisingDataLength();
- }
-
- @Override
- public boolean isActivityAndEnergyReportingSupported() {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
-
- return service.mAdapterProperties.isActivityAndEnergyReportingSupported();
- }
-
- @Override
- public BluetoothActivityEnergyInfo reportActivityInfo(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.reportActivityInfo();
- }
-
- @Override
- public boolean registerMetadataListener(
- IBluetoothMetadataListener listener,
- BluetoothDevice device,
- AttributionSource source) {
- requireNonNull(device);
- requireNonNull(listener);
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "registerMetadataListener")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.mHandler.post(
- () ->
- service.mMetadataListeners
- .computeIfAbsent(device, k -> new RemoteCallbackList())
- .register(listener));
-
- return true;
- }
-
- @Override
- public boolean unregisterMetadataListener(
- IBluetoothMetadataListener listener,
- BluetoothDevice device,
- AttributionSource source) {
- requireNonNull(device);
- requireNonNull(listener);
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "unregisterMetadataListener")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.mHandler.post(
- () ->
- service.mMetadataListeners.computeIfPresent(
- device,
- (k, v) -> {
- v.unregister(listener);
- if (v.getRegisteredCallbackCount() == 0) {
- return null;
- }
- return v;
- }));
- return true;
- }
-
- @Override
- public boolean setMetadata(
- BluetoothDevice device, int key, byte[] value, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setMetadata")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.setMetadata(device, key, value);
- }
-
- @Override
- public byte[] getMetadata(BluetoothDevice device, int key, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getMetadata")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getMetadata(device, key);
- }
-
- @Override
- public int isRequestAudioPolicyAsSinkSupported(
- BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "isRequestAudioPolicyAsSinkSupported")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.FEATURE_NOT_CONFIGURED;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.isRequestAudioPolicyAsSinkSupported(device);
- }
-
- @Override
- public int requestAudioPolicyAsSink(
- BluetoothDevice device,
- BluetoothSinkAudioPolicy policies,
- AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- } else if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "requestAudioPolicyAsSink")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- } else if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.requestAudioPolicyAsSink(device, policies);
- }
-
- @Override
- public BluetoothSinkAudioPolicy getRequestedAudioPolicyAsSink(
- BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "getRequestedAudioPolicyAsSink")
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getRequestedAudioPolicyAsSink(device);
- }
-
- @Override
- public void requestActivityInfo(
- IBluetoothActivityEnergyInfoListener listener, AttributionSource source) {
- BluetoothActivityEnergyInfo info = reportActivityInfo(source);
- try {
- listener.onBluetoothActivityEnergyInfoAvailable(info);
- } catch (RemoteException e) {
- Log.e(TAG, "onBluetoothActivityEnergyInfo: RemoteException", e);
- }
- }
-
- @Override
- public void bleOnToOn(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "bleOnToOn")) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.bleOnToOn();
- }
-
- @Override
- public void bleOnToOff(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "bleOnToOff")) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.bleOnToOff();
- }
-
- @Override
- public void dump(FileDescriptor fd, String[] args) {
- PrintWriter writer = new PrintWriter(new FileOutputStream(fd));
- AdapterService service = getService();
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(DUMP, null);
-
- service.dump(fd, writer, args);
- writer.close();
- }
-
- @Override
- public boolean allowLowLatencyAudio(boolean allowed, BluetoothDevice device) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "allowLowLatencyAudio")
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- Utils.getCallingAttributionSource(service),
- "AdapterService allowLowLatencyAudio")) {
- return false;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.allowLowLatencyAudio(allowed, device);
- }
-
- @Override
- public int startRfcommListener(
- String name,
- ParcelUuid uuid,
- PendingIntent pendingIntent,
- AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "startRfcommListener")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService startRfcommListener")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.startRfcommListener(name, uuid, pendingIntent, source);
- }
-
- @Override
- public int stopRfcommListener(ParcelUuid uuid, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(service, TAG, "stopRfcommListener")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService stopRfcommListener")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.stopRfcommListener(uuid, source);
- }
-
- @Override
- public IncomingRfcommSocketInfo retrievePendingSocketForServiceRecord(
- ParcelUuid uuid, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "retrievePendingSocketForServiceRecord")
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- source,
- "AdapterService retrievePendingSocketForServiceRecord")) {
- return null;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.retrievePendingSocketForServiceRecord(uuid, source);
- }
-
- @Override
- public void setForegroundUserId(int userId, AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- Utils.getCallingAttributionSource(mService),
- "AdapterService setForegroundUserId")) {
- return;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- Utils.setForegroundUserId(userId);
- }
-
- @Override
- public int setPreferredAudioProfiles(
- BluetoothDevice device, Bundle modeToProfileBundle, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setPreferredAudioProfiles")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- requireNonNull(device);
- requireNonNull(modeToProfileBundle);
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (service.getBondState(device) != BluetoothDevice.BOND_BONDED) {
- return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
- }
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.setPreferredAudioProfiles(device, modeToProfileBundle);
- }
-
- @Override
- public Bundle getPreferredAudioProfiles(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return Bundle.EMPTY;
- }
- if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "getPreferredAudioProfiles")) {
- return Bundle.EMPTY;
- }
- requireNonNull(device);
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (service.getBondState(device) != BluetoothDevice.BOND_BONDED) {
- return Bundle.EMPTY;
- }
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return Bundle.EMPTY;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getPreferredAudioProfiles(device);
- }
-
- @Override
- public int notifyActiveDeviceChangeApplied(
- BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystem(TAG, "notifyActiveDeviceChangeApplied")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- requireNonNull(device);
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (service.getBondState(device) != BluetoothDevice.BOND_BONDED) {
- return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
- }
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.notifyActiveDeviceChangeApplied(device);
- }
-
- @Override
- public int isDualModeAudioEnabled(AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- if (!Utils.isDualModeAudioEnabled()) {
- return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
- }
-
- return BluetoothStatusCodes.SUCCESS;
- }
-
- @Override
- public int registerPreferredAudioProfilesChangedCallback(
- IBluetoothPreferredAudioProfilesCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "registerPreferredAudioProfilesChangedCallback")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- requireNonNull(callback);
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- // If LE only mode is enabled, the dual mode audio feature is disabled
- if (!Utils.isDualModeAudioEnabled()) {
- return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
- }
-
- service.mPreferredAudioProfilesCallbacks.register(callback);
- return BluetoothStatusCodes.SUCCESS;
- }
-
- @Override
- public int unregisterPreferredAudioProfilesChangedCallback(
- IBluetoothPreferredAudioProfilesCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "unregisterPreferredAudioProfilesChangedCallback")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- requireNonNull(callback);
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- if (!service.mPreferredAudioProfilesCallbacks.unregister(callback)) {
- Log.e(
- TAG,
- "unregisterPreferredAudioProfilesChangedCallback: callback was never "
- + "registered");
- return BluetoothStatusCodes.ERROR_CALLBACK_NOT_REGISTERED;
- }
- return BluetoothStatusCodes.SUCCESS;
- }
-
- @Override
- public int registerBluetoothQualityReportReadyCallback(
- IBluetoothQualityReportReadyCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "registerBluetoothQualityReportReadyCallback")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- requireNonNull(callback);
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.mBluetoothQualityReportReadyCallbacks.register(callback);
- return BluetoothStatusCodes.SUCCESS;
- }
-
- @Override
- public int unregisterBluetoothQualityReportReadyCallback(
- IBluetoothQualityReportReadyCallback callback, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "unregisterBluetoothQualityReportReadyCallback")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- requireNonNull(callback);
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- if (!service.mBluetoothQualityReportReadyCallbacks.unregister(callback)) {
- Log.e(
- TAG,
- "unregisterBluetoothQualityReportReadyCallback: callback was never "
- + "registered");
- return BluetoothStatusCodes.ERROR_CALLBACK_NOT_REGISTERED;
- }
- return BluetoothStatusCodes.SUCCESS;
- }
-
- @Override
- public void registerHciVendorSpecificCallback(
- IBluetoothHciVendorSpecificCallback callback, int[] eventCodes) {
- AdapterService service = getService();
- if (service == null) {
- return;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "registerHciVendorSpecificCallback")) {
- throw new SecurityException("not allowed");
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- requireNonNull(callback);
- requireNonNull(eventCodes);
-
- Set<Integer> eventCodesSet =
- Arrays.stream(eventCodes).boxed().collect(Collectors.toSet());
- if (eventCodesSet.stream()
- .anyMatch((n) -> (n < 0) || (n >= 0x52 && n < 0x60) || (n > 0xff))) {
- throw new IllegalArgumentException("invalid vendor-specific event code");
- }
-
- service.mBluetoothHciVendorSpecificDispatcher.register(callback, eventCodesSet);
- }
-
- @Override
- public void unregisterHciVendorSpecificCallback(
- IBluetoothHciVendorSpecificCallback callback) {
- AdapterService service = getService();
- if (service == null) {
- return;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "unregisterHciVendorSpecificCallback")) {
- throw new SecurityException("not allowed");
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- requireNonNull(callback);
-
- service.mBluetoothHciVendorSpecificDispatcher.unregister(callback);
- }
-
- @Override
- public void sendHciVendorSpecificCommand(
- int ocf, byte[] parameters, IBluetoothHciVendorSpecificCallback callback) {
- AdapterService service = getService();
- if (service == null) {
- return;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "sendHciVendorSpecificCommand")) {
- throw new SecurityException("not allowed");
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- // Open this no-op android command for test purpose
- int getVendorCapabilitiesOcf = 0x153;
- if (ocf < 0
- || (ocf >= 0x150 && ocf < 0x160 && ocf != getVendorCapabilitiesOcf)
- || (ocf > 0x3ff)) {
- throw new IllegalArgumentException("invalid vendor-specific event code");
- }
- requireNonNull(parameters);
- if (parameters.length > 255) {
- throw new IllegalArgumentException("Parameters size is too big");
- }
-
- Optional<byte[]> cookie =
- service.mBluetoothHciVendorSpecificDispatcher.getRegisteredCookie(callback);
- if (!cookie.isPresent()) {
- Log.e(TAG, "send command without registered callback");
- throw new IllegalStateException("callback not registered");
- }
-
- service.mBluetoothHciVendorSpecificNativeInterface.sendCommand(
- ocf, parameters, cookie.get());
- }
-
- @Override
- public int getOffloadedTransportDiscoveryDataScanSupported(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "getOffloadedTransportDiscoveryDataScanSupported")
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, "getOffloadedTransportDiscoveryDataScanSupported")) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getOffloadedTransportDiscoveryDataScanSupported();
- }
-
- @Override
- public boolean isMediaProfileConnected(AttributionSource source) {
- AdapterService service = getService();
- if (service == null
- || !Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService.isMediaProfileConnected")) {
- return false;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.isMediaProfileConnected();
- }
-
- @Override
- public IBinder getBluetoothGatt() {
- AdapterService service = getService();
- return service == null ? null : service.getBluetoothGatt();
- }
-
- @Override
- public IBinder getBluetoothScan() {
- AdapterService service = getService();
- return service == null ? null : service.getBluetoothScan();
- }
-
- @Override
- public void unregAllGattClient(AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.unregAllGattClient(source);
- }
-
- @Override
- public IBinder getProfile(int profileId) {
- AdapterService service = getService();
- if (service == null) {
- return null;
- }
-
- return service.getProfile(profileId);
- }
-
- @Override
- public int setActiveAudioDevicePolicy(
- BluetoothDevice device, int activeAudioDevicePolicy, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setActiveAudioDevicePolicy")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.mDatabaseManager.setActiveAudioDevicePolicy(
- device, activeAudioDevicePolicy);
- }
-
- @Override
- public int getActiveAudioDevicePolicy(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT;
- }
- if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "getActiveAudioDevicePolicy")) {
- throw new IllegalStateException(
- "Caller is not the system or part of the active/managed user");
- }
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.mDatabaseManager.getActiveAudioDevicePolicy(device);
- }
-
- @Override
- public int setMicrophonePreferredForCalls(
- BluetoothDevice device, boolean enabled, AttributionSource source) {
- requireNonNull(device);
- AdapterService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "setMicrophonePreferredForCalls")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- }
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (!Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService setMicrophonePreferredForCalls")) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.mDatabaseManager.setMicrophonePreferredForCalls(device, enabled);
- }
-
- @Override
- public boolean isMicrophonePreferredForCalls(
- BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- AdapterService service = getService();
- if (service == null) {
- return true;
- }
- if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "isMicrophonePreferredForCalls")) {
- throw new IllegalStateException(
- "Caller is not the system or part of the active/managed user");
- }
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (!Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService isMicrophonePreferredForCalls")) {
- return true;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.mDatabaseManager.isMicrophonePreferredForCalls(device);
- }
-
- @Override
- public boolean isLeCocSocketOffloadSupported(AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.isLeCocSocketOffloadSupported();
- }
-
- @Override
- public boolean isRfcommSocketOffloadSupported(AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return false;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.isRfcommSocketOffloadSupported();
- }
-
- @Override
- public IBinder getBluetoothAdvertise() {
- AdapterService service = getService();
- return service == null ? null : service.getBluetoothAdvertise();
- }
-
- @Override
- public IBinder getDistanceMeasurement() {
- AdapterService service = getService();
- return service == null ? null : service.getDistanceMeasurement();
- }
-
- @Override
- public int getKeyMissingCount(BluetoothDevice device, AttributionSource source) {
- AdapterService service = getService();
- if (service == null) {
- return -1;
- }
- if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "getKeyMissingCount")) {
- throw new IllegalStateException(
- "Caller is not the system or part of the active/managed user");
- }
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- throw new IllegalArgumentException("device cannot have an invalid address");
- }
- if (!Utils.checkConnectPermissionForDataDelivery(
- service, source, "AdapterService getKeyMissingCount")) {
- return -1;
- }
-
- return service.mDatabaseManager.getKeyMissingCount(device);
- }
- }
-
- /**
* Gets the preferred audio profiles for the device. See {@link
* BluetoothAdapter#getPreferredAudioProfiles(BluetoothDevice)} for more details.
*
@@ -4449,7 +2264,7 @@ public class AdapterService extends Service {
* @param modeToProfileBundle is the preferences we want to set for the device
* @return whether the preferences were successfully requested
*/
- private int setPreferredAudioProfiles(BluetoothDevice device, Bundle modeToProfileBundle) {
+ int setPreferredAudioProfiles(BluetoothDevice device, Bundle modeToProfileBundle) {
Log.i(TAG, "setPreferredAudioProfiles for device=" + device);
if (!isDualModeAudioEnabled()) {
Log.e(TAG, "setPreferredAudioProfiles called while sysprop is disabled");
@@ -4669,7 +2484,7 @@ public class AdapterService extends Service {
* @param device the remote device whose preferred audio profiles have been changed
* @return whether the Bluetooth stack acknowledged the change successfully
*/
- private int notifyActiveDeviceChangeApplied(BluetoothDevice device) {
+ int notifyActiveDeviceChangeApplied(BluetoothDevice device) {
if (mLeAudioService == null) {
Log.e(TAG, "LE Audio profile not enabled");
return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED;
@@ -6125,27 +3940,22 @@ public class AdapterService extends Service {
mLocalCallbacks.remove(callback);
}
- @VisibleForTesting
void registerRemoteCallback(IBluetoothCallback callback) {
mSystemServerCallbacks.register(callback);
}
- @VisibleForTesting
void unregisterRemoteCallback(IBluetoothCallback callback) {
mSystemServerCallbacks.unregister(callback);
}
- @VisibleForTesting
void bleOnToOn() {
mAdapterStateMachine.sendMessage(AdapterState.USER_TURN_ON);
}
- @VisibleForTesting
void bleOnToOff() {
mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_OFF);
}
- @VisibleForTesting
boolean factoryReset() {
mDatabaseManager.factoryReset();
@@ -6164,12 +3974,11 @@ public class AdapterService extends Service {
return mNativeInterface.factoryReset();
}
- @VisibleForTesting
int getScanMode() {
return mScanMode;
}
- private boolean setScanMode(int mode, String from) {
+ boolean setScanMode(int mode, String from) {
mScanModeChanges.add(from + ": " + scanModeName(mode));
if (!mNativeInterface.setScanMode(convertScanModeToHal(mode))) {
return false;
@@ -6183,7 +3992,6 @@ public class AdapterService extends Service {
return true;
}
- @VisibleForTesting
BluetoothActivityEnergyInfo reportActivityInfo() {
if (mAdapterProperties.getState() != BluetoothAdapter.STATE_ON
|| !mAdapterProperties.isActivityAndEnergyReportingSupported()) {
@@ -6629,10 +4437,6 @@ public class AdapterService extends Service {
return BluetoothProperties.getHardwareOperatingVoltageMv().orElse(0) / 1000.0;
}
- public RemoteDevices getRemoteDevices() {
- return mRemoteDevices;
- }
-
private static String scanModeName(int scanMode) {
return switch (scanMode) {
case SCAN_MODE_NONE -> "SCAN_MODE_NONE";
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterServiceBinder.java b/android/app/src/com/android/bluetooth/btservice/AdapterServiceBinder.java
new file mode 100644
index 0000000000..57776bc560
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterServiceBinder.java
@@ -0,0 +1,2266 @@
+/*
+ * Copyright (C) 2025 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 com.android.bluetooth.btservice;
+
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
+import static android.bluetooth.BluetoothDevice.TRANSPORT_AUTO;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import static com.android.bluetooth.ChangeIds.ENFORCE_CONNECT;
+import static com.android.bluetooth.Utils.callerIsSystem;
+import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
+import static com.android.bluetooth.Utils.getBytesFromAddress;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothAdapter.ActiveDeviceProfile;
+import android.bluetooth.BluetoothAdapter.ActiveDeviceUse;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDevice.BluetoothAddress;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProtoEnums;
+import android.bluetooth.BluetoothSinkAudioPolicy;
+import android.bluetooth.BluetoothStatusCodes;
+import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothActivityEnergyInfoListener;
+import android.bluetooth.IBluetoothCallback;
+import android.bluetooth.IBluetoothConnectionCallback;
+import android.bluetooth.IBluetoothHciVendorSpecificCallback;
+import android.bluetooth.IBluetoothMetadataListener;
+import android.bluetooth.IBluetoothOobDataCallback;
+import android.bluetooth.IBluetoothPreferredAudioProfilesCallback;
+import android.bluetooth.IBluetoothQualityReportReadyCallback;
+import android.bluetooth.IBluetoothSocketManager;
+import android.bluetooth.IncomingRfcommSocketInfo;
+import android.bluetooth.OobData;
+import android.content.AttributionSource;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.bluetooth.BluetoothStatsLog;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
+import com.android.bluetooth.flags.Flags;
+import com.android.modules.expresslog.Counter;
+
+import libcore.util.SneakyThrow;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+/**
+ * There is no leak of this binder since it is never re-used and the process is systematically
+ * killed
+ */
+class AdapterServiceBinder extends IBluetooth.Stub {
+ private static final String TAG =
+ Utils.TAG_PREFIX_BLUETOOTH + AdapterServiceBinder.class.getSimpleName();
+
+ private static final int MIN_ADVT_INSTANCES_FOR_MA = 5;
+ private static final int MIN_OFFLOADED_FILTERS = 10;
+ private static final int MIN_OFFLOADED_SCAN_STORAGE_BYTES = 1024;
+
+ private final AdapterService mService;
+
+ AdapterServiceBinder(AdapterService svc) {
+ mService = svc;
+ }
+
+ public AdapterService getService() {
+ if (!mService.isAvailable()) {
+ return null;
+ }
+ return mService;
+ }
+
+ @Override
+ public int getState() {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothAdapter.STATE_OFF;
+ }
+
+ return service.getState();
+ }
+
+ @Override
+ public void killBluetoothProcess() {
+ mService.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
+
+ Runnable killAction =
+ () -> {
+ if (Flags.killInsteadOfExit()) {
+ Log.i(TAG, "killBluetoothProcess: Calling killProcess(myPid())");
+ Process.killProcess(Process.myPid());
+ } else {
+ Log.i(TAG, "killBluetoothProcess: Calling System.exit");
+ System.exit(0);
+ }
+ };
+
+ // Post on the main handler to let the cleanup complete before calling exit
+ mService.getHandler().post(killAction);
+
+ try {
+ // Wait for Bluetooth to be killed from its main thread
+ Thread.sleep(1_000); // SystemServer is waiting 2000 ms, we need to wait less here
+ } catch (InterruptedException e) {
+ Log.e(TAG, "killBluetoothProcess: Interrupted while waiting for kill");
+ }
+
+ // Bluetooth cannot be killed on the main thread; it is in a deadLock.
+ // Trying to recover by killing the Bluetooth from the binder thread.
+ // This is bad :(
+ Counter.logIncrement("bluetooth.value_kill_from_binder_thread");
+ Log.wtf(TAG, "Failed to kill Bluetooth using its main thread. Trying from binder");
+ killAction.run();
+ }
+
+ @Override
+ public void offToBleOn(boolean quietMode, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "offToBleOn")) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.offToBleOn(quietMode);
+ }
+
+ @Override
+ public void onToBleOn(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "onToBleOn")) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.onToBleOn();
+ }
+
+ @Override
+ public String getAddress(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getAddress")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getAddress")) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(LOCAL_MAC_ADDRESS, null);
+
+ return Utils.getAddressStringFromByte(service.getAdapterProperties().getAddress());
+ }
+
+ @Override
+ public List<ParcelUuid> getUuids(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getUuids")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getUuids")) {
+ return Collections.emptyList();
+ }
+
+ ParcelUuid[] parcels = service.getAdapterProperties().getUuids();
+ if (parcels == null) {
+ parcels = new ParcelUuid[0];
+ }
+ return Arrays.asList(parcels);
+ }
+
+ @Override
+ public String getIdentityAddress(String address) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getIdentityAddress")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service,
+ Utils.getCallingAttributionSource(mService),
+ "AdapterService getIdentityAddress")) {
+ return null;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getIdentityAddress(address);
+ }
+
+ @Override
+ @NonNull
+ public BluetoothAddress getIdentityAddressWithType(@NonNull String address) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getIdentityAddressWithType")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service,
+ Utils.getCallingAttributionSource(mService),
+ "AdapterService getIdentityAddressWithType")) {
+ return new BluetoothAddress(null, BluetoothDevice.ADDRESS_TYPE_UNKNOWN);
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getIdentityAddressWithType(address);
+ }
+
+ @Override
+ public String getName(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getName")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getName")) {
+ return null;
+ }
+
+ return service.getName();
+ }
+
+ @Override
+ public int getNameLengthForAdvertise(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getNameLengthForAdvertise")
+ || !Utils.checkAdvertisePermissionForDataDelivery(service, source, TAG)) {
+ return -1;
+ }
+
+ return service.getNameLengthForAdvertise();
+ }
+
+ @Override
+ public boolean setName(String name, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setName")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService setName")) {
+ return false;
+ }
+
+ if (Flags.emptyNamesAreInvalid()) {
+ requireNonNull(name);
+ name = name.trim();
+ if (name.isEmpty()) {
+ throw new IllegalArgumentException("Empty names are not valid");
+ }
+ }
+
+ Log.d(TAG, "AdapterServiceBinder.setName(" + name + ")");
+ return service.getAdapterProperties().setName(name);
+ }
+
+ @Override
+ public int getScanMode(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getScanMode")
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, "AdapterService getScanMode")) {
+ return SCAN_MODE_NONE;
+ }
+
+ return service.getScanMode();
+ }
+
+ @Override
+ public int setScanMode(int mode, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setScanMode")
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, "AdapterService setScanMode")) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ String logCaller = Utils.getUidPidString() + " packageName=" + source.getPackageName();
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ mService.getHandler()
+ .post(
+ () ->
+ future.complete(
+ service.getState() == BluetoothAdapter.STATE_ON
+ && service.setScanMode(mode, logCaller)));
+ return future.join() ? BluetoothStatusCodes.SUCCESS : BluetoothStatusCodes.ERROR_UNKNOWN;
+ }
+
+ @Override
+ public long getDiscoverableTimeout(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getDiscoverableTimeout")
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, "AdapterService getDiscoverableTimeout")) {
+ return -1;
+ }
+
+ return service.getAdapterProperties().getDiscoverableTimeout();
+ }
+
+ @Override
+ public int setDiscoverableTimeout(long timeout, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setDiscoverableTimeout")
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, "AdapterService setDiscoverableTimeout")) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getAdapterProperties().setDiscoverableTimeout((int) timeout)
+ ? BluetoothStatusCodes.SUCCESS
+ : BluetoothStatusCodes.ERROR_UNKNOWN;
+ }
+
+ @Override
+ public boolean startDiscovery(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "startDiscovery")) {
+ return false;
+ }
+
+ if (!Utils.checkScanPermissionForDataDelivery(service, source, "Starting discovery.")) {
+ return false;
+ }
+
+ Log.i(TAG, "startDiscovery: from " + Utils.getUidPidString());
+ return service.startDiscovery(source);
+ }
+
+ @Override
+ public boolean cancelDiscovery(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "cancelDiscovery")
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, "AdapterService cancelDiscovery")) {
+ return false;
+ }
+
+ Log.i(TAG, "cancelDiscovery: from " + Utils.getUidPidString());
+ return service.getNative().cancelDiscovery();
+ }
+
+ @Override
+ public boolean isDiscovering(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "isDiscovering")
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, "AdapterService isDiscovering")) {
+ return false;
+ }
+
+ return service.getAdapterProperties().isDiscovering();
+ }
+
+ @Override
+ public long getDiscoveryEndMillis(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getDiscoveryEndMillis")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return -1;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getAdapterProperties().discoveryEndMillis();
+ }
+
+ @Override
+ public List<BluetoothDevice> getMostRecentlyConnectedDevices(AttributionSource source) {
+ // don't check caller, may be called from system UI
+ AdapterService service = getService();
+ if (service == null
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getMostRecentlyConnectedDevices")) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getDatabaseManager().getMostRecentlyConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getBondedDevices(AttributionSource source) {
+ // don't check caller, may be called from system UI
+ AdapterService service = getService();
+ if (service == null
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getBondedDevices")) {
+ return Collections.emptyList();
+ }
+
+ return Arrays.asList(service.getBondedDevices());
+ }
+
+ @Override
+ public int getAdapterConnectionState() {
+ // don't check caller, may be called from system UI
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothAdapter.STATE_DISCONNECTED;
+ }
+
+ return service.getAdapterProperties().getConnectionState();
+ }
+
+ /**
+ * This method has an associated binder cache. The invalidation methods must be changed if the
+ * logic behind this method changes.
+ */
+ @Override
+ public int getProfileConnectionState(int profile, AttributionSource source) {
+ AdapterService service = getService();
+ boolean checkConnect = false;
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ checkConnect = CompatChanges.isChangeEnabled(ENFORCE_CONNECT, callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getProfileConnectionState")
+ || (checkConnect
+ && !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getProfileConnectionState"))) {
+ return STATE_DISCONNECTED;
+ }
+
+ return service.getAdapterProperties().getProfileConnectionState(profile);
+ }
+
+ @Override
+ public boolean createBond(BluetoothDevice device, int transport, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "createBond")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService createBond")) {
+ return false;
+ }
+
+ Log.i(
+ TAG,
+ "createBond:"
+ + (" device=" + device)
+ + (" transport=" + transport)
+ + (" from " + Utils.getUidPidString()));
+ return service.createBond(device, transport, null, null, source.getPackageName());
+ }
+
+ @Override
+ public boolean createBondOutOfBand(
+ BluetoothDevice device,
+ int transport,
+ OobData remoteP192Data,
+ OobData remoteP256Data,
+ AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "createBond")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService createBond")) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ Log.i(
+ TAG,
+ "createBondOutOfBand:"
+ + (" device=" + device)
+ + (" transport=" + transport)
+ + (" from " + Utils.getUidPidString()));
+ return service.createBond(
+ device, transport, remoteP192Data, remoteP256Data, source.getPackageName());
+ }
+
+ @Override
+ public boolean cancelBondProcess(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "cancelBondProcess")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService cancelBondProcess")) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ Log.i(TAG, "cancelBondProcess: device=" + device + ", from " + Utils.getUidPidString());
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ if (deviceProp != null) {
+ deviceProp.setBondingInitiatedLocally(false);
+ }
+
+ service.logUserBondResponse(device, false, source);
+ return service.getNative().cancelBond(getBytesFromAddress(device.getAddress()));
+ }
+
+ @Override
+ public boolean removeBond(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "removeBond")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService removeBond")) {
+ return false;
+ }
+
+ Log.i(TAG, "removeBond: device=" + device + ", from " + Utils.getUidPidString());
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDED) {
+ Log.w(
+ TAG,
+ device
+ + " cannot be removed since "
+ + ((deviceProp == null)
+ ? "properties are empty"
+ : "bond state is " + deviceProp.getBondState()));
+ return false;
+ }
+ service.logUserBondResponse(device, false, source);
+ service.getBondAttemptCallerInfo().remove(device.getAddress());
+ service.getPhonePolicy().ifPresent(policy -> policy.onRemoveBondRequest(device));
+ deviceProp.setBondingInitiatedLocally(false);
+
+ Message msg = service.getBondStateMachine().obtainMessage(BondStateMachine.REMOVE_BOND);
+ msg.obj = device;
+ service.getBondStateMachine().sendMessage(msg);
+ return true;
+ }
+
+ @Override
+ public int getBondState(BluetoothDevice device, AttributionSource source) {
+ // don't check caller, may be called from system UI
+ AdapterService service = getService();
+ if (service == null
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getBondState")) {
+ return BluetoothDevice.BOND_NONE;
+ }
+
+ return service.getBondState(device);
+ }
+
+ @Override
+ public boolean isBondingInitiatedLocally(BluetoothDevice device, AttributionSource source) {
+ // don't check caller, may be called from system UI
+ AdapterService service = getService();
+ if (service == null
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService isBondingInitiatedLocally")) {
+ return false;
+ }
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ return deviceProp != null && deviceProp.isBondingInitiatedLocally();
+ }
+
+ @Override
+ public void generateLocalOobData(
+ int transport, IBluetoothOobDataCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "generateLocalOobData")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.generateLocalOobData(transport, callback);
+ }
+
+ @Override
+ public long getSupportedProfiles(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return 0;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return Config.getSupportedProfilesBitMask();
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getConnectionState")) {
+ return BluetoothDevice.CONNECTION_STATE_DISCONNECTED;
+ }
+
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public int getConnectionHandle(
+ BluetoothDevice device, int transport, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getConnectionHandle")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothDevice.ERROR;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getConnectionHandle(device, transport);
+ }
+
+ @Override
+ public boolean canBondWithoutDialog(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.canBondWithoutDialog(device);
+ }
+
+ @Override
+ public String getPackageNameOfBondingApplication(
+ BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+
+ if (service == null || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getPackageNameOfBondingApplication(device);
+ }
+
+ @Override
+ public boolean removeActiveDevice(@ActiveDeviceUse int profiles, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "removeActiveDevice")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ Log.i(
+ TAG,
+ "removeActiveDevice: profiles=" + profiles + ", from " + Utils.getUidPidString());
+ return service.setActiveDevice(null, profiles);
+ }
+
+ @Override
+ public boolean setActiveDevice(
+ BluetoothDevice device, @ActiveDeviceUse int profiles, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setActiveDevice")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ Log.i(
+ TAG,
+ "setActiveDevice: device="
+ + device
+ + ", profiles="
+ + profiles
+ + ", from "
+ + Utils.getUidPidString());
+
+ return service.setActiveDevice(device, profiles);
+ }
+
+ @Override
+ public List<BluetoothDevice> getActiveDevices(
+ @ActiveDeviceProfile int profile, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getActiveDevices")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getActiveDevices(profile);
+ }
+
+ @Override
+ public int connectAllEnabledProfiles(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !service.isEnabled()) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "connectAllEnabledProfiles")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ if (device == null) {
+ throw new IllegalArgumentException("device cannot be null");
+ }
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+
+ service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ Log.i(
+ TAG,
+ "connectAllEnabledProfiles: device="
+ + device
+ + ", from "
+ + Utils.getUidPidString());
+ MetricsLogger.getInstance()
+ .logBluetoothEvent(
+ device,
+ BluetoothStatsLog
+ .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__INITIATOR_CONNECTION,
+ BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__START,
+ source.getUid());
+
+ try {
+ return service.connectAllEnabledProfiles(device);
+ } catch (Exception e) {
+ Log.v(TAG, "connectAllEnabledProfiles() failed", e);
+ SneakyThrow.sneakyThrow(e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public int disconnectAllEnabledProfiles(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "disconnectAllEnabledProfiles")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ if (device == null) {
+ throw new IllegalArgumentException("device cannot be null");
+ }
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ Log.i(
+ TAG,
+ "disconnectAllEnabledProfiles: device="
+ + device
+ + ", from "
+ + Utils.getUidPidString());
+
+ try {
+ return service.disconnectAllEnabledProfiles(device);
+ } catch (Exception e) {
+ Log.v(TAG, "disconnectAllEnabledProfiles() failed", e);
+ SneakyThrow.sneakyThrow(e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String getRemoteName(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteName")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getRemoteName")) {
+ return null;
+ }
+
+ return service.getRemoteName(device);
+ }
+
+ @Override
+ public int getRemoteType(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteType")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getRemoteType")) {
+ return BluetoothDevice.DEVICE_TYPE_UNKNOWN;
+ }
+
+ return service.getRemoteType(device);
+ }
+
+ @Override
+ public String getRemoteAlias(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteAlias")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getRemoteAlias")) {
+ return null;
+ }
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ return deviceProp != null ? deviceProp.getAlias() : null;
+ }
+
+ @Override
+ public int setRemoteAlias(BluetoothDevice device, String name, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setRemoteAlias")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ if (name != null && name.isEmpty()) {
+ throw new IllegalArgumentException("alias cannot be the empty string");
+ }
+
+ if (!Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService setRemoteAlias")) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+
+ Utils.enforceCdmAssociationIfNotBluetoothPrivileged(
+ service, service.getCompanionDeviceManager(), source, device);
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ if (deviceProp == null) {
+ return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
+ }
+ deviceProp.setAlias(device, name);
+ return BluetoothStatusCodes.SUCCESS;
+ }
+
+ @Override
+ public int getRemoteClass(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteClass")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getRemoteClass")) {
+ return 0;
+ }
+
+ return service.getRemoteClass(device);
+ }
+
+ @Override
+ public List<ParcelUuid> getRemoteUuids(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getRemoteUuids")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getRemoteUuids")) {
+ return Collections.emptyList();
+ }
+
+ final ParcelUuid[] parcels = service.getRemoteUuids(device);
+ if (parcels == null) {
+ return null;
+ }
+ return Arrays.asList(parcels);
+ }
+
+ @Override
+ public boolean fetchRemoteUuids(
+ BluetoothDevice device, int transport, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "fetchRemoteUuids")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService fetchRemoteUuids")) {
+ return false;
+ }
+ if (transport != TRANSPORT_AUTO) {
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ }
+
+ Log.i(
+ TAG,
+ "fetchRemoteUuids: device="
+ + device
+ + ", transport="
+ + transport
+ + ", from "
+ + Utils.getUidPidString());
+
+ service.getRemoteDevices().fetchUuids(device, transport);
+ MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_FETCH_UUID_REQUEST, 1);
+ return true;
+ }
+
+ @Override
+ public boolean setPin(
+ BluetoothDevice device,
+ boolean accept,
+ int len,
+ byte[] pinCode,
+ AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPin")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService setPin")) {
+ return false;
+ }
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ // Only allow setting a pin in bonding state, or bonded state in case of security
+ // upgrade.
+ if (deviceProp == null || !deviceProp.isBondingOrBonded()) {
+ Log.e(TAG, "setPin: device=" + device + ", not bonding");
+ return false;
+ }
+ if (pinCode.length != len) {
+ android.util.EventLog.writeEvent(
+ 0x534e4554, "139287605", -1, "PIN code length mismatch");
+ return false;
+ }
+ service.logUserBondResponse(device, accept, source);
+ Log.i(
+ TAG,
+ "setPin: device="
+ + device
+ + ", accept="
+ + accept
+ + ", from "
+ + Utils.getUidPidString());
+ return service.getNative()
+ .pinReply(getBytesFromAddress(device.getAddress()), accept, len, pinCode);
+ }
+
+ @Override
+ public boolean setPasskey(
+ BluetoothDevice device,
+ boolean accept,
+ int len,
+ byte[] passkey,
+ AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPasskey")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService setPasskey")) {
+ return false;
+ }
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ if (deviceProp == null || !deviceProp.isBonding()) {
+ Log.e(TAG, "setPasskey: device=" + device + ", not bonding");
+ return false;
+ }
+ if (passkey.length != len) {
+ android.util.EventLog.writeEvent(
+ 0x534e4554, "139287605", -1, "Passkey length mismatch");
+ return false;
+ }
+ service.logUserBondResponse(device, accept, source);
+ Log.i(
+ TAG,
+ "setPasskey: device="
+ + device
+ + ", accept="
+ + accept
+ + ", from "
+ + Utils.getUidPidString());
+
+ return service.getNative()
+ .sspReply(
+ getBytesFromAddress(device.getAddress()),
+ AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY,
+ accept,
+ Utils.byteArrayToInt(passkey));
+ }
+
+ @Override
+ public boolean setPairingConfirmation(
+ BluetoothDevice device, boolean accept, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPairingConfirmation")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ if (deviceProp == null || !deviceProp.isBonding()) {
+ Log.e(TAG, "setPairingConfirmation: device=" + device + ", not bonding");
+ return false;
+ }
+ service.logUserBondResponse(device, accept, source);
+ Log.i(
+ TAG,
+ "setPairingConfirmation: device="
+ + device
+ + ", accept="
+ + accept
+ + ", from "
+ + Utils.getUidPidString());
+
+ return service.getNative()
+ .sspReply(
+ getBytesFromAddress(device.getAddress()),
+ AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+ accept,
+ 0);
+ }
+
+ @Override
+ public boolean getSilenceMode(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getSilenceMode")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getSilenceDeviceManager().getSilenceMode(device);
+ }
+
+ @Override
+ public boolean setSilenceMode(
+ BluetoothDevice device, boolean silence, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setSilenceMode")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.getSilenceDeviceManager().setSilenceMode(device, silence);
+ return true;
+ }
+
+ @Override
+ public int getPhonebookAccessPermission(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "getPhonebookAccessPermission")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getPhonebookAccessPermission")) {
+ return BluetoothDevice.ACCESS_UNKNOWN;
+ }
+
+ return service.getPhonebookAccessPermission(device);
+ }
+
+ @Override
+ public boolean setPhonebookAccessPermission(
+ BluetoothDevice device, int value, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "setPhonebookAccessPermission")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.setPhonebookAccessPermission(device, value);
+ return true;
+ }
+
+ @Override
+ public int getMessageAccessPermission(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getMessageAccessPermission")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getMessageAccessPermission")) {
+ return BluetoothDevice.ACCESS_UNKNOWN;
+ }
+
+ return service.getMessageAccessPermission(device);
+ }
+
+ @Override
+ public boolean setMessageAccessPermission(
+ BluetoothDevice device, int value, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setMessageAccessPermission")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.setMessageAccessPermission(device, value);
+ return true;
+ }
+
+ @Override
+ public int getSimAccessPermission(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getSimAccessPermission")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getSimAccessPermission")) {
+ return BluetoothDevice.ACCESS_UNKNOWN;
+ }
+
+ return service.getSimAccessPermission(device);
+ }
+
+ @Override
+ public boolean setSimAccessPermission(
+ BluetoothDevice device, int value, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setSimAccessPermission")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.setSimAccessPermission(device, value);
+ return true;
+ }
+
+ @Override
+ public void logL2capcocServerConnection(
+ BluetoothDevice device,
+ int port,
+ boolean isSecured,
+ int result,
+ long socketCreationTimeMillis,
+ long socketCreationLatencyMillis,
+ long socketConnectionTimeMillis,
+ long timeoutMillis) {
+ AdapterService service = getService();
+ if (service == null) {
+ return;
+ }
+ service.logL2capcocServerConnection(
+ device,
+ port,
+ isSecured,
+ result,
+ socketCreationTimeMillis,
+ socketCreationLatencyMillis,
+ socketConnectionTimeMillis,
+ timeoutMillis,
+ Binder.getCallingUid());
+ }
+
+ @Override
+ public IBluetoothSocketManager getSocketManager() {
+ AdapterService service = getService();
+ if (service == null) {
+ return null;
+ }
+
+ return IBluetoothSocketManager.Stub.asInterface(service.getBluetoothSocketManagerBinder());
+ }
+
+ @Override
+ public void logL2capcocClientConnection(
+ BluetoothDevice device,
+ int port,
+ boolean isSecured,
+ int result,
+ long socketCreationTimeNanos,
+ long socketCreationLatencyNanos,
+ long socketConnectionTimeNanos) {
+ AdapterService service = getService();
+ if (service == null) {
+ return;
+ }
+ service.logL2capcocClientConnection(
+ device,
+ port,
+ isSecured,
+ result,
+ socketCreationTimeNanos,
+ socketCreationLatencyNanos,
+ socketConnectionTimeNanos,
+ Binder.getCallingUid());
+ }
+
+ @Override
+ public void logRfcommConnectionAttempt(
+ BluetoothDevice device,
+ boolean isSecured,
+ int resultCode,
+ long socketCreationTimeNanos,
+ boolean isSerialPort) {
+ AdapterService service = getService();
+ if (service == null) {
+ return;
+ }
+ service.logRfcommConnectionAttempt(
+ device,
+ isSecured,
+ resultCode,
+ socketCreationTimeNanos,
+ isSerialPort,
+ Binder.getCallingUid());
+ }
+
+ @Override
+ public boolean sdpSearch(BluetoothDevice device, ParcelUuid uuid, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "sdpSearch")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService sdpSearch")) {
+ return false;
+ }
+ return service.sdpSearch(device, uuid);
+ }
+
+ @Override
+ public int getBatteryLevel(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getBatteryLevel")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getBatteryLevel")) {
+ return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ }
+
+ DeviceProperties deviceProp = service.getRemoteDevices().getDeviceProperties(device);
+ if (deviceProp == null) {
+ return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ }
+ return deviceProp.getBatteryLevel();
+ }
+
+ @Override
+ public int getMaxConnectedAudioDevices(AttributionSource source) {
+ // don't check caller, may be called from system UI
+ AdapterService service = getService();
+ if (service == null
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getMaxConnectedAudioDevices")) {
+ return -1;
+ }
+
+ return service.getMaxConnectedAudioDevices();
+ }
+
+ @Override
+ public boolean factoryReset(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.factoryReset();
+ }
+
+ @Override
+ public void registerBluetoothConnectionCallback(
+ IBluetoothConnectionCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "registerBluetoothConnectionCallback")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.getBluetoothConnectionCallbacks().register(callback);
+ }
+
+ @Override
+ public void unregisterBluetoothConnectionCallback(
+ IBluetoothConnectionCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "unregisterBluetoothConnectionCallback")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.getBluetoothConnectionCallbacks().unregister(callback);
+ }
+
+ @Override
+ public void registerCallback(IBluetoothCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "registerCallback")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.registerRemoteCallback(callback);
+ }
+
+ @Override
+ public void unregisterCallback(IBluetoothCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "unregisterCallback")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.unregisterRemoteCallback(callback);
+ }
+
+ @Override
+ public boolean isMultiAdvertisementSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+
+ int val = service.getAdapterProperties().getNumOfAdvertisementInstancesSupported();
+ return val >= MIN_ADVT_INSTANCES_FOR_MA;
+ }
+
+ /**
+ * This method has an associated binder cache. The invalidation methods must be changed if the
+ * logic behind this method changes.
+ */
+ @Override
+ public boolean isOffloadedFilteringSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+
+ int val = service.getNumOfOffloadedScanFilterSupported();
+ return val >= MIN_OFFLOADED_FILTERS;
+ }
+
+ @Override
+ public boolean isOffloadedScanBatchingSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+
+ int val = service.getOffloadedScanResultStorage();
+ return val >= MIN_OFFLOADED_SCAN_STORAGE_BYTES;
+ }
+
+ @Override
+ public boolean isLe2MPhySupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+
+ return service.isLe2MPhySupported();
+ }
+
+ @Override
+ public boolean isLeCodedPhySupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+
+ return service.isLeCodedPhySupported();
+ }
+
+ @Override
+ public boolean isLeExtendedAdvertisingSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+
+ return service.isLeExtendedAdvertisingSupported();
+ }
+
+ @Override
+ public boolean isLePeriodicAdvertisingSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+
+ return service.isLePeriodicAdvertisingSupported();
+ }
+
+ @Override
+ public int isLeAudioSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+
+ Set<Integer> supportedProfileServices =
+ Arrays.stream(Config.getSupportedProfiles()).boxed().collect(Collectors.toSet());
+ int[] leAudioUnicastProfiles = Config.getLeAudioUnicastProfiles();
+
+ if (Arrays.stream(leAudioUnicastProfiles).allMatch(supportedProfileServices::contains)) {
+ return BluetoothStatusCodes.FEATURE_SUPPORTED;
+ }
+
+ return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
+ }
+
+ @Override
+ public int isLeAudioBroadcastSourceSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+
+ long supportBitMask = Config.getSupportedProfilesBitMask();
+ if ((supportBitMask & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) != 0) {
+ return BluetoothStatusCodes.FEATURE_SUPPORTED;
+ }
+
+ return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
+ }
+
+ @Override
+ public int isLeAudioBroadcastAssistantSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+
+ int[] supportedProfileServices = Config.getSupportedProfiles();
+
+ if (Arrays.stream(supportedProfileServices)
+ .anyMatch(
+ profileId -> profileId == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)) {
+ return BluetoothStatusCodes.FEATURE_SUPPORTED;
+ }
+
+ return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
+ }
+
+ @Override
+ public int isDistanceMeasurementSupported(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ } else if (!callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "isDistanceMeasurementSupported")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ } else if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return BluetoothStatusCodes.FEATURE_SUPPORTED;
+ }
+
+ @Override
+ public int getLeMaximumAdvertisingDataLength() {
+ AdapterService service = getService();
+ if (service == null) {
+ return 0;
+ }
+
+ return service.getLeMaximumAdvertisingDataLength();
+ }
+
+ @Override
+ public boolean isActivityAndEnergyReportingSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+
+ return service.getAdapterProperties().isActivityAndEnergyReportingSupported();
+ }
+
+ @Override
+ public BluetoothActivityEnergyInfo reportActivityInfo(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.reportActivityInfo();
+ }
+
+ @Override
+ public boolean registerMetadataListener(
+ IBluetoothMetadataListener listener, BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(listener);
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "registerMetadataListener")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.getHandler()
+ .post(
+ () ->
+ service.getMetadataListeners()
+ .computeIfAbsent(device, k -> new RemoteCallbackList())
+ .register(listener));
+
+ return true;
+ }
+
+ @Override
+ public boolean unregisterMetadataListener(
+ IBluetoothMetadataListener listener, BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(listener);
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "unregisterMetadataListener")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.getHandler()
+ .post(
+ () ->
+ service.getMetadataListeners()
+ .computeIfPresent(
+ device,
+ (k, v) -> {
+ v.unregister(listener);
+ if (v.getRegisteredCallbackCount() == 0) {
+ return null;
+ }
+ return v;
+ }));
+ return true;
+ }
+
+ @Override
+ public boolean setMetadata(
+ BluetoothDevice device, int key, byte[] value, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setMetadata")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setMetadata(device, key, value);
+ }
+
+ @Override
+ public byte[] getMetadata(BluetoothDevice device, int key, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getMetadata")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getMetadata(device, key);
+ }
+
+ @Override
+ public int isRequestAudioPolicyAsSinkSupported(
+ BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "isRequestAudioPolicyAsSinkSupported")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.FEATURE_NOT_CONFIGURED;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isRequestAudioPolicyAsSinkSupported(device);
+ }
+
+ @Override
+ public int requestAudioPolicyAsSink(
+ BluetoothDevice device, BluetoothSinkAudioPolicy policies, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ } else if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "requestAudioPolicyAsSink")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ } else if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.requestAudioPolicyAsSink(device, policies);
+ }
+
+ @Override
+ public BluetoothSinkAudioPolicy getRequestedAudioPolicyAsSink(
+ BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "getRequestedAudioPolicyAsSink")
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getRequestedAudioPolicyAsSink(device);
+ }
+
+ @Override
+ public void requestActivityInfo(
+ IBluetoothActivityEnergyInfoListener listener, AttributionSource source) {
+ BluetoothActivityEnergyInfo info = reportActivityInfo(source);
+ try {
+ listener.onBluetoothActivityEnergyInfoAvailable(info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onBluetoothActivityEnergyInfo: RemoteException", e);
+ }
+ }
+
+ @Override
+ public void bleOnToOn(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "bleOnToOn")) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.bleOnToOn();
+ }
+
+ @Override
+ public void bleOnToOff(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "bleOnToOff")) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.bleOnToOff();
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, String[] args) {
+ PrintWriter writer = new PrintWriter(new FileOutputStream(fd));
+ AdapterService service = getService();
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(DUMP, null);
+
+ service.dump(fd, writer, args);
+ writer.close();
+ }
+
+ @Override
+ public boolean allowLowLatencyAudio(boolean allowed, BluetoothDevice device) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "allowLowLatencyAudio")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service,
+ Utils.getCallingAttributionSource(service),
+ "AdapterService allowLowLatencyAudio")) {
+ return false;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.allowLowLatencyAudio(allowed, device);
+ }
+
+ @Override
+ public int startRfcommListener(
+ String name, ParcelUuid uuid, PendingIntent pendingIntent, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "startRfcommListener")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService startRfcommListener")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.startRfcommListener(name, uuid, pendingIntent, source);
+ }
+
+ @Override
+ public int stopRfcommListener(ParcelUuid uuid, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "stopRfcommListener")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService stopRfcommListener")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.stopRfcommListener(uuid, source);
+ }
+
+ @Override
+ public IncomingRfcommSocketInfo retrievePendingSocketForServiceRecord(
+ ParcelUuid uuid, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "retrievePendingSocketForServiceRecord")
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService retrievePendingSocketForServiceRecord")) {
+ return null;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.retrievePendingSocketForServiceRecord(uuid, source);
+ }
+
+ @Override
+ public void setForegroundUserId(int userId, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service,
+ Utils.getCallingAttributionSource(mService),
+ "AdapterService setForegroundUserId")) {
+ return;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ Utils.setForegroundUserId(userId);
+ }
+
+ @Override
+ public int setPreferredAudioProfiles(
+ BluetoothDevice device, Bundle modeToProfileBundle, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setPreferredAudioProfiles")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ requireNonNull(device);
+ requireNonNull(modeToProfileBundle);
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (service.getBondState(device) != BluetoothDevice.BOND_BONDED) {
+ return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.setPreferredAudioProfiles(device, modeToProfileBundle);
+ }
+
+ @Override
+ public Bundle getPreferredAudioProfiles(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return Bundle.EMPTY;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "getPreferredAudioProfiles")) {
+ return Bundle.EMPTY;
+ }
+ requireNonNull(device);
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (service.getBondState(device) != BluetoothDevice.BOND_BONDED) {
+ return Bundle.EMPTY;
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return Bundle.EMPTY;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getPreferredAudioProfiles(device);
+ }
+
+ @Override
+ public int notifyActiveDeviceChangeApplied(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystem(TAG, "notifyActiveDeviceChangeApplied")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ requireNonNull(device);
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (service.getBondState(device) != BluetoothDevice.BOND_BONDED) {
+ return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.notifyActiveDeviceChangeApplied(device);
+ }
+
+ @Override
+ public int isDualModeAudioEnabled(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ if (!Utils.isDualModeAudioEnabled()) {
+ return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
+ }
+
+ return BluetoothStatusCodes.SUCCESS;
+ }
+
+ @Override
+ public int registerPreferredAudioProfilesChangedCallback(
+ IBluetoothPreferredAudioProfilesCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "registerPreferredAudioProfilesChangedCallback")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ requireNonNull(callback);
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ // If LE only mode is enabled, the dual mode audio feature is disabled
+ if (!Utils.isDualModeAudioEnabled()) {
+ return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
+ }
+
+ service.getPreferredAudioProfilesCallbacks().register(callback);
+ return BluetoothStatusCodes.SUCCESS;
+ }
+
+ @Override
+ public int unregisterPreferredAudioProfilesChangedCallback(
+ IBluetoothPreferredAudioProfilesCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "unregisterPreferredAudioProfilesChangedCallback")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ requireNonNull(callback);
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ if (!service.getPreferredAudioProfilesCallbacks().unregister(callback)) {
+ Log.e(
+ TAG,
+ "unregisterPreferredAudioProfilesChangedCallback: callback was never "
+ + "registered");
+ return BluetoothStatusCodes.ERROR_CALLBACK_NOT_REGISTERED;
+ }
+ return BluetoothStatusCodes.SUCCESS;
+ }
+
+ @Override
+ public int registerBluetoothQualityReportReadyCallback(
+ IBluetoothQualityReportReadyCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "registerBluetoothQualityReportReadyCallback")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ requireNonNull(callback);
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ service.getBluetoothQualityReportReadyCallbacks().register(callback);
+ return BluetoothStatusCodes.SUCCESS;
+ }
+
+ @Override
+ public int unregisterBluetoothQualityReportReadyCallback(
+ IBluetoothQualityReportReadyCallback callback, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "unregisterBluetoothQualityReportReadyCallback")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ requireNonNull(callback);
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ if (!service.getBluetoothQualityReportReadyCallbacks().unregister(callback)) {
+ Log.e(
+ TAG,
+ "unregisterBluetoothQualityReportReadyCallback: callback was never "
+ + "registered");
+ return BluetoothStatusCodes.ERROR_CALLBACK_NOT_REGISTERED;
+ }
+ return BluetoothStatusCodes.SUCCESS;
+ }
+
+ @Override
+ public void registerHciVendorSpecificCallback(
+ IBluetoothHciVendorSpecificCallback callback, int[] eventCodes) {
+ AdapterService service = getService();
+ if (service == null) {
+ return;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "registerHciVendorSpecificCallback")) {
+ throw new SecurityException("not allowed");
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ requireNonNull(callback);
+ requireNonNull(eventCodes);
+
+ Set<Integer> eventCodesSet = Arrays.stream(eventCodes).boxed().collect(Collectors.toSet());
+ if (eventCodesSet.stream()
+ .anyMatch((n) -> (n < 0) || (n >= 0x52 && n < 0x60) || (n > 0xff))) {
+ throw new IllegalArgumentException("invalid vendor-specific event code");
+ }
+
+ service.getBluetoothHciVendorSpecificDispatcher().register(callback, eventCodesSet);
+ }
+
+ @Override
+ public void unregisterHciVendorSpecificCallback(IBluetoothHciVendorSpecificCallback callback) {
+ AdapterService service = getService();
+ if (service == null) {
+ return;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "unregisterHciVendorSpecificCallback")) {
+ throw new SecurityException("not allowed");
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ requireNonNull(callback);
+
+ service.getBluetoothHciVendorSpecificDispatcher().unregister(callback);
+ }
+
+ @Override
+ public void sendHciVendorSpecificCommand(
+ int ocf, byte[] parameters, IBluetoothHciVendorSpecificCallback callback) {
+ AdapterService service = getService();
+ if (service == null) {
+ return;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "sendHciVendorSpecificCommand")) {
+ throw new SecurityException("not allowed");
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ // Open this no-op android command for test purpose
+ int getVendorCapabilitiesOcf = 0x153;
+ if (ocf < 0
+ || (ocf >= 0x150 && ocf < 0x160 && ocf != getVendorCapabilitiesOcf)
+ || (ocf > 0x3ff)) {
+ throw new IllegalArgumentException("invalid vendor-specific event code");
+ }
+ requireNonNull(parameters);
+ if (parameters.length > 255) {
+ throw new IllegalArgumentException("Parameters size is too big");
+ }
+
+ Optional<byte[]> cookie =
+ service.getBluetoothHciVendorSpecificDispatcher().getRegisteredCookie(callback);
+ if (!cookie.isPresent()) {
+ Log.e(TAG, "send command without registered callback");
+ throw new IllegalStateException("callback not registered");
+ }
+
+ service.getBluetoothHciVendorSpecificNativeInterface()
+ .sendCommand(ocf, parameters, cookie.get());
+ }
+
+ @Override
+ public int getOffloadedTransportDiscoveryDataScanSupported(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "getOffloadedTransportDiscoveryDataScanSupported")
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, "getOffloadedTransportDiscoveryDataScanSupported")) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.getOffloadedTransportDiscoveryDataScanSupported();
+ }
+
+ @Override
+ public boolean isMediaProfileConnected(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null
+ || !Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService.isMediaProfileConnected")) {
+ return false;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.isMediaProfileConnected();
+ }
+
+ @Override
+ public IBinder getBluetoothGatt() {
+ AdapterService service = getService();
+ return service == null ? null : service.getBluetoothGatt();
+ }
+
+ @Override
+ public IBinder getBluetoothScan() {
+ AdapterService service = getService();
+ return service == null ? null : service.getBluetoothScan();
+ }
+
+ @Override
+ public void unregAllGattClient(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.unregAllGattClient(source);
+ }
+
+ @Override
+ public IBinder getProfile(int profileId) {
+ AdapterService service = getService();
+ if (service == null) {
+ return null;
+ }
+
+ return service.getProfile(profileId);
+ }
+
+ @Override
+ public int setActiveAudioDevicePolicy(
+ BluetoothDevice device, int activeAudioDevicePolicy, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setActiveAudioDevicePolicy")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getDatabaseManager()
+ .setActiveAudioDevicePolicy(device, activeAudioDevicePolicy);
+ }
+
+ @Override
+ public int getActiveAudioDevicePolicy(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "getActiveAudioDevicePolicy")) {
+ throw new IllegalStateException(
+ "Caller is not the system or part of the active/managed user");
+ }
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getDatabaseManager().getActiveAudioDevicePolicy(device);
+ }
+
+ @Override
+ public int setMicrophonePreferredForCalls(
+ BluetoothDevice device, boolean enabled, AttributionSource source) {
+ requireNonNull(device);
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setMicrophonePreferredForCalls")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ }
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService setMicrophonePreferredForCalls")) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getDatabaseManager().setMicrophonePreferredForCalls(device, enabled);
+ }
+
+ @Override
+ public boolean isMicrophonePreferredForCalls(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ AdapterService service = getService();
+ if (service == null) {
+ return true;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "isMicrophonePreferredForCalls")) {
+ throw new IllegalStateException(
+ "Caller is not the system or part of the active/managed user");
+ }
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService isMicrophonePreferredForCalls")) {
+ return true;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getDatabaseManager().isMicrophonePreferredForCalls(device);
+ }
+
+ @Override
+ public boolean isLeCocSocketOffloadSupported(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isLeCocSocketOffloadSupported();
+ }
+
+ @Override
+ public boolean isRfcommSocketOffloadSupported(AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isRfcommSocketOffloadSupported();
+ }
+
+ @Override
+ public IBinder getBluetoothAdvertise() {
+ AdapterService service = getService();
+ return service == null ? null : service.getBluetoothAdvertise();
+ }
+
+ @Override
+ public IBinder getDistanceMeasurement() {
+ AdapterService service = getService();
+ return service == null ? null : service.getDistanceMeasurement();
+ }
+
+ @Override
+ public int getKeyMissingCount(BluetoothDevice device, AttributionSource source) {
+ AdapterService service = getService();
+ if (service == null) {
+ return -1;
+ }
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "getKeyMissingCount")) {
+ throw new IllegalStateException(
+ "Caller is not the system or part of the active/managed user");
+ }
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+ if (!Utils.checkConnectPermissionForDataDelivery(
+ service, source, "AdapterService getKeyMissingCount")) {
+ return -1;
+ }
+
+ return service.getDatabaseManager().getKeyMissingCount(device);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
index 4acfefc0d8..90d9a4bee6 100644
--- a/android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
@@ -31,7 +31,6 @@ import android.util.Log;
import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.hfp.HeadsetService;
-import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.HashMap;
@@ -205,7 +204,6 @@ public class SilenceDeviceManager {
mSilenceDevices.clear();
}
- @VisibleForTesting
boolean setSilenceMode(BluetoothDevice device, boolean silence) {
Log.d(TAG, "setSilenceMode: " + device + ", " + silence);
mHandler.obtainMessage(
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java
index e7fa638eea..955f579ce4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothOobDataCallback;
@@ -37,28 +38,29 @@ import org.mockito.Mockito;
import java.io.FileDescriptor;
+/** Test cases for {@link AdapterServiceBinder}. */
public class AdapterServiceBinderTest {
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private AdapterService mService;
@Mock private AdapterProperties mAdapterProperties;
- private AdapterService.AdapterServiceBinder mBinder;
+ private AdapterServiceBinder mBinder;
private AttributionSource mAttributionSource;
@Before
public void setUp() {
- mService.mAdapterProperties = mAdapterProperties;
+ when(mService.getAdapterProperties()).thenReturn(mAdapterProperties);
doReturn(true).when(mService).isAvailable();
doNothing().when(mService).enforceCallingOrSelfPermission(any(), any());
- mBinder = new AdapterService.AdapterServiceBinder(mService);
+ mBinder = new AdapterServiceBinder(mService);
mAttributionSource = new AttributionSource.Builder(0).build();
}
@Test
public void getAddress() {
mBinder.getAddress(mAttributionSource);
- verify(mService.mAdapterProperties).getAddress();
+ verify(mAdapterProperties).getAddress();
}
@Test
@@ -116,7 +118,7 @@ public class AdapterServiceBinderTest {
@Test
public void isActivityAndEnergyReportingSupported() {
mBinder.isActivityAndEnergyReportingSupported();
- verify(mService.mAdapterProperties).isActivityAndEnergyReportingSupported();
+ verify(mAdapterProperties).isActivityAndEnergyReportingSupported();
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
index 35190708de..a9e01b3086 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
@@ -113,6 +113,7 @@ import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
+/** Test cases for {@link AdapterService}. */
@MediumTest
@RunWith(ParameterizedAndroidJunit4.class)
public class AdapterServiceTest {
diff --git a/apex/Android.bp b/apex/Android.bp
index c2aabcf0b8..bf75163768 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -84,7 +84,6 @@ apex {
],
key: "com.android.bt.key",
certificate: ":com.android.bt.certificate",
- updatable: true,
compressible: false,
visibility: ["//packages/modules/common/build"],
}
diff --git a/system/gd/hal/hci_hal.h b/system/gd/hal/hci_hal.h
index 49f007ba55..2c028c3b6f 100644
--- a/system/gd/hal/hci_hal.h
+++ b/system/gd/hal/hci_hal.h
@@ -51,6 +51,10 @@ public:
// Send an ISO data packet from the controller to the host
// @param data the ISO HCI packet to be passed to the host stack
virtual void isoDataReceived(HciPacket data) = 0;
+
+ // This function is invoked when the controller encounters an error requiring
+ // the Bluetooth stack to initiate a reset.
+ virtual void controllerNeedsReset() {}
};
// Mirrors hardware/interfaces/bluetooth/1.0/IBluetoothHci.hal in Android
@@ -101,9 +105,6 @@ public:
// Get the MSFT opcode (as specified in Microsoft-defined Bluetooth HCI
// extensions)
virtual uint16_t getMsftOpcode() { return 0; }
-
- // Mark the controller as broken to prevent further read / write operation.
- virtual void markControllerBroken() { return; }
};
// LINT.ThenChange(fuzz/fuzz_hci_hal.h)
diff --git a/system/gd/hal/hci_hal_host.cc b/system/gd/hal/hci_hal_host.cc
index f441fcf212..bf020a2e02 100644
--- a/system/gd/hal/hci_hal_host.cc
+++ b/system/gd/hal/hci_hal_host.cc
@@ -262,9 +262,6 @@ public:
void sendHciCommand(HciPacket command) override {
std::lock_guard<std::mutex> lock(api_mutex_);
- if (controller_broken_) {
- return;
- }
log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
std::vector<uint8_t> packet = std::move(command);
btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
@@ -275,9 +272,6 @@ public:
void sendAclData(HciPacket data) override {
std::lock_guard<std::mutex> lock(api_mutex_);
- if (controller_broken_) {
- return;
- }
log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
std::vector<uint8_t> packet = std::move(data);
btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
@@ -288,10 +282,6 @@ public:
void sendScoData(HciPacket data) override {
std::lock_guard<std::mutex> lock(api_mutex_);
- if (controller_broken_) {
- return;
- }
-
log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
std::vector<uint8_t> packet = std::move(data);
btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
@@ -302,9 +292,6 @@ public:
void sendIsoData(HciPacket data) override {
std::lock_guard<std::mutex> lock(api_mutex_);
- if (controller_broken_) {
- return;
- }
log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
std::vector<uint8_t> packet = std::move(data);
btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
@@ -317,15 +304,6 @@ public:
return os::Management::getInstance().getVendorSpecificCode(MGMT_VS_OPCODE_MSFT);
}
- void markControllerBroken() override {
- std::lock_guard<std::mutex> lock(api_mutex_);
- if (controller_broken_) {
- log::error("Controller already marked as broken!");
- return;
- }
- controller_broken_ = true;
- }
-
protected:
void ListDependencies(ModuleList* list) const { list->add<LinkClocker>(); }
@@ -337,8 +315,7 @@ protected:
// We don't want to crash when the chipset is broken.
if (sock_fd_ == INVALID_FD) {
log::error("Failed to connect to HCI socket. Aborting HAL initialization process.");
- controller_broken_ = true;
- kill(getpid(), SIGTERM);
+ incoming_packet_callback_->controllerNeedsReset();
return;
}
@@ -392,7 +369,6 @@ private:
std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
SnoopLogger* btsnoop_logger_ = nullptr;
LinkClocker* link_clocker_ = nullptr;
- bool controller_broken_ = false;
void write_to_fd(HciPacket packet) {
// TODO(chromeos-bt-team@): replace this with new queue when it's ready
@@ -414,8 +390,8 @@ private:
hci_outgoing_queue_.pop();
if (bytes_written == -1) {
log::error("Can't write to socket: {}", strerror(errno));
- markControllerBroken();
- kill(getpid(), SIGTERM);
+ incoming_packet_callback_->controllerNeedsReset();
+ return;
}
if (hci_outgoing_queue_.empty()) {
hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_,
@@ -439,15 +415,13 @@ private:
// we don't want crash when the chipset is broken.
if (received_size == -1) {
log::error("Can't receive from socket: {}", strerror(errno));
- markControllerBroken();
- kill(getpid(), SIGTERM);
+ incoming_packet_callback_->controllerNeedsReset();
return;
}
if (received_size == 0) {
log::warn("Can't read H4 header. EOF received");
- markControllerBroken();
- kill(getpid(), SIGTERM);
+ incoming_packet_callback_->controllerNeedsReset();
return;
}
diff --git a/system/gd/hci/hci_layer.cc b/system/gd/hci/hci_layer.cc
index 85d8af092e..da370a7564 100644
--- a/system/gd/hci/hci_layer.cc
+++ b/system/gd/hci/hci_layer.cc
@@ -64,6 +64,11 @@ using std::unique_ptr;
static std::recursive_mutex life_cycle_guard;
static bool life_cycle_stopped = true;
+#ifdef TARGET_FLOSS
+// Signal to indicate the controller needs to be reset.
+const int SIG_RESET_CTRL = SIGUSR1;
+#endif
+
static std::chrono::milliseconds getHciTimeoutMs() {
static auto sHciTimeoutMs = std::chrono::milliseconds(bluetooth::os::GetSystemPropertyUint32Base(
"bluetooth.hci.timeout_milliseconds", HciLayer::kHciTimeoutMs.count()));
@@ -296,12 +301,18 @@ struct HciLayer::impl {
void on_hci_timeout(OpCode op_code) {
#ifdef TARGET_FLOSS
+ std::unique_lock<std::recursive_mutex> lock(life_cycle_guard);
+ if (life_cycle_stopped) {
+ return;
+ }
+
log::warn("Ignoring the timeouted HCI command {}.", OpCodeText(op_code));
- // Terminate the process to trigger controller reset, also mark the controller
- // is broken to prevent further error while terminating.
- auto hal = module_.GetDependency<hal::HciHal>();
- hal->markControllerBroken();
- kill(getpid(), SIGTERM);
+
+ // Terminate the process to trigger controller reset, also stop sending and
+ // processing any incoming packet immediately to prevent further error
+ // while terminating.
+ module_.LifeCycleStop();
+ kill(getpid(), SIG_RESET_CTRL);
return;
#endif
@@ -507,12 +518,11 @@ struct HciLayer::impl {
log::assert_that(event_view.IsValid(), "assert failed: event_view.IsValid()");
#ifdef TARGET_FLOSS
log::warn("Hardware Error Event with code 0x{:02x}", event_view.GetHardwareCode());
- // Sending SIGTERM to process the exception from BT controller.
+ // Sending signal to indicate BT controller needs to reset.
// The Floss daemon will be restarted. HCI reset during restart will clear the
// error state of the BT controller.
- auto hal = module_.GetDependency<hal::HciHal>();
- hal->markControllerBroken();
- kill(getpid(), SIGTERM);
+ module_.LifeCycleStop();
+ kill(getpid(), SIG_RESET_CTRL);
#else
log::fatal("Hardware Error Event with code 0x{:02x}", event_view.GetHardwareCode());
#endif
@@ -619,6 +629,14 @@ struct HciLayer::hal_callbacks : public hal::HciHalCallbacks {
module_.impl_->incoming_iso_buffer_.Enqueue(std::move(iso), module_.GetHandler());
}
+#ifdef TARGET_FLOSS
+ void controllerNeedsReset() override {
+ log::info("Controller needs reset!");
+ module_.LifeCycleStop();
+ kill(getpid(), SIG_RESET_CTRL);
+ }
+#endif
+
HciLayer& module_;
};
@@ -958,6 +976,7 @@ void HciLayer::StartWithNoHalDependencies(Handler* handler) {
void HciLayer::Stop() {
std::unique_lock<std::recursive_mutex> lock(life_cycle_guard);
+ life_cycle_stopped = true;
auto hal = GetDependency<hal::HciHal>();
hal->unregisterIncomingPacketCallback();
delete hal_callbacks_;
@@ -966,7 +985,11 @@ void HciLayer::Stop() {
impl_->sco_queue_.GetDownEnd()->UnregisterDequeue();
impl_->iso_queue_.GetDownEnd()->UnregisterDequeue();
delete impl_;
+}
+// Function to stop sending and handling incoming packets
+void HciLayer::LifeCycleStop() {
+ std::unique_lock<std::recursive_mutex> lock(life_cycle_guard);
life_cycle_stopped = true;
}
diff --git a/system/gd/hci/hci_layer.h b/system/gd/hci/hci_layer.h
index 7f894b2de6..94f706cf6b 100644
--- a/system/gd/hci/hci_layer.h
+++ b/system/gd/hci/hci_layer.h
@@ -149,6 +149,8 @@ protected:
void Stop() override;
+ void LifeCycleStop();
+
virtual void Disconnect(uint16_t handle, ErrorCode reason);
virtual void ReadRemoteVersion(hci::ErrorCode hci_status, uint16_t handle, uint8_t version,
uint16_t manufacturer_name, uint16_t sub_version);
diff --git a/system/gd/rust/linux/service/src/interface_manager.rs b/system/gd/rust/linux/service/src/interface_manager.rs
index 116979de0d..62fdbc8638 100644
--- a/system/gd/rust/linux/service/src/interface_manager.rs
+++ b/system/gd/rust/linux/service/src/interface_manager.rs
@@ -1,27 +1,29 @@
-use dbus::{channel::MatchingReceiver, message::MatchRule, nonblock::SyncConnection};
+use dbus::channel::MatchingReceiver;
+use dbus::message::MatchRule;
+use dbus::nonblock::SyncConnection;
use dbus_crossroads::Crossroads;
use dbus_projection::DisconnectWatcher;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc::{channel, Receiver, Sender};
-use btstack::{
- battery_manager::BatteryManager, battery_provider_manager::BatteryProviderManager,
- bluetooth::Bluetooth, bluetooth_admin::BluetoothAdmin, bluetooth_gatt::BluetoothGatt,
- bluetooth_logging::BluetoothLogging, bluetooth_media::BluetoothMedia,
- bluetooth_qa::BluetoothQA, socket_manager::BluetoothSocketManager, suspend::Suspend,
- APIMessage, BluetoothAPI, Message,
-};
+use btstack::battery_manager::BatteryManager;
+use btstack::battery_provider_manager::BatteryProviderManager;
+use btstack::bluetooth::{Bluetooth, SigData};
+use btstack::bluetooth_admin::BluetoothAdmin;
+use btstack::bluetooth_gatt::BluetoothGatt;
+use btstack::bluetooth_logging::BluetoothLogging;
+use btstack::bluetooth_media::BluetoothMedia;
+use btstack::bluetooth_qa::BluetoothQA;
+use btstack::socket_manager::BluetoothSocketManager;
+use btstack::suspend::Suspend;
+use btstack::{APIMessage, BluetoothAPI};
-use crate::iface_battery_manager;
-use crate::iface_battery_provider_manager;
-use crate::iface_bluetooth;
-use crate::iface_bluetooth_admin;
-use crate::iface_bluetooth_gatt;
-use crate::iface_bluetooth_media;
-use crate::iface_bluetooth_qa;
-use crate::iface_bluetooth_telephony;
-use crate::iface_logging;
+use crate::{
+ iface_battery_manager, iface_battery_provider_manager, iface_bluetooth, iface_bluetooth_admin,
+ iface_bluetooth_gatt, iface_bluetooth_media, iface_bluetooth_qa, iface_bluetooth_telephony,
+ iface_logging,
+};
pub(crate) struct InterfaceManager {}
@@ -50,11 +52,11 @@ impl InterfaceManager {
#[allow(clippy::too_many_arguments)]
pub async fn dispatch(
mut rx: Receiver<APIMessage>,
- tx: Sender<Message>,
virt_index: i32,
conn: Arc<SyncConnection>,
conn_join_handle: tokio::task::JoinHandle<()>,
disconnect_watcher: Arc<Mutex<DisconnectWatcher>>,
+ sig_notifier: Arc<SigData>,
bluetooth: Arc<Mutex<Box<Bluetooth>>>,
bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
bluetooth_gatt: Arc<Mutex<Box<BluetoothGatt>>>,
@@ -93,6 +95,9 @@ impl InterfaceManager {
}),
);
+ *sig_notifier.api_enabled.lock().unwrap() = true;
+ sig_notifier.api_notify.notify_all();
+
// Register D-Bus method handlers of IBluetooth.
let adapter_iface = iface_bluetooth::export_bluetooth_dbus_intf(
conn.clone(),
@@ -246,11 +251,8 @@ impl InterfaceManager {
// To shut down the connection, call _handle.abort() and drop the connection.
conn_join_handle.abort();
drop(conn);
-
- let tx = tx.clone();
- tokio::spawn(async move {
- let _ = tx.send(Message::AdapterShutdown).await;
- });
+ *sig_notifier.api_enabled.lock().unwrap() = false;
+ sig_notifier.api_notify.notify_all();
break;
}
}
diff --git a/system/gd/rust/linux/service/src/main.rs b/system/gd/rust/linux/service/src/main.rs
index f326bb62ee..4fc43d11f3 100644
--- a/system/gd/rust/linux/service/src/main.rs
+++ b/system/gd/rust/linux/service/src/main.rs
@@ -9,22 +9,21 @@ use std::time::Duration;
use tokio::runtime::Builder;
use tokio::sync::mpsc::Sender;
-use bt_topshim::{btif::get_btinterface, topstack};
-use btstack::{
- battery_manager::BatteryManager,
- battery_provider_manager::BatteryProviderManager,
- battery_service::BatteryService,
- bluetooth::{Bluetooth, IBluetooth, SigData},
- bluetooth_admin::BluetoothAdmin,
- bluetooth_gatt::BluetoothGatt,
- bluetooth_logging::BluetoothLogging,
- bluetooth_media::BluetoothMedia,
- bluetooth_qa::BluetoothQA,
- dis::DeviceInformation,
- socket_manager::BluetoothSocketManager,
- suspend::Suspend,
- Message, Stack,
-};
+use bt_topshim::btif::get_btinterface;
+use bt_topshim::topstack;
+use btstack::battery_manager::BatteryManager;
+use btstack::battery_provider_manager::BatteryProviderManager;
+use btstack::battery_service::BatteryService;
+use btstack::bluetooth::{Bluetooth, IBluetooth, SigData};
+use btstack::bluetooth_admin::BluetoothAdmin;
+use btstack::bluetooth_gatt::BluetoothGatt;
+use btstack::bluetooth_logging::BluetoothLogging;
+use btstack::bluetooth_media::BluetoothMedia;
+use btstack::bluetooth_qa::BluetoothQA;
+use btstack::dis::DeviceInformation;
+use btstack::socket_manager::BluetoothSocketManager;
+use btstack::suspend::Suspend;
+use btstack::{Message, Stack};
mod dbus_arg;
mod iface_battery_manager;
@@ -40,6 +39,8 @@ mod interface_manager;
const DBUS_SERVICE_NAME: &str = "org.chromium.bluetooth";
const ADMIN_SETTINGS_FILE_PATH: &str = "/var/lib/bluetooth/admin_policy.json";
+// Time to wait for API unregistration in DBus
+const API_DISABLE_TIMEOUT_MS: Duration = Duration::from_millis(100);
// The maximum ACL disconnect timeout is 3.5s defined by BTA_DM_DISABLE_TIMER_MS
// and BTA_DM_DISABLE_TIMER_RETRIAL_MS
const STACK_TURN_OFF_TIMEOUT_MS: Duration = Duration::from_millis(4000);
@@ -115,6 +116,8 @@ fn main() -> Result<(), Box<dyn Error>> {
enabled_notify: Condvar::new(),
thread_attached: Mutex::new(false),
thread_notify: Condvar::new(),
+ api_enabled: Mutex::new(false),
+ api_notify: Condvar::new(),
});
// This needs to be built before any |topstack::get_runtime()| call!
@@ -148,8 +151,14 @@ fn main() -> Result<(), Box<dyn Error>> {
signal::SaFlags::empty(),
signal::SigSet::empty(),
);
+ let sig_action_usr1 = signal::SigAction::new(
+ signal::SigHandler::Handler(handle_sigusr1),
+ signal::SaFlags::empty(),
+ signal::SigSet::empty(),
+ );
unsafe {
signal::sigaction(signal::SIGTERM, &sig_action_term).unwrap();
+ signal::sigaction(signal::SIGUSR1, &sig_action_usr1).unwrap();
}
// Construct btstack profiles.
@@ -241,11 +250,11 @@ fn main() -> Result<(), Box<dyn Error>> {
tokio::spawn(interface_manager::InterfaceManager::dispatch(
api_rx,
- tx.clone(),
virt_index,
conn,
conn_join_handle,
disconnect_watcher.clone(),
+ sig_notifier.clone(),
bluetooth.clone(),
bluetooth_admin.clone(),
bluetooth_gatt.clone(),
@@ -267,37 +276,63 @@ fn main() -> Result<(), Box<dyn Error>> {
/// Data needed for signal handling.
static SIG_DATA: Mutex<Option<(Sender<Message>, Arc<SigData>)>> = Mutex::new(None);
-extern "C" fn handle_sigterm(_signum: i32) {
- let guard = SIG_DATA.lock().unwrap();
- if let Some((tx, notifier)) = guard.as_ref() {
- log::debug!("Handling SIGTERM by disabling the adapter!");
- let txl = tx.clone();
- topstack::get_runtime().spawn(async move {
- // Send the shutdown message here.
- let _ = txl.send(Message::InterfaceShutdown).await;
- });
+/// Try to cleanup the whole stack. Returns whether to clean up.
+extern "C" fn try_cleanup_stack(abort: bool) -> bool {
+ let lock = SIG_DATA.try_lock();
+
+ // If SIG_DATA is locked, it is likely the cleanup procedure is ongoing. No
+ // need to do anything here.
+ if lock.is_err() {
+ return false;
+ }
+
+ if let Some((tx, notifier)) = lock.unwrap().as_ref() {
+ log::info!("Cleanup stack: disabling the adapter!");
+
+ // Remove the API first to prevent clients calling while shutting down.
+ let guard = notifier.api_enabled.lock().unwrap();
+ if *guard {
+ let txl = tx.clone();
+ topstack::get_runtime().spawn(async move {
+ // Remove the API first to prevent clients calling while shutting down.
+ let _ = txl.send(Message::InterfaceShutdown).await;
+ });
+ log::info!(
+ "Cleanup stack: Waiting for API shutdown to complete for {:?}",
+ API_DISABLE_TIMEOUT_MS
+ );
+ let _ = notifier.api_notify.wait_timeout(guard, API_DISABLE_TIMEOUT_MS);
+ }
let guard = notifier.enabled.lock().unwrap();
if *guard {
- log::debug!("Waiting for stack to turn off for {:?}", STACK_TURN_OFF_TIMEOUT_MS);
+ let txl = tx.clone();
+ topstack::get_runtime().spawn(async move {
+ let _ = txl.send(Message::AdapterShutdown(abort)).await;
+ });
+ log::info!(
+ "Cleanup stack: Waiting for stack to turn off for {:?}",
+ STACK_TURN_OFF_TIMEOUT_MS
+ );
let _ = notifier.enabled_notify.wait_timeout(guard, STACK_TURN_OFF_TIMEOUT_MS);
}
- log::debug!("SIGTERM cleaning up the stack.");
- let txl = tx.clone();
- topstack::get_runtime().spawn(async move {
- // Clean up the profiles first as some of them might require main thread to clean up.
- let _ = txl.send(Message::CleanupProfiles).await;
- // Currently there is no good way to know when the profile is cleaned.
- // Simply add a small delay here.
- tokio::time::sleep(STACK_CLEANUP_PROFILES_TIMEOUT_MS).await;
- // Send the cleanup message to clean up the main thread.
- let _ = txl.send(Message::Cleanup).await;
- });
-
let guard = notifier.thread_attached.lock().unwrap();
if *guard {
- log::debug!("Waiting for stack to clean up for {:?}", STACK_CLEANUP_TIMEOUT_MS);
+ let txl = tx.clone();
+ topstack::get_runtime().spawn(async move {
+ // Clean up the profiles first as some of them might require main thread to clean up.
+ let _ = txl.send(Message::CleanupProfiles).await;
+ // Currently there is no good way to know when the profile is cleaned.
+ // Simply add a small delay here.
+ tokio::time::sleep(STACK_CLEANUP_PROFILES_TIMEOUT_MS).await;
+ // Send the cleanup message to clean up the main thread.
+ let _ = txl.send(Message::Cleanup).await;
+ });
+ log::info!(
+ "Cleanup stack: Waiting for libbluetooth stack to clean up for {:?}",
+ STACK_CLEANUP_TIMEOUT_MS
+ );
let _ = notifier.thread_notify.wait_timeout(guard, STACK_CLEANUP_TIMEOUT_MS);
}
@@ -305,7 +340,26 @@ extern "C" fn handle_sigterm(_signum: i32) {
// finishing btif cleanup.
std::thread::sleep(EXTRA_WAIT_BEFORE_KILL_MS);
}
+ return true;
+}
- log::debug!("Sigterm completed");
+extern "C" fn handle_sigterm(_signum: i32) {
+ log::info!("SIGTERM received");
+ if !try_cleanup_stack(false) {
+ log::info!("Skipped to handle SIGTERM");
+ return;
+ }
+ log::info!("SIGTERM completed");
+ std::process::exit(0);
+}
+
+/// Used to indicate controller needs reset
+extern "C" fn handle_sigusr1(_signum: i32) {
+ log::info!("SIGUSR1 received");
+ if !try_cleanup_stack(true) {
+ log::info!("Skipped to handle SIGUSR1");
+ return;
+ }
+ log::info!("SIGUSR1 completed");
std::process::exit(0);
}
diff --git a/system/gd/rust/linux/stack/src/bluetooth.rs b/system/gd/rust/linux/stack/src/bluetooth.rs
index bac9c4a1a9..ade91229c4 100644
--- a/system/gd/rust/linux/stack/src/bluetooth.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth.rs
@@ -503,6 +503,9 @@ pub struct SigData {
pub thread_attached: Mutex<bool>,
pub thread_notify: Condvar,
+
+ pub api_enabled: Mutex<bool>,
+ pub api_notify: Condvar,
}
/// The interface for adapter callbacks registered through `IBluetooth::register_callback`.
@@ -818,6 +821,21 @@ impl Bluetooth {
self.connection_callbacks.remove_callback(id);
}
+ pub fn shutdown_adapter(&mut self, abort: bool) -> bool {
+ self.disabling = true;
+
+ if !abort {
+ if !self.set_discoverable(BtDiscMode::NonDiscoverable, 0) {
+ warn!("set_discoverable failed on disabling");
+ }
+ if !self.set_connectable_internal(false) {
+ warn!("set_connectable_internal failed on disabling");
+ }
+ }
+
+ self.intf.lock().unwrap().disable() == 0
+ }
+
fn get_remote_device_property(
&self,
device: &BluetoothDevice,
@@ -2240,14 +2258,7 @@ impl IBluetooth for Bluetooth {
}
fn disable(&mut self) -> bool {
- self.disabling = true;
- if !self.set_discoverable(BtDiscMode::NonDiscoverable, 0) {
- warn!("set_discoverable failed on disabling");
- }
- if !self.set_connectable_internal(false) {
- warn!("set_connectable_internal failed on disabling");
- }
- self.intf.lock().unwrap().disable() == 0
+ self.shutdown_adapter(false)
}
fn cleanup(&mut self) {
diff --git a/system/gd/rust/linux/stack/src/lib.rs b/system/gd/rust/linux/stack/src/lib.rs
index d9f97bfa32..4f49a186b4 100644
--- a/system/gd/rust/linux/stack/src/lib.rs
+++ b/system/gd/rust/linux/stack/src/lib.rs
@@ -66,7 +66,9 @@ pub enum Message {
/// Remove the DBus API. Call it before other AdapterShutdown.
InterfaceShutdown,
/// Disable the adapter by calling btif disable.
- AdapterShutdown,
+ /// Param: bool to indicate abort(true) or graceful shutdown(false).
+ /// Use abort when we believe adapter is already in a bad state.
+ AdapterShutdown(bool),
/// Clean up the adapter by calling btif cleanup.
Cleanup,
/// Clean up the media by calling profile cleanup.
@@ -287,9 +289,9 @@ impl Stack {
});
}
- Message::AdapterShutdown => {
+ Message::AdapterShutdown(abort) => {
bluetooth_gatt.lock().unwrap().enable(false);
- bluetooth.lock().unwrap().disable();
+ bluetooth.lock().unwrap().shutdown_adapter(abort);
}
Message::Cleanup => {