diff options
-rw-r--r-- | android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java | 2 | ||||
-rw-r--r-- | android/app/src/com/android/bluetooth/btservice/AdapterService.java | 2338 | ||||
-rw-r--r-- | android/app/src/com/android/bluetooth/btservice/AdapterServiceBinder.java | 2266 | ||||
-rw-r--r-- | android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java | 2 | ||||
-rw-r--r-- | android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java | 12 | ||||
-rw-r--r-- | android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java | 1 | ||||
-rw-r--r-- | apex/Android.bp | 1 | ||||
-rw-r--r-- | system/gd/hal/hci_hal.h | 7 | ||||
-rw-r--r-- | system/gd/hal/hci_hal_host.cc | 36 | ||||
-rw-r--r-- | system/gd/hci/hci_layer.cc | 41 | ||||
-rw-r--r-- | system/gd/hci/hci_layer.h | 2 | ||||
-rw-r--r-- | system/gd/rust/linux/service/src/interface_manager.rs | 48 | ||||
-rw-r--r-- | system/gd/rust/linux/service/src/main.rs | 136 | ||||
-rw-r--r-- | system/gd/rust/linux/stack/src/bluetooth.rs | 27 | ||||
-rw-r--r-- | system/gd/rust/linux/stack/src/lib.rs | 8 |
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 => { |