summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/app/aidl/android/bluetooth/BluetoothClass.aidl2
-rw-r--r--android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl2
-rw-r--r--android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl2
-rw-r--r--android/app/aidl/android/bluetooth/IBluetooth.aidl2
-rw-r--r--android/app/aidl/android/bluetooth/IBluetoothCallback.aidl2
-rw-r--r--android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl2
-rw-r--r--android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl2
-rw-r--r--android/app/src/com/android/bluetooth/BluetoothMethodProxy.java5
-rw-r--r--android/app/src/com/android/bluetooth/a2dp/A2dpService.java335
-rw-r--r--android/app/src/com/android/bluetooth/a2dp/A2dpServiceBinder.java336
-rw-r--r--android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java139
-rw-r--r--android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinder.java157
-rw-r--r--android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java90
-rw-r--r--android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinder.java116
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientService.java239
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientServiceBinder.java268
-rw-r--r--android/app/src/com/android/bluetooth/btservice/MetricsLogger.java93
-rw-r--r--android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java170
-rw-r--r--android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinder.java194
-rw-r--r--android/app/src/com/android/bluetooth/hap/HapClientService.java2
-rw-r--r--android/app/src/com/android/bluetooth/hap/HapClientServiceBinder.java (renamed from android/app/src/com/android/bluetooth/hap/HapClientBinder.java)31
-rw-r--r--android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java217
-rw-r--r--android/app/src/com/android/bluetooth/hearingaid/HearingAidServiceBinder.java230
-rw-r--r--android/app/src/com/android/bluetooth/hfp/HeadsetService.java317
-rw-r--r--android/app/src/com/android/bluetooth/hfp/HeadsetServiceBinder.java319
-rw-r--r--android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java344
-rw-r--r--android/app/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinder.java345
-rw-r--r--android/app/src/com/android/bluetooth/hid/HidDeviceService.java202
-rw-r--r--android/app/src/com/android/bluetooth/hid/HidDeviceServiceBinder.java218
-rw-r--r--android/app/src/com/android/bluetooth/hid/HidHostService.java220
-rw-r--r--android/app/src/com/android/bluetooth/hid/HidHostServiceBinder.java232
-rw-r--r--android/app/src/com/android/bluetooth/le_audio/LeAudioService.java581
-rw-r--r--android/app/src/com/android/bluetooth/le_audio/LeAudioServiceBinder.java548
-rw-r--r--android/app/src/com/android/bluetooth/map/BluetoothMapService.java228
-rw-r--r--android/app/src/com/android/bluetooth/map/BluetoothMapServiceBinder.java251
-rw-r--r--android/app/src/com/android/bluetooth/mapclient/MapClientService.java177
-rw-r--r--android/app/src/com/android/bluetooth/mapclient/MapClientServiceBinder.java193
-rw-r--r--android/app/src/com/android/bluetooth/mcp/McpService.java42
-rw-r--r--android/app/src/com/android/bluetooth/mcp/McpServiceBinder.java63
-rw-r--r--android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java15
-rw-r--r--android/app/src/com/android/bluetooth/opp/BluetoothOppService.java2
-rw-r--r--android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java31
-rw-r--r--android/app/src/com/android/bluetooth/pan/PanService.java139
-rw-r--r--android/app/src/com/android/bluetooth/pan/PanServiceBinder.java161
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java2
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java2
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java2
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java2
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java101
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinder.java121
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java2
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java2
-rw-r--r--android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java2
-rw-r--r--android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java2
-rw-r--r--android/app/src/com/android/bluetooth/pbapclient/PbapClientServiceBinder.java (renamed from android/app/src/com/android/bluetooth/pbapclient/PbapClientBinder.java)6
-rw-r--r--android/app/src/com/android/bluetooth/sap/SapService.java147
-rw-r--r--android/app/src/com/android/bluetooth/sap/SapServiceBinder.java166
-rw-r--r--android/app/src/com/android/bluetooth/tbs/TbsService.java114
-rw-r--r--android/app/src/com/android/bluetooth/tbs/TbsServiceBinder.java126
-rw-r--r--android/app/src/com/android/bluetooth/vc/VolumeControlService.java522
-rw-r--r--android/app/src/com/android/bluetooth/vc/VolumeControlServiceBinder.java528
-rw-r--r--android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java25
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java16
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java12
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java10
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceBinderTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/bass_client/BleBroadcastAssistantBinderTest.java)14
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java60
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinderTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/csip/BluetoothCsisBinderTest.java)19
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java1
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java1
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceBinderTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/hap/HapClientBinderTest.java)7
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceBinderTest.java200
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java4
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceBinderTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/hfp/BluetoothHeadsetBinderTest.java)17
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java5
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceBinderTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/hid/BluetoothHidDeviceBinderTest.java)14
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java)2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java31
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceBinderTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java)14
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java18
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java19
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceBinderTest.java69
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java19
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java17
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java6
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceBinderTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientBinderTest.java)58
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceBinderTest.java132
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/tbs/TbsServiceBinderTest.java146
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceBinderTest.java294
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java4
-rw-r--r--flags/gap.aconfig17
-rw-r--r--flags/hid.aconfig10
-rw-r--r--flags/leaudio.aconfig10
-rw-r--r--flags/opp.aconfig27
-rw-r--r--framework/api/system-current.txt1
-rw-r--r--framework/java/android/bluetooth/BluetoothDevicePicker.java11
-rw-r--r--service/src/com/android/server/bluetooth/BluetoothManagerService.java30
-rw-r--r--service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java29
-rw-r--r--system/bta/gatt/bta_gattc_utils.cc2
-rw-r--r--system/btif/src/btif_dm.cc10
-rw-r--r--system/gd/hal/hci_hal_android_test.cc8
-rw-r--r--system/gd/hal/hci_hal_host_test.cc8
-rw-r--r--system/gd/hci/acl_manager.cc10
-rw-r--r--system/gd/hci/acl_manager.h6
-rw-r--r--system/gd/hci/acl_manager/le_impl.h19
-rw-r--r--system/gd/hci/acl_manager/le_impl_test.cc23
-rw-r--r--system/gd/hci/acl_manager/round_robin_scheduler.cc10
-rw-r--r--system/gd/hci/acl_manager/round_robin_scheduler_test.cc3
-rw-r--r--system/gd/hci/le_address_manager.cc58
-rw-r--r--system/gd/hci/le_advertising_manager.cc171
-rw-r--r--system/gd/module.cc31
-rw-r--r--system/gd/module.h6
-rw-r--r--system/gd/module_unittest.cc6
-rw-r--r--system/gd/os/linux_generic/alarm.cc6
-rw-r--r--system/main/shim/acl.cc11
-rw-r--r--system/main/shim/acl.h2
-rw-r--r--system/main/shim/acl_api.cc6
-rw-r--r--system/main/shim/acl_api.h2
-rw-r--r--system/main/shim/stack.cc3
-rw-r--r--system/stack/btm/btm_ble_sec.cc7
-rw-r--r--system/stack/btm/btm_sec.cc9
-rw-r--r--system/stack/include/l2cap_interface.h15
-rw-r--r--system/stack/l2cap/internal/l2c_api.h14
-rw-r--r--system/stack/l2cap/l2c_api.h2
-rw-r--r--system/stack/l2cap/l2c_ble_conn_params.cc20
-rw-r--r--system/stack/l2cap/l2cap_api.cc7
-rw-r--r--system/test/mock/mock_main_shim_acl.cc6
-rw-r--r--system/test/mock/mock_main_shim_acl_api.cc6
-rw-r--r--system/test/mock/mock_main_shim_acl_api.h15
-rw-r--r--system/test/mock/mock_stack_l2cap_interface.h3
132 files changed, 6207 insertions, 4788 deletions
diff --git a/android/app/aidl/android/bluetooth/BluetoothClass.aidl b/android/app/aidl/android/bluetooth/BluetoothClass.aidl
index 8699b1eee3..124ee53760 100644
--- a/android/app/aidl/android/bluetooth/BluetoothClass.aidl
+++ b/android/app/aidl/android/bluetooth/BluetoothClass.aidl
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl
index 14f91140af..74f51a8037 100644
--- a/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl
+++ b/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl
@@ -5,7 +5,7 @@
** 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
+** 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,
diff --git a/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl
index 87dd10ee15..a55a794c6e 100644
--- a/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl
+++ b/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl
@@ -5,7 +5,7 @@
** 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
+** 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,
diff --git a/android/app/aidl/android/bluetooth/IBluetooth.aidl b/android/app/aidl/android/bluetooth/IBluetooth.aidl
index bc46353f7c..d8a5421d6f 100644
--- a/android/app/aidl/android/bluetooth/IBluetooth.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetooth.aidl
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/aidl/android/bluetooth/IBluetoothCallback.aidl b/android/app/aidl/android/bluetooth/IBluetoothCallback.aidl
index 9b2888eadb..6101044e29 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothCallback.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothCallback.aidl
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl
index ce2397ff33..bd54982d13 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl b/android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl
index 38ffe6f699..c3d542c774 100644
--- a/android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl
+++ b/android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
index d4fc9d54e8..a4c265adf7 100644
--- a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
+++ b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
@@ -306,4 +306,9 @@ public class BluetoothMethodProxy {
ComponentCaller caller, Uri uri, int modeFlags) {
return caller.checkContentUriPermission(uri, modeFlags);
}
+
+ /** Proxies {@link Context#grantUriPermission(String, Uri, int)}. } */
+ public void grantUriPermission(Context context, String packageName, Uri uri, int modeFlags) {
+ context.grantUriPermission(packageName, uri, modeFlags);
+ }
}
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
index 9481146d61..de37a5b3f4 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -26,8 +26,6 @@ import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
-import static com.android.bluetooth.Utils.checkCallerTargetSdk;
-
import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElseGet;
@@ -44,15 +42,12 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.BufferConstraints;
-import android.bluetooth.IBluetoothA2dp;
import android.companion.CompanionDeviceManager;
-import android.content.AttributionSource;
import android.content.Intent;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.BluetoothProfileConnectionInfo;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -63,7 +58,6 @@ import com.android.bluetooth.BluetoothStatsLog;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ActiveDeviceManager;
import com.android.bluetooth.btservice.AdapterService;
-import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
@@ -73,7 +67,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@@ -168,7 +161,7 @@ public class A2dpService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothA2dpBinder(this);
+ return new A2dpServiceBinder(this);
}
@Override
@@ -203,6 +196,10 @@ public class A2dpService extends ProfileService {
mHandler.removeCallbacksAndMessages(null);
}
+ CompanionDeviceManager getCompanionDeviceManager() {
+ return mCompanionDeviceManager;
+ }
+
public static synchronized A2dpService getA2dpService() {
if (sA2dpService == null) {
Log.w(TAG, "getA2dpService(): service is null");
@@ -1001,13 +998,7 @@ public class A2dpService extends ProfileService {
return null;
}
Log.d(TAG, "Creating a new state machine for " + device);
- sm =
- new A2dpStateMachine(
- this,
- device,
- mNativeInterface,
- mA2dpOffloadEnabled,
- mLooper);
+ sm = new A2dpStateMachine(this, device, mNativeInterface, mA2dpOffloadEnabled, mLooper);
mStateMachines.put(device, sm);
return sm;
}
@@ -1345,320 +1336,6 @@ public class A2dpService extends ProfileService {
: null;
}
- /** Binder object: must be a static class or memory leak may occur. */
- @VisibleForTesting
- static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder {
- private A2dpService mService;
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private A2dpService getServiceAndEnforceConnect(AttributionSource source) {
- // Cache mService because it can change while getService is called
- A2dpService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
- return service;
- }
-
- private A2dpService getService() {
- // Cache mService because it can change while getService is called
- A2dpService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) {
- return null;
- }
- return service;
- }
-
- BluetoothA2dpBinder(A2dpService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnect(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- if (device == null) {
- return service.removeActiveDevice(false);
- } else {
- return service.setActiveDevice(device);
- }
- }
-
- @Override
- public BluetoothDevice getActiveDevice(AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return null;
- }
-
- return service.getActiveDevice();
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public void setAvrcpAbsoluteVolume(int volume, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.setAvrcpAbsoluteVolume(volume);
- }
-
- @Override
- public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- return service.isA2dpPlaying(device);
- }
-
- @Override
- public List<BluetoothCodecType> getSupportedCodecTypes() {
- A2dpService service = getService();
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getSupportedCodecTypes();
- }
-
- @Override
- public BluetoothCodecStatus getCodecStatus(
- BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return null;
- }
-
- Utils.enforceCdmAssociationIfNotBluetoothPrivileged(
- service, service.mCompanionDeviceManager, source, device);
-
- return service.getCodecStatus(device);
- }
-
- @Override
- public void setCodecConfigPreference(
- BluetoothDevice device,
- BluetoothCodecConfig codecConfig,
- AttributionSource source) {
- requireNonNull(device);
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- Utils.enforceCdmAssociationIfNotBluetoothPrivileged(
- service, service.mCompanionDeviceManager, source, device);
-
- service.setCodecConfigPreference(device, codecConfig);
- }
-
- @Override
- public void enableOptionalCodecs(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- if (checkCallerTargetSdk(
- mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- service.enableOptionalCodecs(device);
- }
-
- @Override
- public void disableOptionalCodecs(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- if (checkCallerTargetSdk(
- mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- service.disableOptionalCodecs(device);
- }
-
- @Override
- public int isOptionalCodecsSupported(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
- }
-
- if (checkCallerTargetSdk(
- mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- return service.getSupportsOptionalCodecs(device);
- }
-
- @Override
- public int isOptionalCodecsEnabled(BluetoothDevice device, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
- }
-
- if (checkCallerTargetSdk(
- mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- return service.getOptionalCodecsEnabled(device);
- }
-
- @Override
- public void setOptionalCodecsEnabled(
- BluetoothDevice device, int value, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- if (checkCallerTargetSdk(
- mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- service.setOptionalCodecsEnabled(device, value);
- }
-
- @Override
- public int getDynamicBufferSupport(AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE;
- }
-
- return service.getDynamicBufferSupport();
- }
-
- @Override
- public BufferConstraints getBufferConstraints(AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return null;
- }
-
- return service.getBufferConstraints();
- }
-
- @Override
- public boolean setBufferLengthMillis(int codec, int value, AttributionSource source) {
- A2dpService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- return service.setBufferLengthMillis(codec, value);
- }
- }
-
@Override
public void dump(StringBuilder sb) {
super.dump(sb);
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpServiceBinder.java b/android/app/src/com/android/bluetooth/a2dp/A2dpServiceBinder.java
new file mode 100644
index 0000000000..da8aaae42a
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpServiceBinder.java
@@ -0,0 +1,336 @@
+/*
+ * 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.a2dp;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import static com.android.bluetooth.Utils.checkCallerTargetSdk;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothCodecType;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BufferConstraints;
+import android.bluetooth.IBluetoothA2dp;
+import android.content.AttributionSource;
+import android.os.Build;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class A2dpServiceBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder {
+ private static final String TAG = A2dpServiceBinder.class.getSimpleName();
+
+ private A2dpService mService;
+
+ A2dpServiceBinder(A2dpService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ private A2dpService getService() {
+ A2dpService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private A2dpService getServiceAndEnforceConnect(AttributionSource source) {
+ A2dpService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ if (device == null) {
+ return service.removeActiveDevice(false);
+ } else {
+ return service.setActiveDevice(device);
+ }
+ }
+
+ @Override
+ public BluetoothDevice getActiveDevice(AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return null;
+ }
+ return service.getActiveDevice();
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public void setAvrcpAbsoluteVolume(int volume, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setAvrcpAbsoluteVolume(volume);
+ }
+
+ @Override
+ public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+ return service.isA2dpPlaying(device);
+ }
+
+ @Override
+ public List<BluetoothCodecType> getSupportedCodecTypes() {
+ A2dpService service = getService();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getSupportedCodecTypes();
+ }
+
+ @Override
+ public BluetoothCodecStatus getCodecStatus(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return null;
+ }
+
+ Utils.enforceCdmAssociationIfNotBluetoothPrivileged(
+ service, service.getCompanionDeviceManager(), source, device);
+
+ return service.getCodecStatus(device);
+ }
+
+ @Override
+ public void setCodecConfigPreference(
+ BluetoothDevice device, BluetoothCodecConfig codecConfig, AttributionSource source) {
+ requireNonNull(device);
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ Utils.enforceCdmAssociationIfNotBluetoothPrivileged(
+ service, service.getCompanionDeviceManager(), source, device);
+
+ service.setCodecConfigPreference(device, codecConfig);
+ }
+
+ @Override
+ public void enableOptionalCodecs(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ }
+ service.enableOptionalCodecs(device);
+ }
+
+ @Override
+ public void disableOptionalCodecs(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ }
+ service.disableOptionalCodecs(device);
+ }
+
+ @Override
+ public int isOptionalCodecsSupported(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
+ }
+
+ if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ }
+ return service.getSupportsOptionalCodecs(device);
+ }
+
+ @Override
+ public int isOptionalCodecsEnabled(BluetoothDevice device, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
+ }
+
+ if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ }
+ return service.getOptionalCodecsEnabled(device);
+ }
+
+ @Override
+ public void setOptionalCodecsEnabled(
+ BluetoothDevice device, int value, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) {
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ }
+ service.setOptionalCodecsEnabled(device, value);
+ }
+
+ @Override
+ public int getDynamicBufferSupport(AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE;
+ }
+
+ return service.getDynamicBufferSupport();
+ }
+
+ @Override
+ public BufferConstraints getBufferConstraints(AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return null;
+ }
+
+ return service.getBufferConstraints();
+ }
+
+ @Override
+ public boolean setBufferLengthMillis(int codec, int value, AttributionSource source) {
+ A2dpService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ return service.setBufferLengthMillis(codec, value);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index 10b9249bad..6750a9a9c8 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -13,30 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.bluetooth.a2dpsink;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAudioConfig;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
-import android.bluetooth.IBluetoothA2dpSink;
-import android.content.AttributionSource;
import android.os.Looper;
import android.sysprop.BluetoothProperties;
import android.util.Log;
-import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
@@ -45,7 +39,6 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -177,136 +170,6 @@ public class A2dpSinkService extends ProfileService {
return new A2dpSinkServiceBinder(this);
}
- // Binder object: Must be static class or memory leak may occur
- @VisibleForTesting
- static class A2dpSinkServiceBinder extends IBluetoothA2dpSink.Stub
- implements IProfileServiceBinder {
- private A2dpSinkService mService;
-
- A2dpSinkServiceBinder(A2dpSinkService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private A2dpSinkService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- A2dpSinkService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- return service;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.disconnect(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.isA2dpPlaying(device);
- }
-
- @Override
- public BluetoothAudioConfig getAudioConfig(
- BluetoothDevice device, AttributionSource source) {
- A2dpSinkService service = getService(source);
- if (service == null) {
- return null;
- }
- return service.getAudioConfig(device);
- }
- }
-
/* Generic Profile Code */
/**
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinder.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinder.java
new file mode 100644
index 0000000000..851e48a286
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinder.java
@@ -0,0 +1,157 @@
+/*
+ * 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.a2dpsink;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothAudioConfig;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothA2dpSink;
+import android.content.AttributionSource;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class A2dpSinkServiceBinder extends IBluetoothA2dpSink.Stub implements IProfileServiceBinder {
+ private static final String TAG = A2dpSinkServiceBinder.class.getSimpleName();
+
+ private A2dpSinkService mService;
+
+ A2dpSinkServiceBinder(A2dpSinkService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private A2dpSinkService getService(AttributionSource source) {
+ A2dpSinkService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ return service;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isA2dpPlaying(device);
+ }
+
+ @Override
+ public BluetoothAudioConfig getAudioConfig(BluetoothDevice device, AttributionSource source) {
+ A2dpSinkService service = getService(source);
+ if (service == null) {
+ return null;
+ }
+ return service.getAudioConfig(device);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 4551f320da..c4ef6a2437 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -16,19 +16,14 @@
package com.android.bluetooth.avrcpcontroller;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothAvrcpPlayerSettings;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.IBluetoothAvrcpController;
-import android.content.AttributionSource;
import android.content.Intent;
import android.media.AudioManager;
import android.support.v4.media.MediaBrowserCompat.MediaItem;
@@ -46,7 +41,6 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -368,90 +362,6 @@ public class AvrcpControllerService extends ProfileService {
return new AvrcpControllerServiceBinder(this);
}
- // Binder object: Must be static class or memory leak may occur
- @VisibleForTesting
- static class AvrcpControllerServiceBinder extends IBluetoothAvrcpController.Stub
- implements IProfileServiceBinder {
- private AvrcpControllerService mService;
-
- AvrcpControllerServiceBinder(AvrcpControllerService service) {
- mService = service;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private AvrcpControllerService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- AvrcpControllerService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- return service;
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- AvrcpControllerService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- AvrcpControllerService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- AvrcpControllerService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
- return service.getConnectionState(device);
- }
-
- @Override
- public void sendGroupNavigationCmd(
- BluetoothDevice device, int keyCode, int keyState, AttributionSource source) {
- getService(source);
- Log.w(TAG, "sendGroupNavigationCmd not implemented");
- }
-
- @Override
- public void setPlayerApplicationSetting(
- BluetoothAvrcpPlayerSettings settings, AttributionSource source) {
- getService(source);
- Log.w(TAG, "setPlayerApplicationSetting not implemented");
- }
-
- @Override
- public BluetoothAvrcpPlayerSettings getPlayerSettings(
- BluetoothDevice device, AttributionSource source) {
- getService(source);
- Log.w(TAG, "getPlayerSettings not implemented");
- return null;
- }
- }
-
// Called by JNI when a device has connected or disconnected.
@VisibleForTesting
synchronized void onConnectionStateChanged(
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinder.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinder.java
new file mode 100644
index 0000000000..105e9a8709
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinder.java
@@ -0,0 +1,116 @@
+/*
+ * 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.avrcpcontroller;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothAvrcpPlayerSettings;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothAvrcpController;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class AvrcpControllerServiceBinder extends IBluetoothAvrcpController.Stub
+ implements IProfileServiceBinder {
+ private static final String TAG = AvrcpControllerServiceBinder.class.getSimpleName();
+
+ private AvrcpControllerService mService;
+
+ AvrcpControllerServiceBinder(AvrcpControllerService service) {
+ mService = service;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private AvrcpControllerService getService(AttributionSource source) {
+ AvrcpControllerService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ return service;
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ AvrcpControllerService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ AvrcpControllerService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ AvrcpControllerService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public void sendGroupNavigationCmd(
+ BluetoothDevice device, int keyCode, int keyState, AttributionSource source) {
+ getService(source);
+ Log.w(TAG, "sendGroupNavigationCmd not implemented");
+ }
+
+ @Override
+ public void setPlayerApplicationSetting(
+ BluetoothAvrcpPlayerSettings settings, AttributionSource source) {
+ getService(source);
+ Log.w(TAG, "setPlayerApplicationSetting not implemented");
+ }
+
+ @Override
+ public BluetoothAvrcpPlayerSettings getPlayerSettings(
+ BluetoothDevice device, AttributionSource source) {
+ getService(source);
+ Log.w(TAG, "getPlayerSettings not implemented");
+ return null;
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
index edf88f9331..42528676b4 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -16,9 +16,6 @@
package com.android.bluetooth.bass_client;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
-import static android.Manifest.permission.BLUETOOTH_SCAN;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
@@ -36,7 +33,6 @@ import static com.android.bluetooth.flags.Flags.leaudioSortScansToSyncByFails;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -50,7 +46,6 @@ import android.bluetooth.BluetoothLeBroadcastSubgroup;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothLeBroadcastAssistant;
import android.bluetooth.IBluetoothLeBroadcastAssistantCallback;
import android.bluetooth.le.IScannerCallback;
import android.bluetooth.le.PeriodicAdvertisingCallback;
@@ -60,7 +55,6 @@ import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
-import android.content.AttributionSource;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -737,7 +731,7 @@ public class BassClientService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothLeBroadcastAssistantBinder(this);
+ return new BassClientServiceBinder(this);
}
@Override
@@ -4829,235 +4823,4 @@ public class BassClientService extends ProfileService {
sEventLogger.dump(sb);
sb.append("\n");
}
-
- /** Binder object: must be a static class or memory leak may occur */
- @VisibleForTesting
- static class BluetoothLeBroadcastAssistantBinder extends IBluetoothLeBroadcastAssistant.Stub
- implements IProfileServiceBinder {
- BassClientService mService;
-
- BluetoothLeBroadcastAssistantBinder(BassClientService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
- private BassClientService getServiceAndEnforceConnect(AttributionSource source) {
- // Cache mService because it can change while getService is called
- BassClientService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service;
- }
-
- @RequiresPermission(allOf = {BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED})
- private BassClientService getServiceAndEnforceScan(AttributionSource source) {
- // Cache mService because it can change while getService is called
- BassClientService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, TAG, "getServiceAndEnforceScan")) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service;
- }
-
- @Override
- public int getConnectionState(BluetoothDevice sink, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return STATE_DISCONNECTED;
- }
- return service.getConnectionState(sink);
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return Collections.emptyList();
- }
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return Collections.emptyList();
- }
- return service.getConnectedDevices();
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return CONNECTION_POLICY_FORBIDDEN;
- }
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public void registerCallback(
- IBluetoothLeBroadcastAssistantCallback cb, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return;
- }
- service.registerCallback(cb);
- }
-
- @Override
- public void unregisterCallback(
- IBluetoothLeBroadcastAssistantCallback cb, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return;
- }
- service.unregisterCallback(cb);
- }
-
- @Override
- public void startSearchingForSources(List<ScanFilter> filters, AttributionSource source) {
- BassClientService service = getServiceAndEnforceScan(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return;
- }
- service.startSearchingForSources(filters);
- }
-
- @Override
- public void stopSearchingForSources(AttributionSource source) {
- BassClientService service = getServiceAndEnforceScan(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return;
- }
- service.stopSearchingForSources();
- }
-
- @Override
- public boolean isSearchInProgress(AttributionSource source) {
- BassClientService service = getServiceAndEnforceScan(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return false;
- }
- return service.isSearchInProgress();
- }
-
- @Override
- public void addSource(
- BluetoothDevice sink,
- BluetoothLeBroadcastMetadata sourceMetadata,
- boolean isGroupOp,
- AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return;
- }
- service.addSource(sink, sourceMetadata, isGroupOp);
- }
-
- @Override
- public void modifySource(
- BluetoothDevice sink,
- int sourceId,
- BluetoothLeBroadcastMetadata updatedMetadata,
- AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return;
- }
- service.modifySource(sink, sourceId, updatedMetadata);
- }
-
- @Override
- public void removeSource(BluetoothDevice sink, int sourceId, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return;
- }
- service.removeSource(sink, sourceId);
- }
-
- @Override
- public List<BluetoothLeBroadcastReceiveState> getAllSources(
- BluetoothDevice sink, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return Collections.emptyList();
- }
- return service.getAllSources(sink);
- }
-
- @Override
- public int getMaximumSourceCapacity(BluetoothDevice sink, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return 0;
- }
- return service.getMaximumSourceCapacity(sink);
- }
-
- @Override
- public BluetoothLeBroadcastMetadata getSourceMetadata(
- BluetoothDevice sink, int sourceId, AttributionSource source) {
- BassClientService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- Log.e(TAG, "Service is null");
- return null;
- }
- return service.getSourceMetadata(sink, sourceId);
- }
- }
}
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientServiceBinder.java b/android/app/src/com/android/bluetooth/bass_client/BassClientServiceBinder.java
new file mode 100644
index 0000000000..d319e6ed47
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientServiceBinder.java
@@ -0,0 +1,268 @@
+/*
+ * 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.bass_client;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.BLUETOOTH_SCAN;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.IBluetoothLeBroadcastAssistant;
+import android.bluetooth.IBluetoothLeBroadcastAssistantCallback;
+import android.bluetooth.le.ScanFilter;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class BassClientServiceBinder extends IBluetoothLeBroadcastAssistant.Stub
+ implements IProfileServiceBinder {
+ private static final String TAG = BassClientServiceBinder.class.getSimpleName();
+
+ private BassClientService mService;
+
+ BassClientServiceBinder(BassClientService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+ private BassClientService getServiceAndEnforceConnect(AttributionSource source) {
+ BassClientService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service;
+ }
+
+ @RequiresPermission(allOf = {BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED})
+ private BassClientService getServiceAndEnforceScan(AttributionSource source) {
+ BassClientService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, TAG, "getServiceAndEnforceScan")) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service;
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice sink, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(sink);
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return CONNECTION_POLICY_FORBIDDEN;
+ }
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public void registerCallback(
+ IBluetoothLeBroadcastAssistantCallback cb, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return;
+ }
+ service.registerCallback(cb);
+ }
+
+ @Override
+ public void unregisterCallback(
+ IBluetoothLeBroadcastAssistantCallback cb, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return;
+ }
+ service.unregisterCallback(cb);
+ }
+
+ @Override
+ public void startSearchingForSources(List<ScanFilter> filters, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceScan(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return;
+ }
+ service.startSearchingForSources(filters);
+ }
+
+ @Override
+ public void stopSearchingForSources(AttributionSource source) {
+ BassClientService service = getServiceAndEnforceScan(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return;
+ }
+ service.stopSearchingForSources();
+ }
+
+ @Override
+ public boolean isSearchInProgress(AttributionSource source) {
+ BassClientService service = getServiceAndEnforceScan(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return false;
+ }
+ return service.isSearchInProgress();
+ }
+
+ @Override
+ public void addSource(
+ BluetoothDevice sink,
+ BluetoothLeBroadcastMetadata sourceMetadata,
+ boolean isGroupOp,
+ AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return;
+ }
+ service.addSource(sink, sourceMetadata, isGroupOp);
+ }
+
+ @Override
+ public void modifySource(
+ BluetoothDevice sink,
+ int sourceId,
+ BluetoothLeBroadcastMetadata updatedMetadata,
+ AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return;
+ }
+ service.modifySource(sink, sourceId, updatedMetadata);
+ }
+
+ @Override
+ public void removeSource(BluetoothDevice sink, int sourceId, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return;
+ }
+ service.removeSource(sink, sourceId);
+ }
+
+ @Override
+ public List<BluetoothLeBroadcastReceiveState> getAllSources(
+ BluetoothDevice sink, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return Collections.emptyList();
+ }
+ return service.getAllSources(sink);
+ }
+
+ @Override
+ public int getMaximumSourceCapacity(BluetoothDevice sink, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return 0;
+ }
+ return service.getMaximumSourceCapacity(sink);
+ }
+
+ @Override
+ public BluetoothLeBroadcastMetadata getSourceMetadata(
+ BluetoothDevice sink, int sourceId, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return null;
+ }
+ return service.getSourceMetadata(sink, sourceId);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
index 87794605dc..f59e69a42d 100644
--- a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
+++ b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
@@ -37,6 +37,12 @@ import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVEN
import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_VOLUME_CONTROL;
import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__STATE_BONDED;
import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__STATE_NONE;
+import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__ASHA;
+import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__CLASSIC;
+import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__LE_AUDIO;
+import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__DAY;
+import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__MONTH;
+import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__WEEK;
import static com.android.bluetooth.BtRestrictedStatsLog.RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED;
import android.app.AlarmManager;
@@ -45,6 +51,7 @@ import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAvrcpController;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothHearingAid;
@@ -60,11 +67,13 @@ import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.BluetoothSap;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
@@ -87,11 +96,14 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.function.BiPredicate;
/** Class of Bluetooth Metrics */
public class MetricsLogger {
@@ -273,6 +285,7 @@ public class MetricsLogger {
filter.addAction(BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
mAdapterService.registerReceiver(mReceiver, filter);
}
@@ -285,9 +298,18 @@ public class MetricsLogger {
Log.w(TAG, "Received intent with null action");
return;
}
+ BluetoothDevice device =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
switch (action) {
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
logConnectionStateChanges(BluetoothProfile.A2DP, intent);
+ if (state == BluetoothProfile.STATE_CONNECTED
+ && isMedicalDevice(device)) {
+ updateHearingDeviceActiveTime(
+ device,
+ HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__CLASSIC);
+ }
break;
case BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED:
logConnectionStateChanges(BluetoothProfile.A2DP_SINK, intent);
@@ -297,12 +319,23 @@ public class MetricsLogger {
break;
case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
logConnectionStateChanges(BluetoothProfile.HEADSET, intent);
+ if (state == BluetoothProfile.STATE_CONNECTED
+ && isMedicalDevice(device)) {
+ updateHearingDeviceActiveTime(
+ device,
+ HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__CLASSIC);
+ }
break;
case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED:
logConnectionStateChanges(BluetoothProfile.HEADSET_CLIENT, intent);
break;
case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
logConnectionStateChanges(BluetoothProfile.HEARING_AID, intent);
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ updateHearingDeviceActiveTime(
+ device,
+ HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__ASHA);
+ }
break;
case BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED:
logConnectionStateChanges(BluetoothProfile.HID_DEVICE, intent);
@@ -331,6 +364,14 @@ public class MetricsLogger {
case BluetoothSap.ACTION_CONNECTION_STATE_CHANGED:
logConnectionStateChanges(BluetoothProfile.SAP, intent);
break;
+ case BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED:
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ updateHearingDeviceActiveTime(
+ device,
+ HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__LE_AUDIO
+ );
+ }
+ break;
default:
Log.w(TAG, "Received unknown intent " + intent);
break;
@@ -915,4 +956,56 @@ public class MetricsLogger {
syncStatus,
getRemoteDeviceInfoProto(device, false));
}
+
+ void logHearingDeviceActiveEvent(BluetoothDevice device, int type, int timePeriod) {
+ BluetoothStatsLog.write(
+ BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED,
+ type,
+ timePeriod,
+ getRemoteDeviceInfoProto(device, true));
+ }
+
+ void updateHearingDeviceActiveTime(BluetoothDevice device, int deviceTypeProto) {
+ // Time comparison includes a +/- 1 hour tolerance to prevent data loss
+ updateLastActiveTime(
+ device,
+ deviceTypeProto,
+ HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__DAY,
+ "last_active_day",
+ (now, lastActive) -> now.isAfter(lastActive.plusDays(1).minusHours(1)));
+ updateLastActiveTime(
+ device,
+ deviceTypeProto,
+ HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__WEEK,
+ "last_active_week",
+ (now, lastActive) -> now.isAfter(lastActive.plusWeeks(1).minusHours(1)));
+ updateLastActiveTime(
+ device,
+ deviceTypeProto,
+ HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__MONTH,
+ "last_active_month",
+ (now, lastActive) -> now.isAfter(lastActive.plusMonths(1).minusHours(1)));
+ }
+
+ private void updateLastActiveTime(
+ BluetoothDevice device,
+ int deviceTypeProto,
+ int timePeriodProto,
+ String timePeriodSettingsKey,
+ BiPredicate<LocalDateTime, LocalDateTime> timeComparison) {
+ final ContentResolver contentResolver = mAdapterService.getContentResolver();
+ final String lastActive = Settings.Secure.getString(contentResolver, timePeriodSettingsKey);
+ final LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault());
+ if (lastActive == null || timeComparison.test(now, LocalDateTime.parse(lastActive))) {
+ Settings.Secure.putString(contentResolver, timePeriodSettingsKey, now.toString());
+ logHearingDeviceActiveEvent(device, deviceTypeProto, timePeriodProto);
+ }
+ }
+
+ private boolean isMedicalDevice(BluetoothDevice device) {
+ final String deviceName = mAdapterService.getRemoteName(device);
+ final List<String> wordBreakdownList = getWordBreakdownList(deviceName);
+ boolean isMedicalDevice = !getMatchedStringForMedicalDevice(wordBreakdownList).isEmpty();
+ return isMedicalDevice;
+ }
}
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index 2b49f467a8..7a6597f5b0 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -17,7 +17,6 @@
package com.android.bluetooth.csip;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -31,7 +30,6 @@ import static java.util.Objects.requireNonNullElseGet;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -40,7 +38,6 @@ import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothCsipSetCoordinator;
import android.bluetooth.IBluetoothCsipSetCoordinatorCallback;
import android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback;
-import android.content.AttributionSource;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
@@ -152,7 +149,7 @@ public class CsipSetCoordinatorService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothCsisBinder(this);
+ return new CsipSetCoordinatorServiceBinder(this);
}
@Override
@@ -991,171 +988,6 @@ public class CsipSetCoordinatorService extends ProfileService {
BluetoothProfile.CSIP_SET_COORDINATOR, device, fromState, toState);
}
- /** Binder object: must be a static class or memory leak may occur */
- @VisibleForTesting
- static class BluetoothCsisBinder extends IBluetoothCsipSetCoordinator.Stub
- implements IProfileServiceBinder {
- private CsipSetCoordinatorService mService;
-
- BluetoothCsisBinder(CsipSetCoordinatorService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
- private CsipSetCoordinatorService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- CsipSetCoordinatorService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service;
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- requireNonNull(source);
-
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- requireNonNull(source);
-
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public ParcelUuid lockGroup(
- int groupId,
- @NonNull IBluetoothCsipSetCoordinatorLockCallback callback,
- AttributionSource source) {
- requireNonNull(callback);
- requireNonNull(source);
-
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return null;
- }
-
- UUID lockUuid = service.lockGroup(groupId, callback);
- return lockUuid == null ? null : new ParcelUuid(lockUuid);
- }
-
- @Override
- public void unlockGroup(@NonNull ParcelUuid lockUuid, AttributionSource source) {
- requireNonNull(lockUuid);
- requireNonNull(source);
-
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.unlockGroup(lockUuid.getUuid());
- }
-
- @Override
- public List<Integer> getAllGroupIds(ParcelUuid uuid, AttributionSource source) {
- requireNonNull(uuid);
- requireNonNull(source);
-
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getAllGroupIds(uuid);
- }
-
- @Override
- public Map<Integer, ParcelUuid> getGroupUuidMapByDevice(
- BluetoothDevice device, AttributionSource source) {
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return null;
- }
-
- return service.getGroupUuidMapByDevice(device);
- }
-
- @Override
- public int getDesiredGroupSize(int groupId, AttributionSource source) {
- CsipSetCoordinatorService service = getService(source);
- if (service == null) {
- return IBluetoothCsipSetCoordinator.CSIS_GROUP_SIZE_UNKNOWN;
- }
-
- return service.getDesiredGroupSize(groupId);
- }
- }
-
@Override
public void dump(StringBuilder sb) {
super.dump(sb);
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinder.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinder.java
new file mode 100644
index 0000000000..5b08c1983b
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinder.java
@@ -0,0 +1,194 @@
+/*
+ * 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.csip;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothCsipSetCoordinator;
+import android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback;
+import android.content.AttributionSource;
+import android.os.ParcelUuid;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+class CsipSetCoordinatorServiceBinder extends IBluetoothCsipSetCoordinator.Stub
+ implements IProfileServiceBinder {
+ private static final String TAG = CsipSetCoordinatorServiceBinder.class.getSimpleName();
+
+ private CsipSetCoordinatorService mService;
+
+ CsipSetCoordinatorServiceBinder(CsipSetCoordinatorService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+ private CsipSetCoordinatorService getService(AttributionSource source) {
+ CsipSetCoordinatorService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service;
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ requireNonNull(source);
+
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ requireNonNull(source);
+
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public ParcelUuid lockGroup(
+ int groupId,
+ @NonNull IBluetoothCsipSetCoordinatorLockCallback callback,
+ AttributionSource source) {
+ requireNonNull(callback);
+ requireNonNull(source);
+
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return null;
+ }
+
+ UUID lockUuid = service.lockGroup(groupId, callback);
+ return lockUuid == null ? null : new ParcelUuid(lockUuid);
+ }
+
+ @Override
+ public void unlockGroup(@NonNull ParcelUuid lockUuid, AttributionSource source) {
+ requireNonNull(lockUuid);
+ requireNonNull(source);
+
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.unlockGroup(lockUuid.getUuid());
+ }
+
+ @Override
+ public List<Integer> getAllGroupIds(ParcelUuid uuid, AttributionSource source) {
+ requireNonNull(uuid);
+ requireNonNull(source);
+
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getAllGroupIds(uuid);
+ }
+
+ @Override
+ public Map<Integer, ParcelUuid> getGroupUuidMapByDevice(
+ BluetoothDevice device, AttributionSource source) {
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return null;
+ }
+ return service.getGroupUuidMapByDevice(device);
+ }
+
+ @Override
+ public int getDesiredGroupSize(int groupId, AttributionSource source) {
+ CsipSetCoordinatorService service = getService(source);
+ if (service == null) {
+ return IBluetoothCsipSetCoordinator.CSIS_GROUP_SIZE_UNKNOWN;
+ }
+ return service.getDesiredGroupSize(groupId);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index 731f3da90c..9b8092763c 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -159,7 +159,7 @@ public class HapClientService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new HapClientBinder(this);
+ return new HapClientServiceBinder(this);
}
@Override
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientBinder.java b/android/app/src/com/android/bluetooth/hap/HapClientServiceBinder.java
index eba1d9db4a..e036e43bb2 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientBinder.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientServiceBinder.java
@@ -36,21 +36,19 @@ import android.content.AttributionSource;
import android.util.Log;
import com.android.bluetooth.Utils;
-import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Collections;
import java.util.List;
-/** HapClientBinder class */
@VisibleForTesting
-class HapClientBinder extends IBluetoothHapClient.Stub
- implements ProfileService.IProfileServiceBinder {
- private static final String TAG = HapClientBinder.class.getSimpleName();
+class HapClientServiceBinder extends IBluetoothHapClient.Stub implements IProfileServiceBinder {
+ private static final String TAG = HapClientServiceBinder.class.getSimpleName();
private HapClientService mService;
- HapClientBinder(HapClientService svc) {
+ HapClientServiceBinder(HapClientService svc) {
mService = svc;
}
@@ -62,7 +60,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
private HapClientService getService(AttributionSource source) {
requireNonNull(source);
- // Cache mService because it can change while getService is called
HapClientService service = mService;
if (Utils.isInstrumentationTestMode()) {
@@ -86,7 +83,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
if (service == null) {
return Collections.emptyList();
}
-
return service.getConnectedDevices();
}
@@ -97,7 +93,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
if (service == null) {
return Collections.emptyList();
}
-
return service.getDevicesMatchingConnectionStates(states);
}
@@ -109,7 +104,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
return service.getConnectionState(device);
}
@@ -139,7 +133,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
return service.getConnectionPolicy(device);
}
@@ -151,7 +144,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
return service.getActivePresetIndex(device);
}
@@ -164,7 +156,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
return service.getActivePresetInfo(device);
}
@@ -176,7 +167,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
return service.getHapGroup(device);
}
@@ -188,7 +178,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
service.selectPreset(device, presetIndex);
}
@@ -198,7 +187,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
if (service == null) {
return;
}
-
service.selectPresetForGroup(groupId, presetIndex);
}
@@ -210,7 +198,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
service.switchToNextPreset(device);
}
@@ -220,7 +207,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
if (service == null) {
return;
}
-
service.switchToNextPresetForGroup(groupId);
}
@@ -232,7 +218,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
service.switchToPreviousPreset(device);
}
@@ -242,7 +227,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
if (service == null) {
return;
}
-
service.switchToPreviousPresetForGroup(groupId);
}
@@ -255,7 +239,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
return service.getPresetInfo(device, presetIndex);
}
@@ -268,7 +251,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
return service.getAllPresetInfo(device);
}
@@ -280,7 +262,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(device);
-
return service.getFeatures(device);
}
@@ -294,7 +275,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
requireNonNull(device);
requireNonNull(name);
-
service.setPresetName(device, presetIndex, name);
}
@@ -307,7 +287,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(name);
-
service.setPresetNameForGroup(groupId, presetIndex, name);
}
@@ -319,7 +298,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(callback);
-
service.registerCallback(callback);
}
@@ -331,7 +309,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub
}
requireNonNull(callback);
-
service.unregisterCallback(callback);
}
}
diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
index a884944bac..0bf2650bb1 100644
--- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
+++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
@@ -17,7 +17,6 @@
package com.android.bluetooth.hearingaid;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
@@ -27,14 +26,11 @@ import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothHearingAid.AdvertisementServiceData;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothHearingAid;
-import android.content.AttributionSource;
import android.content.Intent;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
@@ -57,7 +53,6 @@ import com.android.bluetooth.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -130,7 +125,7 @@ public class HearingAidService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothHearingAidBinder(this);
+ return new HearingAidServiceBinder(this);
}
@Override
@@ -490,7 +485,7 @@ public class HearingAidService extends ProfileService {
return mDeviceCapabilitiesMap.getOrDefault(device, -1);
}
- private AdvertisementServiceData getAdvertisementServiceData(BluetoothDevice device) {
+ AdvertisementServiceData getAdvertisementServiceData(BluetoothDevice device) {
int capability = mAdapterService.getAshaCapability(device);
int id = mAdapterService.getAshaTruncatedHiSyncId(device);
if (capability < 0) {
@@ -855,214 +850,6 @@ public class HearingAidService extends ProfileService {
device, BluetoothProfile.HEARING_AID, toState, fromState);
}
- /** Binder object: must be a static class or memory leak may occur */
- @VisibleForTesting
- static class BluetoothHearingAidBinder extends IBluetoothHearingAid.Stub
- implements IProfileServiceBinder {
- private HearingAidService mService;
-
- BluetoothHearingAidBinder(HearingAidService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private HearingAidService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- HearingAidService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
- return service;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.disconnect(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return false;
- }
-
- if (device == null) {
- return service.removeActiveDevice(false);
- } else {
- return service.setActiveDevice(device);
- }
- }
-
- @Override
- public List<BluetoothDevice> getActiveDevices(AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getActiveDevices();
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public void setVolume(int volume, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.setVolume(volume);
- }
-
- @Override
- public long getHiSyncId(BluetoothDevice device, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return BluetoothHearingAid.HI_SYNC_ID_INVALID;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getHiSyncId(device);
- }
-
- @Override
- public int getDeviceSide(BluetoothDevice device, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return BluetoothHearingAid.SIDE_UNKNOWN;
- }
-
- int side = service.getCapabilities(device);
- if (side != BluetoothHearingAid.SIDE_UNKNOWN) {
- side &= 1;
- }
-
- return side;
- }
-
- @Override
- public int getDeviceMode(BluetoothDevice device, AttributionSource source) {
- HearingAidService service = getService(source);
- if (service == null) {
- return BluetoothHearingAid.MODE_UNKNOWN;
- }
-
- int mode = service.getCapabilities(device);
- if (mode != BluetoothHearingAid.MODE_UNKNOWN) {
- mode = mode >> 1 & 1;
- }
-
- return mode;
- }
-
- @Override
- public AdvertisementServiceData getAdvertisementServiceData(
- BluetoothDevice device, AttributionSource source) {
- HearingAidService service = mService;
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkScanPermissionForDataDelivery(
- service, source, TAG, "getAdvertisementServiceData")) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getAdvertisementServiceData(device);
- }
- }
-
@Override
public void dump(StringBuilder sb) {
super.dump(sb);
diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidServiceBinder.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidServiceBinder.java
new file mode 100644
index 0000000000..de384fba5c
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidServiceBinder.java
@@ -0,0 +1,230 @@
+/*
+ * 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.hearingaid;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothHearingAid.AdvertisementServiceData;
+import android.bluetooth.IBluetoothHearingAid;
+import android.content.AttributionSource;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class HearingAidServiceBinder extends IBluetoothHearingAid.Stub implements IProfileServiceBinder {
+ private static final String TAG = HearingAidServiceBinder.class.getSimpleName();
+
+ private HearingAidService mService;
+
+ HearingAidServiceBinder(HearingAidService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private HearingAidService getService(AttributionSource source) {
+ HearingAidService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ if (device == null) {
+ return service.removeActiveDevice(false);
+ } else {
+ return service.setActiveDevice(device);
+ }
+ }
+
+ @Override
+ public List<BluetoothDevice> getActiveDevices(AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getActiveDevices();
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public void setVolume(int volume, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setVolume(volume);
+ }
+
+ @Override
+ public long getHiSyncId(BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getHiSyncId(device);
+ }
+
+ @Override
+ public int getDeviceSide(BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return BluetoothHearingAid.SIDE_UNKNOWN;
+ }
+
+ int side = service.getCapabilities(device);
+ if (side != BluetoothHearingAid.SIDE_UNKNOWN) {
+ side &= 1;
+ }
+
+ return side;
+ }
+
+ @Override
+ public int getDeviceMode(BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = getService(source);
+ if (service == null) {
+ return BluetoothHearingAid.MODE_UNKNOWN;
+ }
+
+ int mode = service.getCapabilities(device);
+ if (mode != BluetoothHearingAid.MODE_UNKNOWN) {
+ mode = mode >> 1 & 1;
+ }
+
+ return mode;
+ }
+
+ @Override
+ public AdvertisementServiceData getAdvertisementServiceData(
+ BluetoothDevice device, AttributionSource source) {
+ HearingAidService service = mService;
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkScanPermissionForDataDelivery(
+ service, source, TAG, "getAdvertisementServiceData")) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getAdvertisementServiceData(device);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
index 251f347ea2..c5ab1f8dde 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -17,8 +17,6 @@
package com.android.bluetooth.hfp;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
-import static android.Manifest.permission.MODIFY_PHONE_STATE;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
@@ -33,15 +31,12 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSinkAudioPolicy;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothHeadset;
-import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -80,7 +75,6 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -237,7 +231,7 @@ public class HeadsetService extends ProfileService {
@Override
public IProfileServiceBinder initBinder() {
- return new BluetoothHeadsetBinder(this);
+ return new HeadsetServiceBinder(this);
}
@Override
@@ -516,303 +510,6 @@ public class HeadsetService extends ProfileService {
}
}
- /** Handlers for incoming service calls */
- @VisibleForTesting
- static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub
- implements IProfileServiceBinder {
- private HeadsetService mService;
-
- BluetoothHeadsetBinder(HeadsetService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private HeadsetService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- HeadsetService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
- return service;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnect(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public boolean isNoiseReductionSupported(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.isNoiseReductionSupported(device);
- }
-
- @Override
- public boolean isVoiceRecognitionSupported(
- BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.isVoiceRecognitionSupported(device);
- }
-
- @Override
- public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- requireNonNull(device);
-
- return service.startVoiceRecognition(device);
- }
-
- @Override
- public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.stopVoiceRecognition(device);
- }
-
- @Override
- public boolean isAudioConnected(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.isAudioConnected(device);
- }
-
- @Override
- public int getAudioState(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getAudioState(device);
- }
-
- @Override
- public int connectAudio(AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.connectAudio();
- }
-
- @Override
- public int disconnectAudio(AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.disconnectAudio();
- }
-
- @Override
- public void setAudioRouteAllowed(boolean allowed, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setAudioRouteAllowed(allowed);
- }
-
- @Override
- public boolean getAudioRouteAllowed(AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getAudioRouteAllowed();
- }
-
- @Override
- public void setForceScoAudio(boolean forced, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.setForceScoAudio(forced);
- }
-
- @Override
- public boolean startScoUsingVirtualVoiceCall(AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.startScoUsingVirtualVoiceCall();
- }
-
- @Override
- public boolean stopScoUsingVirtualVoiceCall(AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.stopScoUsingVirtualVoiceCall();
- }
-
- @Override
- public boolean sendVendorSpecificResultCode(
- BluetoothDevice device, String command, String arg, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.sendVendorSpecificResultCode(device, command, arg);
- }
-
- @Override
- public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
-
- return service.setActiveDevice(device);
- }
-
- @Override
- public BluetoothDevice getActiveDevice(AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return null;
- }
-
- return service.getActiveDevice();
- }
-
- @Override
- public boolean isInbandRingingEnabled(AttributionSource source) {
- HeadsetService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.isInbandRingingEnabled();
- }
- }
-
// API methods
public static synchronized HeadsetService getHeadsetService() {
if (sHeadsetService == null) {
@@ -947,7 +644,6 @@ public class HeadsetService extends ProfileService {
* @param states an array of states from {@link BluetoothProfile}
* @return a list of devices matching the array of connection states
*/
- @VisibleForTesting
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
ArrayList<BluetoothDevice> devices = new ArrayList<>();
synchronized (mStateMachines) {
@@ -1659,7 +1355,6 @@ public class HeadsetService extends ProfileService {
}
}
- @VisibleForTesting
boolean startScoUsingVirtualVoiceCall() {
Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
synchronized (mStateMachines) {
@@ -2134,8 +1829,7 @@ public class HeadsetService extends ProfileService {
}
}
- private boolean sendVendorSpecificResultCode(
- BluetoothDevice device, String command, String arg) {
+ boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg) {
synchronized (mStateMachines) {
final HeadsetStateMachine stateMachine = mStateMachines.get(device);
if (stateMachine == null) {
@@ -2396,9 +2090,10 @@ public class HeadsetService extends ProfileService {
// Do it here because some controllers cannot handle SCO and CIS
// co-existence see {@link LeAudioService#setInactiveForHfpHandover}
LeAudioService leAudioService = mFactory.getLeAudioService();
- boolean isLeAudioConnectedDeviceNotActive = leAudioService != null
- && !leAudioService.getConnectedDevices().isEmpty()
- && leAudioService.getActiveDevices().get(0) == null;
+ boolean isLeAudioConnectedDeviceNotActive =
+ leAudioService != null
+ && !leAudioService.getConnectedDevices().isEmpty()
+ && leAudioService.getActiveDevices().get(0) == null;
// usually controller limitation cause CONNECTING -> DISCONNECTED, so only
// resume LE audio active device if it is HFP audio only and SCO disconnected
if (fromState != BluetoothHeadset.STATE_AUDIO_CONNECTING
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetServiceBinder.java b/android/app/src/com/android/bluetooth/hfp/HeadsetServiceBinder.java
new file mode 100644
index 0000000000..d93ad5dc5f
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetServiceBinder.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2022 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.hfp;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothStatusCodes;
+import android.bluetooth.IBluetoothHeadset;
+import android.content.AttributionSource;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class HeadsetServiceBinder extends IBluetoothHeadset.Stub implements IProfileServiceBinder {
+ private static final String TAG = HeadsetServiceBinder.class.getSimpleName();
+
+ private HeadsetService mService;
+
+ HeadsetServiceBinder(HeadsetService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private HeadsetService getService(AttributionSource source) {
+ HeadsetService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public boolean isNoiseReductionSupported(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.isNoiseReductionSupported(device);
+ }
+
+ @Override
+ public boolean isVoiceRecognitionSupported(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.isVoiceRecognitionSupported(device);
+ }
+
+ @Override
+ public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ requireNonNull(device);
+ return service.startVoiceRecognition(device);
+ }
+
+ @Override
+ public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.stopVoiceRecognition(device);
+ }
+
+ @Override
+ public boolean isAudioConnected(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.isAudioConnected(device);
+ }
+
+ @Override
+ public int getAudioState(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getAudioState(device);
+ }
+
+ @Override
+ public int connectAudio(AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.connectAudio();
+ }
+
+ @Override
+ public int disconnectAudio(AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.disconnectAudio();
+ }
+
+ @Override
+ public void setAudioRouteAllowed(boolean allowed, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setAudioRouteAllowed(allowed);
+ }
+
+ @Override
+ public boolean getAudioRouteAllowed(AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getAudioRouteAllowed();
+ }
+
+ @Override
+ public void setForceScoAudio(boolean forced, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.setForceScoAudio(forced);
+ }
+
+ @Override
+ public boolean startScoUsingVirtualVoiceCall(AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.startScoUsingVirtualVoiceCall();
+ }
+
+ @Override
+ public boolean stopScoUsingVirtualVoiceCall(AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ return service.stopScoUsingVirtualVoiceCall();
+ }
+
+ @Override
+ public boolean sendVendorSpecificResultCode(
+ BluetoothDevice device, String command, String arg, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.sendVendorSpecificResultCode(device, command, arg);
+ }
+
+ @Override
+ public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
+ return service.setActiveDevice(device);
+ }
+
+ @Override
+ public BluetoothDevice getActiveDevice(AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return null;
+ }
+ return service.getActiveDevice();
+ }
+
+ @Override
+ public boolean isInbandRingingEnabled(AttributionSource source) {
+ HeadsetService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isInbandRingingEnabled();
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index 9c0e416168..7d3a5ff43f 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -16,11 +16,8 @@
package com.android.bluetooth.hfpclient;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
@@ -28,15 +25,11 @@ import static android.content.pm.PackageManager.FEATURE_WATCH;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothHeadsetClientCall;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSinkAudioPolicy;
import android.bluetooth.BluetoothStatusCodes;
-import android.bluetooth.IBluetoothHeadsetClient;
-import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -58,7 +51,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -145,7 +137,7 @@ public class HeadsetClientService extends ProfileService {
@Override
public IProfileServiceBinder initBinder() {
- return new BluetoothHeadsetClientBinder(this);
+ return new HeadsetClientServiceBinder(this);
}
@Override
@@ -290,340 +282,6 @@ public class HeadsetClientService extends ProfileService {
call.isInBandRing());
}
- /** Handlers for incoming service calls */
- @VisibleForTesting
- static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
- implements IProfileServiceBinder {
- private HeadsetClientService mService;
-
- BluetoothHeadsetClientBinder(HeadsetClientService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private HeadsetClientService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- HeadsetClientService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- return service;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnect(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.startVoiceRecognition(device);
- }
-
- @Override
- public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.stopVoiceRecognition(device);
- }
-
- @Override
- public int getAudioState(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
- }
-
- return service.getAudioState(device);
- }
-
- @Override
- public void setAudioRouteAllowed(
- BluetoothDevice device, boolean allowed, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- Log.w(TAG, "Service handle is null for setAudioRouteAllowed!");
- return;
- }
-
- service.setAudioRouteAllowed(device, allowed);
- }
-
- @Override
- public boolean getAudioRouteAllowed(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- Log.w(TAG, "Service handle is null for getAudioRouteAllowed!");
- return false;
- }
-
- return service.getAudioRouteAllowed(device);
- }
-
- @Override
- public boolean connectAudio(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.connectAudio(device);
- }
-
- @Override
- public boolean disconnectAudio(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnectAudio(device);
- }
-
- @Override
- public boolean acceptCall(BluetoothDevice device, int flag, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.acceptCall(device, flag);
- }
-
- @Override
- public boolean rejectCall(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.rejectCall(device);
- }
-
- @Override
- public boolean holdCall(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.holdCall(device);
- }
-
- @Override
- public boolean terminateCall(
- BluetoothDevice device, BluetoothHeadsetClientCall call, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- Log.w(TAG, "service is null");
- return false;
- }
-
- return service.terminateCall(device, call != null ? call.getUUID() : null);
- }
-
- @Override
- public boolean explicitCallTransfer(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.explicitCallTransfer(device);
- }
-
- @Override
- public boolean enterPrivateMode(
- BluetoothDevice device, int index, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.enterPrivateMode(device, index);
- }
-
- @Override
- public BluetoothHeadsetClientCall dial(
- BluetoothDevice device, String number, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return null;
- }
-
- return toLegacyCall(service.dial(device, number));
- }
-
- @Override
- public List<BluetoothHeadsetClientCall> getCurrentCalls(
- BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- List<BluetoothHeadsetClientCall> currentCalls = new ArrayList<>();
- if (service == null) {
- return currentCalls;
- }
-
- List<HfpClientCall> calls = service.getCurrentCalls(device);
- if (calls != null) {
- for (HfpClientCall call : calls) {
- currentCalls.add(toLegacyCall(call));
- }
- }
- return currentCalls;
- }
-
- @Override
- public boolean sendDTMF(BluetoothDevice device, byte code, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.sendDTMF(device, code);
- }
-
- @Override
- public boolean getLastVoiceTagNumber(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.getLastVoiceTagNumber(device);
- }
-
- @Override
- public Bundle getCurrentAgEvents(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return null;
- }
-
- service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getCurrentAgEvents(device);
- }
-
- @Override
- public boolean sendVendorAtCommand(
- BluetoothDevice device, int vendorId, String atCommand, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.sendVendorAtCommand(device, vendorId, atCommand);
- }
-
- @Override
- public Bundle getCurrentAgFeatures(BluetoothDevice device, AttributionSource source) {
- HeadsetClientService service = getService(source);
- if (service == null) {
- return null;
- }
-
- return service.getCurrentAgFeaturesBundle(device);
- }
- }
-
// API methods
public static synchronized HeadsetClientService getHeadsetClientService() {
if (sHeadsetClientService == null) {
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinder.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinder.java
new file mode 100644
index 0000000000..3232fccd4f
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinder.java
@@ -0,0 +1,345 @@
+/*
+ * 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.hfpclient;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothHeadsetClientCall;
+import android.bluetooth.IBluetoothHeadsetClient;
+import android.content.AttributionSource;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Handlers for incoming service calls */
+class HeadsetClientServiceBinder extends IBluetoothHeadsetClient.Stub
+ implements IProfileServiceBinder {
+ private static final String TAG = HeadsetClientServiceBinder.class.getSimpleName();
+
+ private HeadsetClientService mService;
+
+ HeadsetClientServiceBinder(HeadsetClientService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private HeadsetClientService getService(AttributionSource source) {
+ HeadsetClientService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ return service;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+
+ service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+
+ service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.startVoiceRecognition(device);
+ }
+
+ @Override
+ public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.stopVoiceRecognition(device);
+ }
+
+ @Override
+ public int getAudioState(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
+ }
+ return service.getAudioState(device);
+ }
+
+ @Override
+ public void setAudioRouteAllowed(
+ BluetoothDevice device, boolean allowed, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ Log.w(TAG, "Service handle is null for setAudioRouteAllowed!");
+ return;
+ }
+ service.setAudioRouteAllowed(device, allowed);
+ }
+
+ @Override
+ public boolean getAudioRouteAllowed(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ Log.w(TAG, "Service handle is null for getAudioRouteAllowed!");
+ return false;
+ }
+ return service.getAudioRouteAllowed(device);
+ }
+
+ @Override
+ public boolean connectAudio(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.connectAudio(device);
+ }
+
+ @Override
+ public boolean disconnectAudio(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnectAudio(device);
+ }
+
+ @Override
+ public boolean acceptCall(BluetoothDevice device, int flag, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.acceptCall(device, flag);
+ }
+
+ @Override
+ public boolean rejectCall(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.rejectCall(device);
+ }
+
+ @Override
+ public boolean holdCall(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.holdCall(device);
+ }
+
+ @Override
+ public boolean terminateCall(
+ BluetoothDevice device, BluetoothHeadsetClientCall call, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ Log.w(TAG, "service is null");
+ return false;
+ }
+ return service.terminateCall(device, call != null ? call.getUUID() : null);
+ }
+
+ @Override
+ public boolean explicitCallTransfer(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.explicitCallTransfer(device);
+ }
+
+ @Override
+ public boolean enterPrivateMode(BluetoothDevice device, int index, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.enterPrivateMode(device, index);
+ }
+
+ @Override
+ public BluetoothHeadsetClientCall dial(
+ BluetoothDevice device, String number, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return null;
+ }
+ return HeadsetClientService.toLegacyCall(service.dial(device, number));
+ }
+
+ @Override
+ public List<BluetoothHeadsetClientCall> getCurrentCalls(
+ BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ List<BluetoothHeadsetClientCall> currentCalls = new ArrayList<>();
+ if (service == null) {
+ return currentCalls;
+ }
+
+ List<HfpClientCall> calls = service.getCurrentCalls(device);
+ if (calls != null) {
+ for (HfpClientCall call : calls) {
+ currentCalls.add(HeadsetClientService.toLegacyCall(call));
+ }
+ }
+ return currentCalls;
+ }
+
+ @Override
+ public boolean sendDTMF(BluetoothDevice device, byte code, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.sendDTMF(device, code);
+ }
+
+ @Override
+ public boolean getLastVoiceTagNumber(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.getLastVoiceTagNumber(device);
+ }
+
+ @Override
+ public Bundle getCurrentAgEvents(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return null;
+ }
+
+ service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getCurrentAgEvents(device);
+ }
+
+ @Override
+ public boolean sendVendorAtCommand(
+ BluetoothDevice device, int vendorId, String atCommand, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.sendVendorAtCommand(device, vendorId, atCommand);
+ }
+
+ @Override
+ public Bundle getCurrentAgFeatures(BluetoothDevice device, AttributionSource source) {
+ HeadsetClientService service = getService(source);
+ if (service == null) {
+ return null;
+ }
+ return service.getCurrentAgFeaturesBundle(device);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
index 2c2f11940e..d3d84d9630 100644
--- a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
@@ -34,9 +34,7 @@ import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothHidDeviceAppQosSettings;
import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
import android.bluetooth.BluetoothProfile;
-import android.bluetooth.IBluetoothHidDevice;
import android.bluetooth.IBluetoothHidDeviceCallback;
-import android.content.AttributionSource;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
@@ -50,15 +48,12 @@ import android.util.Log;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
-import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.internal.annotations.VisibleForTesting;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
@@ -328,204 +323,9 @@ public class HidDeviceService extends ProfileService {
}
};
- @VisibleForTesting
- static class BluetoothHidDeviceBinder extends IBluetoothHidDevice.Stub
- implements IProfileServiceBinder {
-
- private static final String TAG = BluetoothHidDeviceBinder.class.getSimpleName();
-
- private HidDeviceService mService;
-
- BluetoothHidDeviceBinder(HidDeviceService service) {
- mService = service;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private HidDeviceService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- HidDeviceService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
- return service;
- }
-
- @Override
- public boolean registerApp(
- BluetoothHidDeviceAppSdpSettings sdp,
- BluetoothHidDeviceAppQosSettings inQos,
- BluetoothHidDeviceAppQosSettings outQos,
- IBluetoothHidDeviceCallback callback,
- AttributionSource source) {
- Log.d(TAG, "registerApp()");
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.registerApp(sdp, inQos, outQos, callback);
- }
-
- @Override
- public boolean unregisterApp(AttributionSource source) {
- Log.d(TAG, "unregisterApp()");
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.unregisterApp();
- }
-
- @Override
- public boolean sendReport(
- BluetoothDevice device, int id, byte[] data, AttributionSource source) {
- Log.d(TAG, "sendReport(): device=" + device + " id=" + id);
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.sendReport(device, id, data);
- }
-
- @Override
- public boolean replyReport(
- BluetoothDevice device, byte type, byte id, byte[] data, AttributionSource source) {
- Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.replyReport(device, type, id, data);
- }
-
- @Override
- public boolean unplug(BluetoothDevice device, AttributionSource source) {
- Log.d(TAG, "unplug(): device=" + device);
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.unplug(device);
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- Log.d(TAG, "connect(): device=" + device);
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- Log.d(TAG, "disconnect(): device=" + device);
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnect(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- Log.d(
- TAG,
- "setConnectionPolicy():"
- + (" device=" + device)
- + (" connectionPolicy=" + connectionPolicy));
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public boolean reportError(BluetoothDevice device, byte error, AttributionSource source) {
- Log.d(TAG, "reportError(): device=" + device + " error=" + error);
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.reportError(device, error);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- Log.d(TAG, "getConnectionState(): device=" + device);
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return BluetoothHidDevice.STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- Log.d(TAG, "getConnectedDevices()");
-
- return getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}, source);
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- Log.d(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
-
- HidDeviceService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public String getUserAppName(AttributionSource source) {
- HidDeviceService service = getService(source);
- if (service == null) {
- return "";
- }
- return service.getUserAppName();
- }
- }
-
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothHidDeviceBinder(this);
+ return new HidDeviceServiceBinder(this);
}
private boolean checkDevice(BluetoothDevice device) {
diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceServiceBinder.java b/android/app/src/com/android/bluetooth/hid/HidDeviceServiceBinder.java
new file mode 100644
index 0000000000..0cd0e1d301
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/hid/HidDeviceServiceBinder.java
@@ -0,0 +1,218 @@
+/*
+ * 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.hid;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDevice;
+import android.bluetooth.BluetoothHidDeviceAppQosSettings;
+import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
+import android.bluetooth.IBluetoothHidDevice;
+import android.bluetooth.IBluetoothHidDeviceCallback;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+class HidDeviceServiceBinder extends IBluetoothHidDevice.Stub implements IProfileServiceBinder {
+ private static final String TAG = HidDeviceServiceBinder.class.getSimpleName();
+
+ private HidDeviceService mService;
+
+ HidDeviceServiceBinder(HidDeviceService service) {
+ mService = service;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private HidDeviceService getService(AttributionSource source) {
+ // Cache mService because it can change while getService is called
+ HidDeviceService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @Override
+ public boolean registerApp(
+ BluetoothHidDeviceAppSdpSettings sdp,
+ BluetoothHidDeviceAppQosSettings inQos,
+ BluetoothHidDeviceAppQosSettings outQos,
+ IBluetoothHidDeviceCallback callback,
+ AttributionSource source) {
+ Log.d(TAG, "registerApp()");
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.registerApp(sdp, inQos, outQos, callback);
+ }
+
+ @Override
+ public boolean unregisterApp(AttributionSource source) {
+ Log.d(TAG, "unregisterApp()");
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.unregisterApp();
+ }
+
+ @Override
+ public boolean sendReport(
+ BluetoothDevice device, int id, byte[] data, AttributionSource source) {
+ Log.d(TAG, "sendReport(): device=" + device + " id=" + id);
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.sendReport(device, id, data);
+ }
+
+ @Override
+ public boolean replyReport(
+ BluetoothDevice device, byte type, byte id, byte[] data, AttributionSource source) {
+ Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.replyReport(device, type, id, data);
+ }
+
+ @Override
+ public boolean unplug(BluetoothDevice device, AttributionSource source) {
+ Log.d(TAG, "unplug(): device=" + device);
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.unplug(device);
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ Log.d(TAG, "connect(): device=" + device);
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ Log.d(TAG, "disconnect(): device=" + device);
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ Log.d(
+ TAG,
+ "setConnectionPolicy():"
+ + (" device=" + device)
+ + (" connectionPolicy=" + connectionPolicy));
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public boolean reportError(BluetoothDevice device, byte error, AttributionSource source) {
+ Log.d(TAG, "reportError(): device=" + device + " error=" + error);
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.reportError(device, error);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ Log.d(TAG, "getConnectionState(): device=" + device);
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return BluetoothHidDevice.STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ Log.d(TAG, "getConnectedDevices()");
+
+ return getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}, source);
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ Log.d(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
+
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public String getUserAppName(AttributionSource source) {
+ HidDeviceService service = getService(source);
+ if (service == null) {
+ return "";
+ }
+ return service.getUserAppName();
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/hid/HidHostService.java b/android/app/src/com/android/bluetooth/hid/HidHostService.java
index 3a29ed5b62..1eac6ed336 100644
--- a/android/app/src/com/android/bluetooth/hid/HidHostService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidHostService.java
@@ -17,7 +17,6 @@
package com.android.bluetooth.hid;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
@@ -27,14 +26,11 @@ import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHidHost;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothHidHost;
-import android.content.AttributionSource;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@@ -145,7 +141,7 @@ public class HidHostService extends ProfileService {
@Override
public IProfileServiceBinder initBinder() {
- return new BluetoothHidHostBinder(this);
+ return new HidHostServiceBinder(this);
}
@Override
@@ -713,207 +709,6 @@ public class HidHostService extends ProfileService {
return true;
}
- @VisibleForTesting
- static class BluetoothHidHostBinder extends IBluetoothHidHost.Stub
- implements IProfileServiceBinder {
- private HidHostService mService;
-
- BluetoothHidHostBinder(HidHostService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private HidHostService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- HidHostService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- return service;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.disconnect(device);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
- return service.getConnectionState(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- return getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}, source);
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public boolean setPreferredTransport(
- BluetoothDevice device, int transport, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.setPreferredTransport(device, transport);
- }
-
- @Override
- public int getPreferredTransport(BluetoothDevice device, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return BluetoothDevice.TRANSPORT_AUTO;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getPreferredTransport(device);
- }
-
- /* The following APIs regarding test app for compliance */
- @Override
- public boolean getProtocolMode(BluetoothDevice device, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.getProtocolMode(device);
- }
-
- @Override
- public boolean virtualUnplug(BluetoothDevice device, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.virtualUnplug(device);
- }
-
- @Override
- public boolean setProtocolMode(
- BluetoothDevice device, int protocolMode, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.setProtocolMode(device, protocolMode);
- }
-
- @Override
- public boolean getReport(
- BluetoothDevice device,
- byte reportType,
- byte reportId,
- int bufferSize,
- AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.getReport(device, reportType, reportId, bufferSize);
- }
-
- @Override
- public boolean setReport(
- BluetoothDevice device, byte reportType, String report, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.setReport(device, reportType, report);
- }
-
- @Override
- public boolean sendData(BluetoothDevice device, String report, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.sendData(device, report);
- }
-
- @Override
- public boolean setIdleTime(
- BluetoothDevice device, byte idleTime, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.setIdleTime(device, idleTime);
- }
-
- @Override
- public boolean getIdleTime(BluetoothDevice device, AttributionSource source) {
- HidHostService service = getService(source);
- if (service == null) {
- return false;
- }
- return service.getIdleTime(device);
- }
- }
- ;
-
// APIs
/**
@@ -982,7 +777,6 @@ public class HidHostService extends ProfileService {
return STATE_DISCONNECTED;
}
- @VisibleForTesting
List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Log.d(TAG, "getDevicesMatchingConnectionStates()");
return mInputDevices.entrySet().stream()
@@ -1073,7 +867,6 @@ public class HidHostService extends ProfileService {
/**
* @see BluetoothHidHost#getPreferredTransport
*/
- @VisibleForTesting
int getPreferredTransport(BluetoothDevice device) {
Log.d(TAG, "getPreferredTransport: device=" + device);
@@ -1082,7 +875,6 @@ public class HidHostService extends ProfileService {
}
/* The following APIs regarding test app for compliance */
- @VisibleForTesting
boolean getProtocolMode(BluetoothDevice device) {
Log.d(TAG, "getProtocolMode: device=" + device);
int state = this.getConnectionState(device);
@@ -1094,7 +886,6 @@ public class HidHostService extends ProfileService {
return true;
}
- @VisibleForTesting
boolean virtualUnplug(BluetoothDevice device) {
Log.d(TAG, "virtualUnplug: device=" + device);
int state = this.getConnectionState(device);
@@ -1106,7 +897,6 @@ public class HidHostService extends ProfileService {
return true;
}
- @VisibleForTesting
boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
Log.d(TAG, "setProtocolMode: device=" + device);
int state = this.getConnectionState(device);
@@ -1120,7 +910,6 @@ public class HidHostService extends ProfileService {
return true;
}
- @VisibleForTesting
boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
Log.d(TAG, "getReport: device=" + device);
int state = this.getConnectionState(device);
@@ -1138,7 +927,6 @@ public class HidHostService extends ProfileService {
return true;
}
- @VisibleForTesting
boolean setReport(BluetoothDevice device, byte reportType, String report) {
Log.d(TAG, "setReport: device=" + device);
int state = this.getConnectionState(device);
@@ -1155,7 +943,6 @@ public class HidHostService extends ProfileService {
return true;
}
- @VisibleForTesting
boolean sendData(BluetoothDevice device, String report) {
Log.d(TAG, "sendData: device=" + device);
int state = this.getConnectionState(device);
@@ -1393,8 +1180,9 @@ public class HidHostService extends ProfileService {
// Allow this connection only if the device is bonded. Any attempt to connect
// while bonding would potentially lead to an unauthorized connection.
if (bondState != BluetoothDevice.BOND_BONDED) {
- Log.w(TAG, "okToConnect: return false, device=" + device + " bondState="
- + bondState);
+ Log.w(
+ TAG,
+ "okToConnect: return false, device=" + device + " bondState=" + bondState);
return false;
}
}
diff --git a/android/app/src/com/android/bluetooth/hid/HidHostServiceBinder.java b/android/app/src/com/android/bluetooth/hid/HidHostServiceBinder.java
new file mode 100644
index 0000000000..3295c83f01
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/hid/HidHostServiceBinder.java
@@ -0,0 +1,232 @@
+/*
+ * 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.hid;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothHidHost;
+import android.content.AttributionSource;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class HidHostServiceBinder extends IBluetoothHidHost.Stub implements IProfileServiceBinder {
+ private static final String TAG = HidHostServiceBinder.class.getSimpleName();
+
+ private HidHostService mService;
+
+ HidHostServiceBinder(HidHostService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private HidHostService getService(AttributionSource source) {
+ HidHostService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ return service;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.disconnect(device);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ return getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}, source);
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public boolean setPreferredTransport(
+ BluetoothDevice device, int transport, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setPreferredTransport(device, transport);
+ }
+
+ @Override
+ public int getPreferredTransport(BluetoothDevice device, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return BluetoothDevice.TRANSPORT_AUTO;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getPreferredTransport(device);
+ }
+
+ /* The following APIs regarding test app for compliance */
+ @Override
+ public boolean getProtocolMode(BluetoothDevice device, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.getProtocolMode(device);
+ }
+
+ @Override
+ public boolean virtualUnplug(BluetoothDevice device, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.virtualUnplug(device);
+ }
+
+ @Override
+ public boolean setProtocolMode(
+ BluetoothDevice device, int protocolMode, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.setProtocolMode(device, protocolMode);
+ }
+
+ @Override
+ public boolean getReport(
+ BluetoothDevice device,
+ byte reportType,
+ byte reportId,
+ int bufferSize,
+ AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.getReport(device, reportType, reportId, bufferSize);
+ }
+
+ @Override
+ public boolean setReport(
+ BluetoothDevice device, byte reportType, String report, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.setReport(device, reportType, report);
+ }
+
+ @Override
+ public boolean sendData(BluetoothDevice device, String report, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.sendData(device, report);
+ }
+
+ @Override
+ public boolean setIdleTime(BluetoothDevice device, byte idleTime, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.setIdleTime(device, idleTime);
+ }
+
+ @Override
+ public boolean getIdleTime(BluetoothDevice device, AttributionSource source) {
+ HidHostService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.getIdleTime(device);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
index de152f0ae1..4f0d215e5b 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -36,7 +36,6 @@ import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
@@ -61,7 +60,6 @@ import android.bluetooth.le.IScannerCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
-import android.content.AttributionSource;
import android.content.Context;
import android.content.Intent;
import android.media.AudioDeviceCallback;
@@ -621,7 +619,7 @@ public class LeAudioService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothLeAudioBinder(this);
+ return new LeAudioServiceBinder(this);
}
public static boolean isEnabled() {
@@ -805,12 +803,12 @@ public class LeAudioService extends ProfileService {
mLeAudioBroadcasterNativeInterface.ifPresent(i -> i.cleanup());
- try {
- mStateMachinesThread.quitSafely();
- mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS);
- } catch (InterruptedException e) {
- // Do not rethrow as we are shutting down anyway
- }
+ try {
+ mStateMachinesThread.quitSafely();
+ mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ // Do not rethrow as we are shutting down anyway
+ }
mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback);
@@ -1834,8 +1832,10 @@ public class LeAudioService extends ProfileService {
if (device != null && mActiveAudioInDevice != null) {
LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(mActiveAudioInDevice);
if (deviceDescriptor == null) {
- Log.e(TAG, "updateActiveInDevice: No valid descriptor for device: "
- + mActiveAudioInDevice);
+ Log.e(
+ TAG,
+ "updateActiveInDevice: No valid descriptor for device: "
+ + mActiveAudioInDevice);
return false;
}
@@ -1900,8 +1900,10 @@ public class LeAudioService extends ProfileService {
if (device != null && mActiveAudioOutDevice != null) {
LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(mActiveAudioOutDevice);
if (deviceDescriptor == null) {
- Log.e(TAG, "updateActiveOutDevice: No valid descriptor for device: "
- + mActiveAudioOutDevice);
+ Log.e(
+ TAG,
+ "updateActiveOutDevice: No valid descriptor for device: "
+ + mActiveAudioOutDevice);
return false;
}
@@ -2129,9 +2131,7 @@ public class LeAudioService extends ProfileService {
Log.d(TAG, "Scanner is not running (mScannerId=" + mScannerId + ")");
return;
}
- mAdapterService
- .getBluetoothScanController()
- .stopScanInternal(mScannerId);
+ mAdapterService.getBluetoothScanController().stopScanInternal(mScannerId);
mAdapterService.getBluetoothScanController().unregisterScannerInternal(mScannerId);
mScannerId = SCANNER_NOT_INITIALIZED;
@@ -3226,8 +3226,10 @@ public class LeAudioService extends ProfileService {
boolean ringtoneContextAvailable = false;
if (groupDescriptor.mAvailableContexts != null) {
- ringtoneContextAvailable = ((groupDescriptor.mAvailableContexts &
- BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) != 0);
+ ringtoneContextAvailable =
+ ((groupDescriptor.mAvailableContexts
+ & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE)
+ != 0);
}
/* Enables in-band ringtone only for the currently active device or
@@ -3724,9 +3726,7 @@ public class LeAudioService extends ProfileService {
} else if (isInitial) {
Log.i(
TAG,
- " New group "
- + groupId
- + " with no context types available");
+ " New group " + groupId + " with no context types available");
descriptor.mInactivatedDueToContextType = true;
}
return;
@@ -3794,9 +3794,7 @@ public class LeAudioService extends ProfileService {
{
LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
if (descriptor == null) {
- Log.e(
- TAG,
- "deviceDisconnected: no descriptors for group: " + groupId);
+ Log.e(TAG, "deviceDisconnected: no descriptors for group: " + groupId);
return;
}
@@ -4349,7 +4347,8 @@ public class LeAudioService extends ProfileService {
if (getConnectedPeerDevices(groupId).isEmpty()) {
descriptor.mIsConnected = false;
descriptor.mAutoActiveModeEnabled = true;
- descriptor.mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0;
+ descriptor.mAvailableContexts =
+ Flags.leaudioUnicastNoAvailableContexts() ? null : 0;
if (descriptor.isActive()) {
/* Notify Native layer */
removeActiveDevice(hasFallbackDevice);
@@ -5524,12 +5523,15 @@ public class LeAudioService extends ProfileService {
* device should be removed from active devices.
*/
int newDirection = AUDIO_DIRECTION_NONE;
- int oldDirection = oldFallbackGroupDescriptor != null
- ? oldFallbackGroupDescriptor.mDirection : AUDIO_DIRECTION_NONE;
+ int oldDirection =
+ oldFallbackGroupDescriptor != null
+ ? oldFallbackGroupDescriptor.mDirection
+ : AUDIO_DIRECTION_NONE;
boolean notifyAndUpdateInactiveOutDeviceOnly = false;
- boolean hasFallbackDeviceWhenGettingInactive = oldFallbackGroupDescriptor != null
- ? oldFallbackGroupDescriptor.mHasFallbackDeviceWhenGettingInactive
- : false;
+ boolean hasFallbackDeviceWhenGettingInactive =
+ oldFallbackGroupDescriptor != null
+ ? oldFallbackGroupDescriptor.mHasFallbackDeviceWhenGettingInactive
+ : false;
if (groupId != LE_AUDIO_GROUP_ID_INVALID) {
newDirection = AUDIO_DIRECTION_INPUT_BIT;
notifyAndUpdateInactiveOutDeviceOnly = true;
@@ -5723,525 +5725,6 @@ public class LeAudioService extends ProfileService {
}
}
- /** Binder object: must be a static class or memory leak may occur */
- @VisibleForTesting
- static class BluetoothLeAudioBinder extends IBluetoothLeAudio.Stub
- implements IProfileServiceBinder {
- private LeAudioService mService;
-
- BluetoothLeAudioBinder(LeAudioService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private LeAudioService getServiceAndEnforceConnect(AttributionSource source) {
- requireNonNull(source);
- // Cache mService because it can change while getService is called
- LeAudioService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
- return service;
- }
-
- private LeAudioService getService() {
- // Cache mService because it can change while getService is called
- LeAudioService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) {
- return null;
- }
- return service;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnect(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getConnectedDevices();
- }
-
- @Override
- public BluetoothDevice getConnectedGroupLeadDevice(int groupId, AttributionSource source) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return null;
- }
-
- return service.getConnectedGroupLeadDevice(groupId);
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- if (device == null) {
- return service.removeActiveDevice(true);
- } else {
- return service.setActiveDevice(device);
- }
- }
-
- @Override
- public List<BluetoothDevice> getActiveDevices(AttributionSource source) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getActiveDevices();
- }
-
- @Override
- public int getAudioLocation(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return BluetoothLeAudio.AUDIO_LOCATION_INVALID;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getAudioLocation(device);
- }
-
- @Override
- public boolean isInbandRingtoneEnabled(AttributionSource source, int groupId) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.isInbandRingtoneEnabled(groupId);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public void setCcidInformation(
- ParcelUuid userUuid, int ccid, int contextType, AttributionSource source) {
- requireNonNull(userUuid);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setCcidInformation(userUuid, ccid, contextType);
- }
-
- @Override
- public int getGroupId(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return LE_AUDIO_GROUP_ID_INVALID;
- }
-
- return service.getGroupId(device);
- }
-
- @Override
- public boolean groupAddNode(int groupId, BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.groupAddNode(groupId, device);
- }
-
- @Override
- public void setInCall(boolean inCall, AttributionSource source) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setInCall(inCall);
- }
-
- @Override
- public void setInactiveForHfpHandover(
- BluetoothDevice hfpHandoverDevice, AttributionSource source) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setInactiveForHfpHandover(hfpHandoverDevice);
- }
-
- @Override
- public boolean groupRemoveNode(
- int groupId, BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.groupRemoveNode(groupId, device);
- }
-
- @Override
- public void setVolume(int volume, AttributionSource source) {
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setVolume(volume);
- }
-
- @Override
- public void registerCallback(IBluetoothLeAudioCallback callback, AttributionSource source) {
- requireNonNull(callback);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.registerCallback(callback);
- }
-
- @Override
- public void unregisterCallback(
- IBluetoothLeAudioCallback callback, AttributionSource source) {
- requireNonNull(callback);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- service.unregisterCallback(callback);
- }
-
- @Override
- public void registerLeBroadcastCallback(
- IBluetoothLeBroadcastCallback callback, AttributionSource source) {
- requireNonNull(callback);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.registerLeBroadcastCallback(callback);
- }
-
- @Override
- public void unregisterLeBroadcastCallback(
- IBluetoothLeBroadcastCallback callback, AttributionSource source) {
- requireNonNull(callback);
- requireNonNull(source);
-
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.unregisterLeBroadcastCallback(callback);
- }
-
- @Override
- public void startBroadcast(
- BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.createBroadcast(broadcastSettings);
- }
-
- @Override
- public void stopBroadcast(int broadcastId, AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.stopBroadcast(broadcastId);
- }
-
- @Override
- public void updateBroadcast(
- int broadcastId,
- BluetoothLeBroadcastSettings broadcastSettings,
- AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.updateBroadcast(broadcastId, broadcastSettings);
- }
-
- @Override
- public boolean isPlaying(int broadcastId, AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.isPlaying(broadcastId);
- }
-
- @Override
- public List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata(
- AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getAllBroadcastMetadata();
- }
-
- @Override
- public int getMaximumNumberOfBroadcasts() {
- LeAudioService service = getService();
- if (service == null) {
- return 0;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getMaximumNumberOfBroadcasts();
- }
-
- @Override
- public int getMaximumStreamsPerBroadcast() {
- LeAudioService service = getService();
- if (service == null) {
- return 0;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getMaximumStreamsPerBroadcast();
- }
-
- @Override
- public int getMaximumSubgroupsPerBroadcast() {
- LeAudioService service = getService();
- if (service == null) {
- return 0;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getMaximumSubgroupsPerBroadcast();
- }
-
- @Override
- public BluetoothLeAudioCodecStatus getCodecStatus(int groupId, AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getCodecStatus(groupId);
- }
-
- @Override
- public void setCodecConfigPreference(
- int groupId,
- BluetoothLeAudioCodecConfig inputCodecConfig,
- BluetoothLeAudioCodecConfig outputCodecConfig,
- AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig);
- }
-
- @Override
- public void setBroadcastToUnicastFallbackGroup(int groupId, AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setBroadcastToUnicastFallbackGroup(groupId);
- }
-
- @Override
- public int getBroadcastToUnicastFallbackGroup(AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return LE_AUDIO_GROUP_ID_INVALID;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getBroadcastToUnicastFallbackGroup();
- }
-
- @Override
- public boolean isBroadcastActive(AttributionSource source) {
- LeAudioService service = getServiceAndEnforceConnect(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.isBroadcastActive();
- }
- }
-
@Override
public void dump(StringBuilder sb) {
super.dump(sb);
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioServiceBinder.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioServiceBinder.java
new file mode 100644
index 0000000000..e774c8fe33
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioServiceBinder.java
@@ -0,0 +1,548 @@
+/*
+ * 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.le_audio;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothLeAudioCodecConfig;
+import android.bluetooth.BluetoothLeAudioCodecStatus;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastSettings;
+import android.bluetooth.IBluetoothLeAudio;
+import android.bluetooth.IBluetoothLeAudioCallback;
+import android.bluetooth.IBluetoothLeBroadcastCallback;
+import android.content.AttributionSource;
+import android.os.ParcelUuid;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class LeAudioServiceBinder extends IBluetoothLeAudio.Stub implements IProfileServiceBinder {
+ private static final String TAG = LeAudioServiceBinder.class.getSimpleName();
+
+ private LeAudioService mService;
+
+ LeAudioServiceBinder(LeAudioService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ private LeAudioService getService() {
+ LeAudioService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private LeAudioService getServiceAndEnforceConnect(AttributionSource source) {
+ requireNonNull(source);
+ LeAudioService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public BluetoothDevice getConnectedGroupLeadDevice(int groupId, AttributionSource source) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return null;
+ }
+ return service.getConnectedGroupLeadDevice(groupId);
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ if (device == null) {
+ return service.removeActiveDevice(true);
+ } else {
+ return service.setActiveDevice(device);
+ }
+ }
+
+ @Override
+ public List<BluetoothDevice> getActiveDevices(AttributionSource source) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getActiveDevices();
+ }
+
+ @Override
+ public int getAudioLocation(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return BluetoothLeAudio.AUDIO_LOCATION_INVALID;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getAudioLocation(device);
+ }
+
+ @Override
+ public boolean isInbandRingtoneEnabled(AttributionSource source, int groupId) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isInbandRingtoneEnabled(groupId);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public void setCcidInformation(
+ ParcelUuid userUuid, int ccid, int contextType, AttributionSource source) {
+ requireNonNull(userUuid);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setCcidInformation(userUuid, ccid, contextType);
+ }
+
+ @Override
+ public int getGroupId(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return LE_AUDIO_GROUP_ID_INVALID;
+ }
+ return service.getGroupId(device);
+ }
+
+ @Override
+ public boolean groupAddNode(int groupId, BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.groupAddNode(groupId, device);
+ }
+
+ @Override
+ public void setInCall(boolean inCall, AttributionSource source) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setInCall(inCall);
+ }
+
+ @Override
+ public void setInactiveForHfpHandover(
+ BluetoothDevice hfpHandoverDevice, AttributionSource source) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setInactiveForHfpHandover(hfpHandoverDevice);
+ }
+
+ @Override
+ public boolean groupRemoveNode(int groupId, BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.groupRemoveNode(groupId, device);
+ }
+
+ @Override
+ public void setVolume(int volume, AttributionSource source) {
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setVolume(volume);
+ }
+
+ @Override
+ public void registerCallback(IBluetoothLeAudioCallback callback, AttributionSource source) {
+ requireNonNull(callback);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.registerCallback(callback);
+ }
+
+ @Override
+ public void unregisterCallback(IBluetoothLeAudioCallback callback, AttributionSource source) {
+ requireNonNull(callback);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.unregisterCallback(callback);
+ }
+
+ @Override
+ public void registerLeBroadcastCallback(
+ IBluetoothLeBroadcastCallback callback, AttributionSource source) {
+ requireNonNull(callback);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.registerLeBroadcastCallback(callback);
+ }
+
+ @Override
+ public void unregisterLeBroadcastCallback(
+ IBluetoothLeBroadcastCallback callback, AttributionSource source) {
+ requireNonNull(callback);
+ requireNonNull(source);
+
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.unregisterLeBroadcastCallback(callback);
+ }
+
+ @Override
+ public void startBroadcast(
+ BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.createBroadcast(broadcastSettings);
+ }
+
+ @Override
+ public void stopBroadcast(int broadcastId, AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.stopBroadcast(broadcastId);
+ }
+
+ @Override
+ public void updateBroadcast(
+ int broadcastId,
+ BluetoothLeBroadcastSettings broadcastSettings,
+ AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.updateBroadcast(broadcastId, broadcastSettings);
+ }
+
+ @Override
+ public boolean isPlaying(int broadcastId, AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isPlaying(broadcastId);
+ }
+
+ @Override
+ public List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata(AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getAllBroadcastMetadata();
+ }
+
+ @Override
+ public int getMaximumNumberOfBroadcasts() {
+ LeAudioService service = getService();
+ if (service == null) {
+ return 0;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getMaximumNumberOfBroadcasts();
+ }
+
+ @Override
+ public int getMaximumStreamsPerBroadcast() {
+ LeAudioService service = getService();
+ if (service == null) {
+ return 0;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getMaximumStreamsPerBroadcast();
+ }
+
+ @Override
+ public int getMaximumSubgroupsPerBroadcast() {
+ LeAudioService service = getService();
+ if (service == null) {
+ return 0;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getMaximumSubgroupsPerBroadcast();
+ }
+
+ @Override
+ public BluetoothLeAudioCodecStatus getCodecStatus(int groupId, AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getCodecStatus(groupId);
+ }
+
+ @Override
+ public void setCodecConfigPreference(
+ int groupId,
+ BluetoothLeAudioCodecConfig inputCodecConfig,
+ BluetoothLeAudioCodecConfig outputCodecConfig,
+ AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig);
+ }
+
+ @Override
+ public void setBroadcastToUnicastFallbackGroup(int groupId, AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setBroadcastToUnicastFallbackGroup(groupId);
+ }
+
+ @Override
+ public int getBroadcastToUnicastFallbackGroup(AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return LE_AUDIO_GROUP_ID_INVALID;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getBroadcastToUnicastFallbackGroup();
+ }
+
+ @Override
+ public boolean isBroadcastActive(AttributionSource source) {
+ LeAudioService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isBroadcastActive();
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapService.java b/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
index bac8bdc247..b43af84dcc 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
@@ -18,7 +18,6 @@ package com.android.bluetooth.map;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
@@ -33,9 +32,7 @@ import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothMap;
import android.bluetooth.SdpMnsRecord;
-import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -66,7 +63,6 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -149,7 +145,6 @@ public class BluetoothMapService extends ProfileService {
private static BluetoothMapService sBluetoothMapService;
-
private static final ParcelUuid[] MAP_UUIDS = {
BluetoothUuid.MAP, BluetoothUuid.MNS,
};
@@ -705,7 +700,7 @@ public class BluetoothMapService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothMapBinder(this);
+ return new BluetoothMapServiceBinder(this);
}
/**
@@ -1190,227 +1185,6 @@ public class BluetoothMapService extends ProfileService {
}
}
- // Binder object: Must be static class or memory leak may occur
-
- /**
- * This class implements the IBluetoothMap interface - or actually it validates the
- * preconditions for calling the actual functionality in the MapService, and calls it.
- */
- @VisibleForTesting
- static class BluetoothMapBinder extends IBluetoothMap.Stub implements IProfileServiceBinder {
- private BluetoothMapService mService;
-
- BluetoothMapBinder(BluetoothMapService service) {
- mService = service;
- }
-
- @Override
- public synchronized void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private BluetoothMapService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- BluetoothMapService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
- return service;
- }
-
- @Override
- public int getState(AttributionSource source) {
- Log.v(TAG, "getState()");
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- return BluetoothMap.STATE_DISCONNECTED;
- }
-
- return service.getState();
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 16);
- throw e;
- }
- }
-
- @Override
- public BluetoothDevice getClient(AttributionSource source) {
- Log.v(TAG, "getClient()");
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- Log.v(TAG, "getClient() - no service - returning " + null);
- return null;
- }
- BluetoothDevice client = service.getRemoteDevice();
- Log.v(TAG, "getClient() - returning " + client);
- return client;
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 17);
- throw e;
- }
- }
-
- @Override
- public boolean isConnected(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "isConnected()");
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.getConnectionState(device) == STATE_CONNECTED;
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 18);
- throw e;
- }
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "disconnect()");
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.disconnect(device);
- return true;
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 19);
- throw e;
- }
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- Log.v(TAG, "getConnectedDevices()");
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getConnectedDevices();
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 20);
- throw e;
- }
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- Log.v(TAG, "getDevicesMatchingConnectionStates()");
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getDevicesMatchingConnectionStates(states);
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 21);
- throw e;
- }
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "getConnectionState()");
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 22);
- throw e;
- }
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.setConnectionPolicy(device, connectionPolicy);
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 23);
- throw e;
- }
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- try {
- BluetoothMapService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- return service.getConnectionPolicy(device);
- } catch (RuntimeException e) {
- ContentProfileErrorReportUtils.report(
- BluetoothProfile.MAP,
- BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
- BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
- 24);
- throw e;
- }
- }
- }
-
@Override
public void dump(StringBuilder sb) {
super.dump(sb);
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapServiceBinder.java b/android/app/src/com/android/bluetooth/map/BluetoothMapServiceBinder.java
new file mode 100644
index 0000000000..0706bcab6f
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapServiceBinder.java
@@ -0,0 +1,251 @@
+/*
+ * 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.map;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProtoEnums;
+import android.bluetooth.IBluetoothMap;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.BluetoothStatsLog;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class implements the IBluetoothMap interface - or actually it validates the preconditions
+ * for calling the actual functionality in the MapService, and calls it.
+ */
+class BluetoothMapServiceBinder extends IBluetoothMap.Stub implements IProfileServiceBinder {
+ private static final String TAG = BluetoothMapServiceBinder.class.getSimpleName();
+
+ private BluetoothMapService mService;
+
+ BluetoothMapServiceBinder(BluetoothMapService service) {
+ mService = service;
+ }
+
+ @Override
+ public synchronized void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private BluetoothMapService getService(AttributionSource source) {
+ BluetoothMapService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @Override
+ public int getState(AttributionSource source) {
+ Log.v(TAG, "getState()");
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ return BluetoothMap.STATE_DISCONNECTED;
+ }
+ return service.getState();
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 16);
+ throw e;
+ }
+ }
+
+ @Override
+ public BluetoothDevice getClient(AttributionSource source) {
+ Log.v(TAG, "getClient()");
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ Log.v(TAG, "getClient() - no service - returning " + null);
+ return null;
+ }
+ BluetoothDevice client = service.getRemoteDevice();
+ Log.v(TAG, "getClient() - returning " + client);
+ return client;
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 17);
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean isConnected(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "isConnected()");
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.getConnectionState(device) == STATE_CONNECTED;
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 18);
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "disconnect()");
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ service.disconnect(device);
+ return true;
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 19);
+ throw e;
+ }
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ Log.v(TAG, "getConnectedDevices()");
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectedDevices();
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 20);
+ throw e;
+ }
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ Log.v(TAG, "getDevicesMatchingConnectionStates()");
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 21);
+ throw e;
+ }
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "getConnectionState()");
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 22);
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 23);
+ throw e;
+ }
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ try {
+ BluetoothMapService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+ return service.getConnectionPolicy(device);
+ } catch (RuntimeException e) {
+ ContentProfileErrorReportUtils.report(
+ BluetoothProfile.MAP,
+ BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE,
+ BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
+ 24);
+ throw e;
+ }
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
index 5e2be59b11..fdecfddf15 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
@@ -16,11 +16,8 @@
package com.android.bluetooth.mapclient;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
@@ -28,16 +25,12 @@ import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElseGet;
-import android.Manifest;
-import android.annotation.RequiresPermission;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothMapClient;
import android.bluetooth.SdpMasRecord;
-import android.content.AttributionSource;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -46,7 +39,6 @@ import android.os.Parcelable;
import android.sysprop.BluetoothProperties;
import android.util.Log;
-import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
@@ -54,7 +46,6 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -307,7 +298,7 @@ public class MapClientService extends ProfileService {
@Override
public IProfileServiceBinder initBinder() {
- return new Binder(this);
+ return new MapClientServiceBinder(this);
}
@Override
@@ -408,172 +399,6 @@ public class MapClientService extends ProfileService {
}
}
- // Binder object: Must be static class or memory leak may occur
-
- /**
- * This class implements the IClient interface - or actually it validates the preconditions for
- * calling the actual functionality in the MapClientService, and calls it.
- */
- @VisibleForTesting
- static class Binder extends IBluetoothMapClient.Stub implements IProfileServiceBinder {
- private MapClientService mService;
-
- Binder(MapClientService service) {
- mService = service;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private MapClientService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- MapClientService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !(getCallingUserHandle().isSystem()
- || Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG))
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
- return service;
- }
-
- @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
- private MapClientService getServiceAndEnforcePrivileged(AttributionSource source) {
- // Cache mService because it can change while getService is called
- MapClientService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !(getCallingUserHandle().isSystem()
- || Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG))
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "connect()");
-
- MapClientService service = getServiceAndEnforcePrivileged(source);
- if (service == null) {
- return false;
- }
-
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "disconnect()");
-
- MapClientService service = getServiceAndEnforcePrivileged(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnect(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- Log.v(TAG, "getConnectedDevices()");
-
- MapClientService service = getServiceAndEnforcePrivileged(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- Log.v(TAG, "getDevicesMatchingConnectionStates()");
-
- MapClientService service = getServiceAndEnforcePrivileged(source);
- if (service == null) {
- return Collections.emptyList();
- }
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "getConnectionState()");
-
- MapClientService service = getServiceAndEnforcePrivileged(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- Log.v(TAG, "setConnectionPolicy()");
-
- MapClientService service = getServiceAndEnforcePrivileged(source);
- if (service == null) {
- return false;
- }
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "getConnectionPolicy()");
-
- MapClientService service = getServiceAndEnforcePrivileged(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public boolean sendMessage(
- BluetoothDevice device,
- Uri[] contacts,
- String message,
- PendingIntent sentIntent,
- PendingIntent deliveredIntent,
- AttributionSource source) {
- Log.v(TAG, "sendMessage()");
-
- MapClientService service = getService(source);
- if (service == null) {
- return false;
- }
-
- Log.d(TAG, "Checking Permission of sendMessage");
- mService.enforceCallingOrSelfPermission(
- Manifest.permission.SEND_SMS, "Need SEND_SMS permission");
-
- return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
- }
- }
-
public void aclDisconnected(BluetoothDevice device, int transport) {
mHandler.post(() -> handleAclDisconnected(device, transport));
}
diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientServiceBinder.java b/android/app/src/com/android/bluetooth/mapclient/MapClientServiceBinder.java
new file mode 100644
index 0000000000..d6be8975e8
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/mapclient/MapClientServiceBinder.java
@@ -0,0 +1,193 @@
+/*
+ * 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.mapclient;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothMapClient;
+import android.content.AttributionSource;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class implements the IClient interface - or actually it validates the preconditions for
+ * calling the actual functionality in the MapClientService, and calls it.
+ */
+class MapClientServiceBinder extends IBluetoothMapClient.Stub implements IProfileServiceBinder {
+ private static final String TAG = MapClientServiceBinder.class.getSimpleName();
+
+ private MapClientService mService;
+
+ MapClientServiceBinder(MapClientService service) {
+ mService = service;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private MapClientService getService(AttributionSource source) {
+ MapClientService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !(getCallingUserHandle().isSystem()
+ || Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG))
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+ return service;
+ }
+
+ @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+ private MapClientService getServiceAndEnforcePrivileged(AttributionSource source) {
+ MapClientService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !(getCallingUserHandle().isSystem()
+ || Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG))
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "connect()");
+
+ MapClientService service = getServiceAndEnforcePrivileged(source);
+ if (service == null) {
+ return false;
+ }
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "disconnect()");
+
+ MapClientService service = getServiceAndEnforcePrivileged(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ Log.v(TAG, "getConnectedDevices()");
+
+ MapClientService service = getServiceAndEnforcePrivileged(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ Log.v(TAG, "getDevicesMatchingConnectionStates()");
+
+ MapClientService service = getServiceAndEnforcePrivileged(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "getConnectionState()");
+
+ MapClientService service = getServiceAndEnforcePrivileged(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ Log.v(TAG, "setConnectionPolicy()");
+
+ MapClientService service = getServiceAndEnforcePrivileged(source);
+ if (service == null) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "getConnectionPolicy()");
+
+ MapClientService service = getServiceAndEnforcePrivileged(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public boolean sendMessage(
+ BluetoothDevice device,
+ Uri[] contacts,
+ String message,
+ PendingIntent sentIntent,
+ PendingIntent deliveredIntent,
+ AttributionSource source) {
+ Log.v(TAG, "sendMessage()");
+
+ MapClientService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ Log.d(TAG, "Checking Permission of sendMessage");
+ mService.enforceCallingOrSelfPermission(
+ Manifest.permission.SEND_SMS, "Need SEND_SMS permission");
+
+ return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/mcp/McpService.java b/android/app/src/com/android/bluetooth/mcp/McpService.java
index c0ba4f12bb..6f08be0061 100644
--- a/android/app/src/com/android/bluetooth/mcp/McpService.java
+++ b/android/app/src/com/android/bluetooth/mcp/McpService.java
@@ -17,12 +17,9 @@
package com.android.bluetooth.mcp;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.IBluetoothMcpServiceManager;
-import android.content.AttributionSource;
import android.content.Context;
import android.os.ParcelUuid;
import android.sysprop.BluetoothProperties;
@@ -88,7 +85,7 @@ public class McpService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothMcpServiceBinder(this);
+ return new McpServiceBinder(this);
}
@Override
@@ -198,41 +195,4 @@ public class McpService extends ProfileService {
int ccid, BluetoothDevice device, ParcelUuid charUuid, boolean doNotify) {
mGmcs.setNotificationSubscription(ccid, device, charUuid, doNotify);
}
-
- /** Binder object: must be a static class or memory leak may occur */
- static class BluetoothMcpServiceBinder extends IBluetoothMcpServiceManager.Stub
- implements IProfileServiceBinder {
- private McpService mService;
-
- BluetoothMcpServiceBinder(McpService svc) {
- mService = svc;
- }
-
- private McpService getService() {
- if (mService != null && mService.isAvailable()) {
- return mService;
- }
- Log.e(TAG, "getService() - Service requested, but not available!");
- return null;
- }
-
- @Override
- public void setDeviceAuthorized(
- BluetoothDevice device, boolean isAuthorized, AttributionSource source) {
- McpService service = getService();
- if (service == null) {
- return;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setDeviceAuthorized(device, isAuthorized);
- }
-
- @Override
- public void cleanup() {
- if (mService != null) {
- mService.cleanup();
- }
- mService = null;
- }
- }
}
diff --git a/android/app/src/com/android/bluetooth/mcp/McpServiceBinder.java b/android/app/src/com/android/bluetooth/mcp/McpServiceBinder.java
new file mode 100644
index 0000000000..b22e87cadf
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/mcp/McpServiceBinder.java
@@ -0,0 +1,63 @@
+/*
+ * 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.mcp;
+
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothMcpServiceManager;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+class McpServiceBinder extends IBluetoothMcpServiceManager.Stub implements IProfileServiceBinder {
+ private static final String TAG = McpServiceBinder.class.getSimpleName();
+
+ private McpService mService;
+
+ McpServiceBinder(McpService svc) {
+ mService = svc;
+ }
+
+ private McpService getService() {
+ if (mService != null && mService.isAvailable()) {
+ return mService;
+ }
+ Log.e(TAG, "getService() - Service requested, but not available!");
+ return null;
+ }
+
+ @Override
+ public void setDeviceAuthorized(
+ BluetoothDevice device, boolean isAuthorized, AttributionSource source) {
+ McpService service = getService();
+ if (service == null) {
+ return;
+ }
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setDeviceAuthorized(device, isAuthorized);
+ }
+
+ @Override
+ public void cleanup() {
+ if (mService != null) {
+ mService.cleanup();
+ }
+ mService = null;
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
index 048c134848..73cba4e954 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
@@ -248,6 +248,12 @@ public class BluetoothOppLauncherActivity extends Activity {
permittedUris,
false /* isHandover */,
true /* fromExternal */);
+ if (Flags.sendOppDevicePickerExtraIntent()) {
+ BluetoothOppUtility
+ .grantPermissionToNearbyComponent(
+ BluetoothOppLauncherActivity.this,
+ uris);
+ }
// Done getting file info..Launch device picker
// and finish this activity
launchDevicePicker();
@@ -322,6 +328,11 @@ public class BluetoothOppLauncherActivity extends Activity {
in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE, getPackageName());
in1.putExtra(
BluetoothDevicePicker.EXTRA_LAUNCH_CLASS, BluetoothOppReceiver.class.getName());
+ if (Flags.sendOppDevicePickerExtraIntent()) {
+ in1.putExtra(
+ BluetoothDevicePicker.EXTRA_DEVICE_PICKER_ORIGINAL_SEND_INTENT,
+ getIntent());
+ }
Log.v(TAG, "Launching " + BluetoothDevicePicker.ACTION_LAUNCH);
startActivity(in1);
}
@@ -555,6 +566,10 @@ public class BluetoothOppLauncherActivity extends Activity {
void sendFileInfo(String mimeType, String uriString, boolean isHandover, boolean fromExternal) {
BluetoothOppManager manager = BluetoothOppManager.getInstance(getApplicationContext());
try {
+ if (Flags.sendOppDevicePickerExtraIntent()) {
+ BluetoothOppUtility.grantPermissionToNearbyComponent(
+ this, List.of(Uri.parse(uriString)));
+ }
manager.saveSendingFileInfo(mimeType, uriString, isHandover, fromExternal);
launchDevicePicker();
finish();
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
index 2548b9e45c..46bed2ba89 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
@@ -121,7 +121,7 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
// Since ContentObserver is created with Handler, onChange() can be called
// even after the observer is unregistered.
- if (Flags.oppIgnoreContentObserverAfterServiceStop() && mObserver != this) {
+ if (mObserver != this) {
Log.d(TAG, "onChange() called after stop() is called.");
return;
}
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java
index d7b75bfde2..4e8892ab35 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java
@@ -38,6 +38,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -50,6 +51,7 @@ import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
@@ -77,6 +79,9 @@ import java.util.concurrent.ConcurrentHashMap;
public class BluetoothOppUtility {
private static final String TAG = BluetoothOppUtility.class.getSimpleName();
+ // TODO(b/398120192): use API instead of a hardcode string.
+ private static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component";
+
/** Whether the device has the "nosdcard" characteristic, or null if not-yet-known. */
private static Boolean sNoSdCard = null;
@@ -572,4 +577,30 @@ public class BluetoothOppUtility {
NotificationManager nm = ctx.getSystemService(NotificationManager.class);
nm.cancel(BluetoothOppNotification.NOTIFICATION_ID_PROGRESS);
}
+
+ /** Grants uri read permission to nearby sharing component. */
+ static void grantPermissionToNearbyComponent(Context context, List<Uri> uris) {
+ String packageName = getNearbyComponentPackageName(context);
+ if (packageName == null) {
+ return;
+ }
+ for (Uri uri : uris) {
+ BluetoothMethodProxy.getInstance()
+ .grantUriPermission(
+ context, packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+
+ private static String getNearbyComponentPackageName(Context context) {
+ String componentString =
+ Settings.Secure.getString(context.getContentResolver(), NEARBY_SHARING_COMPONENT);
+ if (componentString == null) {
+ return null;
+ }
+ ComponentName componentName = ComponentName.unflattenFromString(componentString);
+ if (componentName == null) {
+ return null;
+ }
+ return componentName.getPackageName();
+ }
}
diff --git a/android/app/src/com/android/bluetooth/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java
index 311e100b52..5c2dfdf2a7 100644
--- a/android/app/src/com/android/bluetooth/pan/PanService.java
+++ b/android/app/src/com/android/bluetooth/pan/PanService.java
@@ -17,8 +17,6 @@
package com.android.bluetooth.pan;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
-import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
@@ -30,15 +28,12 @@ import static android.bluetooth.BluetoothUtils.logRemoteException;
import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElseGet;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothPan.LocalPanRole;
import android.bluetooth.BluetoothPan.RemotePanRole;
import android.bluetooth.BluetoothProfile;
-import android.bluetooth.IBluetoothPan;
import android.bluetooth.IBluetoothPanCallback;
-import android.content.AttributionSource;
import android.content.Intent;
import android.content.res.Resources.NotFoundException;
import android.net.TetheringInterface;
@@ -60,7 +55,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.HandlerExecutor;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -154,7 +148,7 @@ public class PanService extends ProfileService {
@Override
public IProfileServiceBinder initBinder() {
- return new BluetoothPanBinder(this);
+ return new PanServiceBinder(this);
}
public static synchronized PanService getPanService() {
@@ -276,137 +270,6 @@ public class PanService extends ProfileService {
}
}
- /** Handlers for incoming service calls */
- @VisibleForTesting
- static class BluetoothPanBinder extends IBluetoothPan.Stub implements IProfileServiceBinder {
- private PanService mService;
-
- BluetoothPanBinder(PanService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private PanService getService(AttributionSource source) {
- if (Utils.isInstrumentationTestMode()) {
- return mService;
- }
- if (!Utils.checkServiceAvailable(mService, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
- return null;
- }
- return mService;
- }
-
- @Override
- public boolean connect(BluetoothDevice device, AttributionSource source) {
- PanService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- PanService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnect(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- PanService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- PanService service = getService(source);
- if (service == null) {
- return BluetoothPan.STATE_DISCONNECTED;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean isTetheringOn(AttributionSource source) {
- // TODO(BT) have a variable marking the on/off state
- PanService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.isTetheringOn();
- }
-
- @Override
- public void setBluetoothTethering(
- IBluetoothPanCallback callback, int id, boolean value, AttributionSource source) {
- PanService service = getService(source);
- if (service == null) {
- return;
- }
-
- Log.d(
- TAG,
- "setBluetoothTethering:"
- + (" value=" + value)
- + (" pkgName= " + source.getPackageName())
- + (" mTetherOn= " + service.mTetherOn));
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.enforceCallingOrSelfPermission(TETHER_PRIVILEGED, null);
-
- service.setBluetoothTethering(callback, id, source.getUid(), value);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- PanService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- PanService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getDevicesMatchingConnectionStates(states);
- }
- }
-
public boolean connect(BluetoothDevice device) {
if (mUserManager.isGuestUser()) {
Log.w(TAG, "Guest user does not have the permission to change the WiFi network");
diff --git a/android/app/src/com/android/bluetooth/pan/PanServiceBinder.java b/android/app/src/com/android/bluetooth/pan/PanServiceBinder.java
new file mode 100644
index 0000000000..e3baae21dc
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pan/PanServiceBinder.java
@@ -0,0 +1,161 @@
+/*
+ * 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.pan;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.IBluetoothPan;
+import android.bluetooth.IBluetoothPanCallback;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+/** Handlers for incoming service calls */
+class PanServiceBinder extends IBluetoothPan.Stub implements IProfileServiceBinder {
+ private static final String TAG = PanServiceBinder.class.getSimpleName();
+
+ private PanService mService;
+
+ PanServiceBinder(PanService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private PanService getService(AttributionSource source) {
+ if (Utils.isInstrumentationTestMode()) {
+ return mService;
+ }
+ if (!Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
+ return null;
+ }
+ return mService;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device, AttributionSource source) {
+ PanService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ PanService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ PanService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ PanService service = getService(source);
+ if (service == null) {
+ return BluetoothPan.STATE_DISCONNECTED;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean isTetheringOn(AttributionSource source) {
+ // TODO(BT) have a variable marking the on/off state
+ PanService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.isTetheringOn();
+ }
+
+ @Override
+ public void setBluetoothTethering(
+ IBluetoothPanCallback callback, int id, boolean value, AttributionSource source) {
+ PanService service = getService(source);
+ if (service == null) {
+ return;
+ }
+
+ Log.d(
+ TAG,
+ "setBluetoothTethering:"
+ + (" value=" + value)
+ + (" pkgName= " + source.getPackageName())
+ + (" mTetherOn= " + service.isTetheringOn()));
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.enforceCallingOrSelfPermission(TETHER_PRIVILEGED, null);
+
+ service.setBluetoothTethering(callback, id, source.getUid(), value);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ PanService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ PanService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java
index 7223516e88..18fff92890 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java
index 55419908eb..2e788d424a 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java
index 6fec2895e6..391d759065 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java
index 88f7023b03..c5bb01f426 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
index 00194f5d78..2d67fc9947 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -17,7 +17,6 @@
package com.android.bluetooth.pbap;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothDevice.ACCESS_ALLOWED;
import static android.bluetooth.BluetoothDevice.ACCESS_REJECTED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
@@ -28,19 +27,15 @@ import static com.android.bluetooth.Utils.joinUninterruptibly;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUtils;
-import android.bluetooth.IBluetoothPbap;
-import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -75,7 +70,6 @@ import com.android.bluetooth.util.DevicePolicyUtils;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -734,7 +728,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect
@Override
protected IProfileServiceBinder initBinder() {
- return new PbapBinder(this);
+ return new BluetoothPbapServiceBinder(this);
}
@Override
@@ -775,97 +769,6 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect
sBluetoothPbapService = instance;
}
- @VisibleForTesting
- static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder {
- private BluetoothPbapService mService;
-
- PbapBinder(BluetoothPbapService service) {
- Log.v(TAG, "PbapBinder()");
- mService = service;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private BluetoothPbapService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- BluetoothPbapService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- return service;
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- Log.d(TAG, "getConnectedDevices");
- BluetoothPbapService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- Log.d(TAG, "getDevicesMatchingConnectionStates");
- BluetoothPbapService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- Log.d(TAG, "getConnectionState: " + device);
- BluetoothPbapService service = getService(source);
- if (service == null) {
- return BluetoothAdapter.STATE_DISCONNECTED;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- Log.d(TAG, "setConnectionPolicy for device=" + device + " policy=" + connectionPolicy);
- BluetoothPbapService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public void disconnect(BluetoothDevice device, AttributionSource source) {
- Log.d(TAG, "disconnect");
- BluetoothPbapService service = getService(source);
- if (service == null) {
- return;
- }
- service.disconnect(device);
- }
- }
-
@Override
public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) {
if (remoteDevice == null || socket == null) {
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinder.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinder.java
new file mode 100644
index 0000000000..67ad07169a
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinder.java
@@ -0,0 +1,121 @@
+/*
+ * 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.pbap;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothPbap;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+class BluetoothPbapServiceBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder {
+ private static final String TAG = BluetoothPbapServiceBinder.class.getSimpleName();
+
+ private BluetoothPbapService mService;
+
+ BluetoothPbapServiceBinder(BluetoothPbapService service) {
+ mService = service;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private BluetoothPbapService getService(AttributionSource source) {
+ BluetoothPbapService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ return service;
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ Log.d(TAG, "getConnectedDevices");
+ BluetoothPbapService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ Log.d(TAG, "getDevicesMatchingConnectionStates");
+ BluetoothPbapService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ Log.d(TAG, "getConnectionState: " + device);
+ BluetoothPbapService service = getService(source);
+ if (service == null) {
+ return BluetoothAdapter.STATE_DISCONNECTED;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ Log.d(TAG, "setConnectionPolicy for device=" + device + " policy=" + connectionPolicy);
+ BluetoothPbapService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public void disconnect(BluetoothDevice device, AttributionSource source) {
+ Log.d(TAG, "disconnect");
+ BluetoothPbapService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.disconnect(device);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java
index db427991a2..c63ec28105 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java
index e70706f84d..75a1b3d9f8 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
index 5fa66a785c..c3b08a2e89 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
index c83e856a72..9b75de98f0 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
@@ -173,7 +173,7 @@ public class PbapClientService extends ProfileService {
@Override
public IProfileServiceBinder initBinder() {
- return new PbapClientBinder(this);
+ return new PbapClientServiceBinder(this);
}
@Override
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientBinder.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientServiceBinder.java
index f3bcd1e7b5..5a45896f02 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientBinder.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientServiceBinder.java
@@ -35,12 +35,12 @@ import java.util.Collections;
import java.util.List;
/** Handler for incoming service calls destined for PBAP Client */
-class PbapClientBinder extends IBluetoothPbapClient.Stub implements IProfileServiceBinder {
- private static final String TAG = PbapClientBinder.class.getSimpleName();
+class PbapClientServiceBinder extends IBluetoothPbapClient.Stub implements IProfileServiceBinder {
+ private static final String TAG = PbapClientServiceBinder.class.getSimpleName();
private PbapClientService mService;
- PbapClientBinder(PbapClientService service) {
+ PbapClientServiceBinder(PbapClientService service) {
mService = service;
}
diff --git a/android/app/src/com/android/bluetooth/sap/SapService.java b/android/app/src/com/android/bluetooth/sap/SapService.java
index c8bda8e046..54391d24e3 100644
--- a/android/app/src/com/android/bluetooth/sap/SapService.java
+++ b/android/app/src/com/android/bluetooth/sap/SapService.java
@@ -20,7 +20,6 @@ import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
@@ -37,8 +36,6 @@ import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothSap;
-import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -61,7 +58,6 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
public class SapService extends ProfileService implements AdapterService.BluetoothStateCallback {
@@ -672,7 +668,7 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
@Override
protected IProfileServiceBinder initBinder() {
- return new SapBinder(this);
+ return new SapServiceBinder(this);
}
@Override
@@ -881,145 +877,4 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
}
}
-
- // Binder object: Must be static class or memory leak may occur
-
- /**
- * This class implements the IBluetoothSap interface - or actually it validates the
- * preconditions for calling the actual functionality in the SapService, and calls it.
- */
- private static class SapBinder extends IBluetoothSap.Stub implements IProfileServiceBinder {
- private SapService mService;
-
- SapBinder(SapService service) {
- Log.v(TAG, "SapBinder()");
- mService = service;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private SapService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- SapService service = mService;
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- return service;
- }
-
- @Override
- public int getState(AttributionSource source) {
- Log.v(TAG, "getState()");
-
- SapService service = getService(source);
- if (service == null) {
- return BluetoothSap.STATE_DISCONNECTED;
- }
-
- return service.getState();
- }
-
- @Override
- public BluetoothDevice getClient(AttributionSource source) {
- Log.v(TAG, "getClient()");
-
- SapService service = getService(source);
- if (service == null) {
- return null;
- }
-
- Log.v(TAG, "getClient() - returning " + service.getRemoteDevice());
- return service.getRemoteDevice();
- }
-
- @Override
- public boolean isConnected(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "isConnected()");
-
- SapService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.getConnectionState(device) == STATE_CONNECTED;
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "disconnect()");
-
- SapService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.disconnect(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- Log.v(TAG, "getConnectedDevices()");
-
- SapService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- Log.v(TAG, "getDevicesMatchingConnectionStates()");
-
- SapService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- Log.v(TAG, "getConnectionState()");
-
- SapService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- SapService service = getService(source);
- if (service == null) {
- return false;
- }
-
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- SapService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- return service.getConnectionPolicy(device);
- }
- }
}
diff --git a/android/app/src/com/android/bluetooth/sap/SapServiceBinder.java b/android/app/src/com/android/bluetooth/sap/SapServiceBinder.java
new file mode 100644
index 0000000000..4c8a96901a
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/sap/SapServiceBinder.java
@@ -0,0 +1,166 @@
+/*
+ * 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.sap;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSap;
+import android.bluetooth.IBluetoothSap;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class implements the IBluetoothSap interface - or actually it validates the preconditions
+ * for calling the actual functionality in the SapService, and calls it.
+ */
+class SapServiceBinder extends IBluetoothSap.Stub implements IProfileServiceBinder {
+ private static final String TAG = SapServiceBinder.class.getSimpleName();
+
+ private SapService mService;
+
+ SapServiceBinder(SapService service) {
+ mService = service;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private SapService getService(AttributionSource source) {
+ SapService service = mService;
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ return service;
+ }
+
+ @Override
+ public int getState(AttributionSource source) {
+ Log.v(TAG, "getState()");
+
+ SapService service = getService(source);
+ if (service == null) {
+ return BluetoothSap.STATE_DISCONNECTED;
+ }
+ return service.getState();
+ }
+
+ @Override
+ public BluetoothDevice getClient(AttributionSource source) {
+ Log.v(TAG, "getClient()");
+
+ SapService service = getService(source);
+ if (service == null) {
+ return null;
+ }
+
+ Log.v(TAG, "getClient() - returning " + service.getRemoteDevice());
+ return service.getRemoteDevice();
+ }
+
+ @Override
+ public boolean isConnected(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "isConnected()");
+
+ SapService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.getConnectionState(device) == STATE_CONNECTED;
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "disconnect()");
+
+ SapService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ Log.v(TAG, "getConnectedDevices()");
+
+ SapService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ Log.v(TAG, "getDevicesMatchingConnectionStates()");
+
+ SapService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ Log.v(TAG, "getConnectionState()");
+
+ SapService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ SapService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ SapService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+ return service.getConnectionPolicy(device);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsService.java b/android/app/src/com/android/bluetooth/tbs/TbsService.java
index ebd25f8229..3bb2fa45e7 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsService.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsService.java
@@ -17,19 +17,13 @@
package com.android.bluetooth.tbs;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static java.util.Objects.requireNonNull;
-import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeCall;
-import android.bluetooth.IBluetoothLeCallControl;
import android.bluetooth.IBluetoothLeCallControlCallback;
-import android.content.AttributionSource;
-import android.os.ParcelUuid;
import android.os.RemoteException;
import android.sysprop.BluetoothProperties;
import android.util.Log;
@@ -38,7 +32,6 @@ import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.le_audio.LeAudioService;
-import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.List;
@@ -68,7 +61,7 @@ public class TbsService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new TbsServerBinder(this);
+ return new TbsServiceBinder(this);
}
@Override
@@ -206,105 +199,6 @@ public class TbsService extends ProfileService {
mTbsGeneric.clearInbandRingtoneSupport(device);
}
- /** Binder object: must be a static class or memory leak may occur */
- @VisibleForTesting
- static class TbsServerBinder extends IBluetoothLeCallControl.Stub
- implements IProfileServiceBinder {
- private TbsService mService;
-
- TbsServerBinder(TbsService service) {
- mService = service;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
- private TbsService getService(AttributionSource source) {
- // Cache mService because it can change while getService is called
- TbsService service = mService;
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service;
- }
-
- @Override
- public void registerBearer(
- String token,
- IBluetoothLeCallControlCallback callback,
- String uci,
- List<String> uriSchemes,
- int capabilities,
- String providerName,
- int technology,
- AttributionSource source) {
- TbsService service = getService(source);
- if (service != null) {
- service.registerBearer(
- token, callback, uci, uriSchemes, capabilities, providerName, technology);
- }
- }
-
- @Override
- public void unregisterBearer(String token, AttributionSource source) {
- TbsService service = getService(source);
- if (service != null) {
- service.unregisterBearer(token);
- }
- }
-
- @Override
- public void requestResult(int ccid, int requestId, int result, AttributionSource source) {
- TbsService service = getService(source);
- if (service != null) {
- service.requestResult(ccid, requestId, result);
- }
- }
-
- @Override
- public void callAdded(int ccid, BluetoothLeCall call, AttributionSource source) {
- TbsService service = getService(source);
- if (service != null) {
- service.callAdded(ccid, call);
- }
- }
-
- @Override
- public void callRemoved(int ccid, ParcelUuid callId, int reason, AttributionSource source) {
- TbsService service = getService(source);
- if (service != null) {
- service.callRemoved(ccid, callId.getUuid(), reason);
- }
- }
-
- @Override
- public void callStateChanged(
- int ccid, ParcelUuid callId, int state, AttributionSource source) {
- TbsService service = getService(source);
- if (service != null) {
- service.callStateChanged(ccid, callId.getUuid(), state);
- }
- }
-
- @Override
- public void currentCallsList(
- int ccid, List<BluetoothLeCall> calls, AttributionSource source) {
- TbsService service = getService(source);
- if (service != null) {
- service.currentCallsList(ccid, calls);
- }
- }
- }
-
- @VisibleForTesting
void registerBearer(
String token,
IBluetoothLeCallControlCallback callback,
@@ -335,42 +229,36 @@ public class TbsService extends ProfileService {
Log.d(TAG, "registerBearer: token=" + token + " success=" + success);
}
- @VisibleForTesting
void unregisterBearer(String token) {
Log.d(TAG, "unregisterBearer: token=" + token);
mTbsGeneric.removeBearer(token);
}
- @VisibleForTesting
public void requestResult(int ccid, int requestId, int result) {
Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result=" + result);
mTbsGeneric.requestResult(ccid, requestId, result);
}
- @VisibleForTesting
void callAdded(int ccid, BluetoothLeCall call) {
Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call);
mTbsGeneric.callAdded(ccid, call);
}
- @VisibleForTesting
void callRemoved(int ccid, UUID callId, int reason) {
Log.d(TAG, "callRemoved: ccid=" + ccid + " callId=" + callId + " reason=" + reason);
mTbsGeneric.callRemoved(ccid, callId, reason);
}
- @VisibleForTesting
void callStateChanged(int ccid, UUID callId, int state) {
Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state);
mTbsGeneric.callStateChanged(ccid, callId, state);
}
- @VisibleForTesting
void currentCallsList(int ccid, List<BluetoothLeCall> calls) {
Log.d(TAG, "currentCallsList: ccid=" + ccid + " calls=" + calls);
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsServiceBinder.java b/android/app/src/com/android/bluetooth/tbs/TbsServiceBinder.java
new file mode 100644
index 0000000000..88a1a3435c
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/tbs/TbsServiceBinder.java
@@ -0,0 +1,126 @@
+/*
+ * 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.tbs;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothLeCall;
+import android.bluetooth.IBluetoothLeCallControl;
+import android.bluetooth.IBluetoothLeCallControlCallback;
+import android.content.AttributionSource;
+import android.os.ParcelUuid;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.List;
+
+class TbsServiceBinder extends IBluetoothLeCallControl.Stub implements IProfileServiceBinder {
+ private static final String TAG = TbsServiceBinder.class.getSimpleName();
+
+ private TbsService mService;
+
+ TbsServiceBinder(TbsService service) {
+ mService = service;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+ private TbsService getService(AttributionSource source) {
+ TbsService service = mService;
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service;
+ }
+
+ @Override
+ public void registerBearer(
+ String token,
+ IBluetoothLeCallControlCallback callback,
+ String uci,
+ List<String> uriSchemes,
+ int capabilities,
+ String providerName,
+ int technology,
+ AttributionSource source) {
+ TbsService service = getService(source);
+ if (service != null) {
+ service.registerBearer(
+ token, callback, uci, uriSchemes, capabilities, providerName, technology);
+ }
+ }
+
+ @Override
+ public void unregisterBearer(String token, AttributionSource source) {
+ TbsService service = getService(source);
+ if (service != null) {
+ service.unregisterBearer(token);
+ }
+ }
+
+ @Override
+ public void requestResult(int ccid, int requestId, int result, AttributionSource source) {
+ TbsService service = getService(source);
+ if (service != null) {
+ service.requestResult(ccid, requestId, result);
+ }
+ }
+
+ @Override
+ public void callAdded(int ccid, BluetoothLeCall call, AttributionSource source) {
+ TbsService service = getService(source);
+ if (service != null) {
+ service.callAdded(ccid, call);
+ }
+ }
+
+ @Override
+ public void callRemoved(int ccid, ParcelUuid callId, int reason, AttributionSource source) {
+ TbsService service = getService(source);
+ if (service != null) {
+ service.callRemoved(ccid, callId.getUuid(), reason);
+ }
+ }
+
+ @Override
+ public void callStateChanged(int ccid, ParcelUuid callId, int state, AttributionSource source) {
+ TbsService service = getService(source);
+ if (service != null) {
+ service.callStateChanged(ccid, callId.getUuid(), state);
+ }
+ }
+
+ @Override
+ public void currentCallsList(int ccid, List<BluetoothLeCall> calls, AttributionSource source) {
+ TbsService service = getService(source);
+ if (service != null) {
+ service.currentCallsList(ccid, calls);
+ }
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
index 628a36b6e8..b21d4e19b3 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
@@ -17,8 +17,6 @@
package com.android.bluetooth.vc;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothDevice.BOND_BONDED;
import static android.bluetooth.BluetoothDevice.BOND_NONE;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
@@ -36,19 +34,14 @@ import static com.android.bluetooth.flags.Flags.vcpDeviceVolumeApiImprovements;
import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElseGet;
-import android.annotation.RequiresPermission;
import android.bluetooth.AudioInputControl.AudioInputStatus;
import android.bluetooth.AudioInputControl.AudioInputType;
import android.bluetooth.AudioInputControl.GainMode;
import android.bluetooth.AudioInputControl.Mute;
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IAudioInputCallback;
-import android.bluetooth.IBluetoothVolumeControl;
import android.bluetooth.IBluetoothVolumeControlCallback;
-import android.content.AttributionSource;
import android.media.AudioManager;
import android.os.Handler;
import android.os.HandlerThread;
@@ -70,22 +63,13 @@ import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import libcore.util.SneakyThrow;
-
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Function;
public class VolumeControlService extends ProfileService {
private static final String TAG = VolumeControlService.class.getSimpleName();
@@ -166,7 +150,7 @@ public class VolumeControlService extends ProfileService {
@Override
protected IProfileServiceBinder initBinder() {
- return new BluetoothVolumeControlBinder(this);
+ return new VolumeControlServiceBinder(this);
}
@Override
@@ -209,6 +193,14 @@ public class VolumeControlService extends ProfileService {
}
}
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ Map<BluetoothDevice, VolumeControlInputDescriptor> getAudioInputs() {
+ return mAudioInputs;
+ }
+
/**
* Get the VolumeControlService instance
*
@@ -534,7 +526,6 @@ public class VolumeControlService extends ProfileService {
mNativeInterface.setExtAudioOutVolumeOffset(device, instanceId, volumeOffset);
}
- @VisibleForTesting
synchronized void setDeviceVolume(BluetoothDevice device, int volume, boolean isGroupOp) {
Log.d(
TAG,
@@ -1685,501 +1676,6 @@ public class VolumeControlService extends ProfileService {
BluetoothProfile.VOLUME_CONTROL, device, fromState, toState);
}
- /** Binder object: must be a static class or memory leak may occur */
- @VisibleForTesting
- static class BluetoothVolumeControlBinder extends IBluetoothVolumeControl.Stub
- implements IProfileServiceBinder {
- @VisibleForTesting boolean mIsTesting = false;
- private VolumeControlService mService;
-
- BluetoothVolumeControlBinder(VolumeControlService svc) {
- mService = svc;
- }
-
- @Override
- public void cleanup() {
- mService = null;
- }
-
- @RequiresPermission(BLUETOOTH_CONNECT)
- private VolumeControlService getService(AttributionSource source) {
- requireNonNull(source);
-
- // Cache mService because it can change while getService is called
- VolumeControlService service = mService;
-
- if (Utils.isInstrumentationTestMode()) {
- return service;
- }
-
- if (!Utils.checkServiceAvailable(service, TAG)
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
- || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
- return null;
- }
-
- return service;
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
- VolumeControlService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getConnectedDevices();
- }
-
- @Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(
- int[] states, AttributionSource source) {
- VolumeControlService service = getService(source);
- if (service == null) {
- return Collections.emptyList();
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- return service.getDevicesMatchingConnectionStates(states);
- }
-
- @Override
- public int getConnectionState(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return STATE_DISCONNECTED;
- }
-
- return service.getConnectionState(device);
- }
-
- @Override
- public boolean setConnectionPolicy(
- BluetoothDevice device, int connectionPolicy, AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.setConnectionPolicy(device, connectionPolicy);
- }
-
- @Override
- public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return CONNECTION_POLICY_UNKNOWN;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getConnectionPolicy(device);
- }
-
- @Override
- public boolean isVolumeOffsetAvailable(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return false;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.isVolumeOffsetAvailable(device);
- }
-
- @Override
- public int getNumberOfVolumeOffsetInstances(
- BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return 0;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getNumberOfVolumeOffsetInstances(device);
- }
-
- @Override
- public void setVolumeOffset(
- BluetoothDevice device,
- int instanceId,
- int volumeOffset,
- AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setVolumeOffset(device, instanceId, volumeOffset);
- }
-
- @Override
- public void setDeviceVolume(
- BluetoothDevice device, int volume, boolean isGroupOp, AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.setDeviceVolume(device, volume, isGroupOp);
- }
-
- @Override
- public void setGroupVolume(int groupId, int volume, AttributionSource source) {
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.setGroupVolume(groupId, volume);
- }
-
- @Override
- public int getGroupVolume(int groupId, AttributionSource source) {
- VolumeControlService service = getService(source);
- if (service == null) {
- return 0;
- }
-
- return service.getGroupVolume(groupId);
- }
-
- @Override
- public void setGroupActive(int groupId, boolean active, AttributionSource source) {
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.setGroupActive(groupId, active);
- }
-
- @Override
- public void mute(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.mute(device);
- }
-
- @Override
- public void muteGroup(int groupId, AttributionSource source) {
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.muteGroup(groupId);
- }
-
- @Override
- public void unmute(BluetoothDevice device, AttributionSource source) {
- requireNonNull(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.unmute(device);
- }
-
- @Override
- public void unmuteGroup(int groupId, AttributionSource source) {
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.unmuteGroup(groupId);
- }
-
- private static void postAndWait(Handler handler, Runnable runnable) {
- FutureTask<Void> task = new FutureTask(Executors.callable(runnable));
-
- handler.post(task);
- try {
- task.get(1, TimeUnit.SECONDS);
- } catch (TimeoutException | InterruptedException e) {
- SneakyThrow.sneakyThrow(e);
- } catch (ExecutionException e) {
- SneakyThrow.sneakyThrow(e.getCause());
- }
- }
-
- @Override
- public void registerCallback(
- IBluetoothVolumeControlCallback callback, AttributionSource source) {
- requireNonNull(callback);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- postAndWait(service.mHandler, () -> service.registerCallback(callback));
- }
-
- @Override
- public void unregisterCallback(
- IBluetoothVolumeControlCallback callback, AttributionSource source) {
- requireNonNull(callback);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- postAndWait(service.mHandler, () -> service.unregisterCallback(callback));
- }
-
- @Override
- public void notifyNewRegisteredCallback(
- IBluetoothVolumeControlCallback callback, AttributionSource source) {
- requireNonNull(callback);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- postAndWait(service.mHandler, () -> service.notifyNewRegisteredCallback(callback));
- }
-
- private static void validateBluetoothDevice(BluetoothDevice device) {
- requireNonNull(device);
- String address = device.getAddress();
- if (!BluetoothAdapter.checkBluetoothAddress(address)) {
- throw new IllegalArgumentException("Invalid device address: " + address);
- }
- }
-
- @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
- private <R> R aicsWrapper(
- AttributionSource source,
- BluetoothDevice device,
- Function<VolumeControlInputDescriptor, R> fn,
- R defaultValue) {
- validateBluetoothDevice(device);
-
- VolumeControlService service = getService(source);
- if (service == null) {
- return defaultValue;
- }
-
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
- VolumeControlInputDescriptor inputs = service.mAudioInputs.get(device);
- if (inputs == null) {
- Log.w(TAG, "No audio inputs for " + device);
- return defaultValue;
- }
-
- return fn.apply(inputs);
- }
-
- @Override
- public int getNumberOfAudioInputControlServices(
- AttributionSource source, BluetoothDevice device) {
- validateBluetoothDevice(device);
- Log.d(TAG, "getNumberOfAudioInputControlServices(" + device + ")");
- return aicsWrapper(source, device, i -> i.size(), 0);
- }
-
- @Override
- public void registerAudioInputControlCallback(
- AttributionSource source,
- BluetoothDevice device,
- int instanceId,
- IAudioInputCallback callback) {
- requireNonNull(callback);
- Log.d(
- TAG,
- "registerAudioInputControlCallback("
- + (device + ", " + instanceId + ", " + callback)
- + ")");
- aicsWrapper(
- source,
- device,
- i -> {
- i.registerCallback(instanceId, callback);
- return null;
- },
- null);
- }
-
- @Override
- public void unregisterAudioInputControlCallback(
- AttributionSource source,
- BluetoothDevice device,
- int instanceId,
- IAudioInputCallback callback) {
- requireNonNull(callback);
- Log.d(
- TAG,
- "unregisterAudioInputControlCallback("
- + (device + ", " + instanceId + ", " + callback)
- + ")");
- aicsWrapper(
- source,
- device,
- i -> {
- i.unregisterCallback(instanceId, callback);
- return null;
- },
- null);
- }
-
- @Override
- public int getAudioInputGainSettingUnit(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputGainSettingUnit(" + device + ", " + instanceId + ")");
- return aicsWrapper(source, device, i -> i.getGainSettingUnit(instanceId), 0);
- }
-
- @Override
- public int getAudioInputGainSettingMin(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputGainSettingMin(" + device + ", " + instanceId + ")");
- return aicsWrapper(source, device, i -> i.getGainSettingMin(instanceId), 0);
- }
-
- @Override
- public int getAudioInputGainSettingMax(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputGainSettingMax(" + device + ", " + instanceId + ")");
- return aicsWrapper(source, device, i -> i.getGainSettingMax(instanceId), 0);
- }
-
- @Override
- public String getAudioInputDescription(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputDescription(" + device + ", " + instanceId + ")");
- return aicsWrapper(source, device, i -> i.getDescription(instanceId), "");
- }
-
- @Override
- public boolean isAudioInputDescriptionWritable(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "isAudioInputDescriptionWritable(" + device + ", " + instanceId + ")");
- return aicsWrapper(source, device, i -> i.isDescriptionWritable(instanceId), false);
- }
-
- @Override
- public boolean setAudioInputDescription(
- AttributionSource source,
- BluetoothDevice device,
- int instanceId,
- String description) {
- requireNonNull(description);
- Log.d(TAG, "setAudioInputDescription(" + device + ", " + instanceId + ")");
- return aicsWrapper(
- source, device, i -> i.setDescription(instanceId, description), false);
- }
-
- @Override
- public @AudioInputStatus int getAudioInputStatus(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputStatus(" + device + ", " + instanceId + ")");
- return aicsWrapper(
- source,
- device,
- i -> i.getStatus(instanceId),
- (int) bluetooth.constants.aics.AudioInputStatus.INACTIVE);
- }
-
- @Override
- public @AudioInputType int getAudioInputType(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputType(" + device + ", " + instanceId + ")");
- return aicsWrapper(
- source,
- device,
- i -> i.getType(instanceId),
- bluetooth.constants.AudioInputType.UNSPECIFIED);
- }
-
- @Override
- public int getAudioInputGainSetting(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputGainSetting(" + device + ", " + instanceId + ")");
- return aicsWrapper(source, device, i -> i.getGainSetting(instanceId), 0);
- }
-
- @Override
- public boolean setAudioInputGainSetting(
- AttributionSource source, BluetoothDevice device, int instanceId, int gainSetting) {
- Log.d(TAG, "setAudioInputGainSetting(" + device + ", " + instanceId + ")");
- return aicsWrapper(
- source, device, i -> i.setGainSetting(instanceId, gainSetting), false);
- }
-
- @Override
- public @GainMode int getAudioInputGainMode(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputGainMode(" + device + ", " + instanceId + ")");
- return aicsWrapper(
- source,
- device,
- i -> i.getGainMode(instanceId),
- (int) bluetooth.constants.aics.GainMode.AUTOMATIC_ONLY);
- }
-
- @Override
- public boolean setAudioInputGainMode(
- AttributionSource source,
- BluetoothDevice device,
- int instanceId,
- @GainMode int gainMode) {
- Log.d(TAG, "setAudioInputGainMode(" + device + ", " + instanceId + ")");
- return aicsWrapper(source, device, i -> i.setGainMode(instanceId, gainMode), false);
- }
-
- @Override
- public @Mute int getAudioInputMute(
- AttributionSource source, BluetoothDevice device, int instanceId) {
- Log.d(TAG, "getAudioInputMute(" + device + ", " + instanceId + ")");
- return aicsWrapper(
- source,
- device,
- i -> i.getMute(instanceId),
- (int) bluetooth.constants.aics.Mute.DISABLED);
- }
-
- @Override
- public boolean setAudioInputMute(
- AttributionSource source, BluetoothDevice device, int instanceId, @Mute int mute) {
- Log.d(TAG, "setAudioInputMute(" + device + ", " + instanceId + ")");
- return aicsWrapper(source, device, i -> i.setMute(instanceId, mute), false);
- }
- }
-
@Override
public void dump(StringBuilder sb) {
super.dump(sb);
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlServiceBinder.java b/android/app/src/com/android/bluetooth/vc/VolumeControlServiceBinder.java
new file mode 100644
index 0000000000..7438ab14cd
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlServiceBinder.java
@@ -0,0 +1,528 @@
+/*
+ * 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.vc;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.AudioInputControl.AudioInputStatus;
+import android.bluetooth.AudioInputControl.AudioInputType;
+import android.bluetooth.AudioInputControl.GainMode;
+import android.bluetooth.AudioInputControl.Mute;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IAudioInputCallback;
+import android.bluetooth.IBluetoothVolumeControl;
+import android.bluetooth.IBluetoothVolumeControlCallback;
+import android.content.AttributionSource;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.util.SneakyThrow;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+
+class VolumeControlServiceBinder extends IBluetoothVolumeControl.Stub
+ implements IProfileServiceBinder {
+ private static final String TAG = VolumeControlServiceBinder.class.getSimpleName();
+
+ @VisibleForTesting boolean mIsTesting = false;
+ private VolumeControlService mService;
+
+ VolumeControlServiceBinder(VolumeControlService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ private VolumeControlService getService(AttributionSource source) {
+ requireNonNull(source);
+
+ VolumeControlService service = mService;
+
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
+
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+ return null;
+ }
+
+ return service;
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectedDevices();
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ int[] states, AttributionSource source) {
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return Collections.emptyList();
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ @Override
+ public boolean setConnectionPolicy(
+ BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return CONNECTION_POLICY_UNKNOWN;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getConnectionPolicy(device);
+ }
+
+ @Override
+ public boolean isVolumeOffsetAvailable(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return false;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.isVolumeOffsetAvailable(device);
+ }
+
+ @Override
+ public int getNumberOfVolumeOffsetInstances(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return 0;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return service.getNumberOfVolumeOffsetInstances(device);
+ }
+
+ @Override
+ public void setVolumeOffset(
+ BluetoothDevice device, int instanceId, int volumeOffset, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setVolumeOffset(device, instanceId, volumeOffset);
+ }
+
+ @Override
+ public void setDeviceVolume(
+ BluetoothDevice device, int volume, boolean isGroupOp, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ service.setDeviceVolume(device, volume, isGroupOp);
+ }
+
+ @Override
+ public void setGroupVolume(int groupId, int volume, AttributionSource source) {
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.setGroupVolume(groupId, volume);
+ }
+
+ @Override
+ public int getGroupVolume(int groupId, AttributionSource source) {
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return 0;
+ }
+ return service.getGroupVolume(groupId);
+ }
+
+ @Override
+ public void setGroupActive(int groupId, boolean active, AttributionSource source) {
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.setGroupActive(groupId, active);
+ }
+
+ @Override
+ public void mute(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.mute(device);
+ }
+
+ @Override
+ public void muteGroup(int groupId, AttributionSource source) {
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.muteGroup(groupId);
+ }
+
+ @Override
+ public void unmute(BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.unmute(device);
+ }
+
+ @Override
+ public void unmuteGroup(int groupId, AttributionSource source) {
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+ service.unmuteGroup(groupId);
+ }
+
+ private static void postAndWait(Handler handler, Runnable runnable) {
+ FutureTask<Void> task = new FutureTask(Executors.callable(runnable));
+
+ handler.post(task);
+ try {
+ task.get(1, TimeUnit.SECONDS);
+ } catch (TimeoutException | InterruptedException e) {
+ SneakyThrow.sneakyThrow(e);
+ } catch (ExecutionException e) {
+ SneakyThrow.sneakyThrow(e.getCause());
+ }
+ }
+
+ @Override
+ public void registerCallback(
+ IBluetoothVolumeControlCallback callback, AttributionSource source) {
+ requireNonNull(callback);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ postAndWait(service.getHandler(), () -> service.registerCallback(callback));
+ }
+
+ @Override
+ public void unregisterCallback(
+ IBluetoothVolumeControlCallback callback, AttributionSource source) {
+ requireNonNull(callback);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ postAndWait(service.getHandler(), () -> service.unregisterCallback(callback));
+ }
+
+ @Override
+ public void notifyNewRegisteredCallback(
+ IBluetoothVolumeControlCallback callback, AttributionSource source) {
+ requireNonNull(callback);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ postAndWait(service.getHandler(), () -> service.notifyNewRegisteredCallback(callback));
+ }
+
+ private static void validateBluetoothDevice(BluetoothDevice device) {
+ requireNonNull(device);
+ String address = device.getAddress();
+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+ throw new IllegalArgumentException("Invalid device address: " + address);
+ }
+ }
+
+ @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+ private <R> R aicsWrapper(
+ AttributionSource source,
+ BluetoothDevice device,
+ Function<VolumeControlInputDescriptor, R> fn,
+ R defaultValue) {
+ validateBluetoothDevice(device);
+
+ VolumeControlService service = getService(source);
+ if (service == null) {
+ return defaultValue;
+ }
+
+ service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+ VolumeControlInputDescriptor inputs = service.getAudioInputs().get(device);
+ if (inputs == null) {
+ Log.w(TAG, "No audio inputs for " + device);
+ return defaultValue;
+ }
+
+ return fn.apply(inputs);
+ }
+
+ @Override
+ public int getNumberOfAudioInputControlServices(
+ AttributionSource source, BluetoothDevice device) {
+ validateBluetoothDevice(device);
+ Log.d(TAG, "getNumberOfAudioInputControlServices(" + device + ")");
+ return aicsWrapper(source, device, i -> i.size(), 0);
+ }
+
+ @Override
+ public void registerAudioInputControlCallback(
+ AttributionSource source,
+ BluetoothDevice device,
+ int instanceId,
+ IAudioInputCallback callback) {
+ requireNonNull(callback);
+ Log.d(
+ TAG,
+ "registerAudioInputControlCallback("
+ + (device + ", " + instanceId + ", " + callback)
+ + ")");
+ aicsWrapper(
+ source,
+ device,
+ i -> {
+ i.registerCallback(instanceId, callback);
+ return null;
+ },
+ null);
+ }
+
+ @Override
+ public void unregisterAudioInputControlCallback(
+ AttributionSource source,
+ BluetoothDevice device,
+ int instanceId,
+ IAudioInputCallback callback) {
+ requireNonNull(callback);
+ Log.d(
+ TAG,
+ "unregisterAudioInputControlCallback("
+ + (device + ", " + instanceId + ", " + callback)
+ + ")");
+ aicsWrapper(
+ source,
+ device,
+ i -> {
+ i.unregisterCallback(instanceId, callback);
+ return null;
+ },
+ null);
+ }
+
+ @Override
+ public int getAudioInputGainSettingUnit(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputGainSettingUnit(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.getGainSettingUnit(instanceId), 0);
+ }
+
+ @Override
+ public int getAudioInputGainSettingMin(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputGainSettingMin(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.getGainSettingMin(instanceId), 0);
+ }
+
+ @Override
+ public int getAudioInputGainSettingMax(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputGainSettingMax(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.getGainSettingMax(instanceId), 0);
+ }
+
+ @Override
+ public String getAudioInputDescription(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputDescription(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.getDescription(instanceId), "");
+ }
+
+ @Override
+ public boolean isAudioInputDescriptionWritable(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "isAudioInputDescriptionWritable(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.isDescriptionWritable(instanceId), false);
+ }
+
+ @Override
+ public boolean setAudioInputDescription(
+ AttributionSource source, BluetoothDevice device, int instanceId, String description) {
+ requireNonNull(description);
+ Log.d(TAG, "setAudioInputDescription(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.setDescription(instanceId, description), false);
+ }
+
+ @Override
+ public @AudioInputStatus int getAudioInputStatus(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputStatus(" + device + ", " + instanceId + ")");
+ return aicsWrapper(
+ source,
+ device,
+ i -> i.getStatus(instanceId),
+ (int) bluetooth.constants.aics.AudioInputStatus.INACTIVE);
+ }
+
+ @Override
+ public @AudioInputType int getAudioInputType(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputType(" + device + ", " + instanceId + ")");
+ return aicsWrapper(
+ source,
+ device,
+ i -> i.getType(instanceId),
+ bluetooth.constants.AudioInputType.UNSPECIFIED);
+ }
+
+ @Override
+ public int getAudioInputGainSetting(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputGainSetting(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.getGainSetting(instanceId), 0);
+ }
+
+ @Override
+ public boolean setAudioInputGainSetting(
+ AttributionSource source, BluetoothDevice device, int instanceId, int gainSetting) {
+ Log.d(TAG, "setAudioInputGainSetting(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.setGainSetting(instanceId, gainSetting), false);
+ }
+
+ @Override
+ public @GainMode int getAudioInputGainMode(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputGainMode(" + device + ", " + instanceId + ")");
+ return aicsWrapper(
+ source,
+ device,
+ i -> i.getGainMode(instanceId),
+ (int) bluetooth.constants.aics.GainMode.AUTOMATIC_ONLY);
+ }
+
+ @Override
+ public boolean setAudioInputGainMode(
+ AttributionSource source,
+ BluetoothDevice device,
+ int instanceId,
+ @GainMode int gainMode) {
+ Log.d(TAG, "setAudioInputGainMode(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.setGainMode(instanceId, gainMode), false);
+ }
+
+ @Override
+ public @Mute int getAudioInputMute(
+ AttributionSource source, BluetoothDevice device, int instanceId) {
+ Log.d(TAG, "getAudioInputMute(" + device + ", " + instanceId + ")");
+ return aicsWrapper(
+ source,
+ device,
+ i -> i.getMute(instanceId),
+ (int) bluetooth.constants.aics.Mute.DISABLED);
+ }
+
+ @Override
+ public boolean setAudioInputMute(
+ AttributionSource source, BluetoothDevice device, int instanceId, @Mute int mute) {
+ Log.d(TAG, "setAudioInputMute(" + device + ", " + instanceId + ")");
+ return aicsWrapper(source, device, i -> i.setMute(instanceId, mute), false);
+ }
+}
diff --git a/android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java b/android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java
index fd9531105b..7bb7bbfbc3 100644
--- a/android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java
+++ b/android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java
@@ -46,6 +46,7 @@ import android.net.Uri;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
+import android.provider.Settings;
import android.sysprop.BluetoothProperties;
import androidx.lifecycle.Lifecycle;
@@ -382,6 +383,30 @@ public class BluetoothOppLauncherActivityTest {
assertThat(argument.getValue().getData()).isEqualTo(Uri.EMPTY);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SEND_OPP_DEVICE_PICKER_EXTRA_INTENT)
+ public void onCreate_withActionSend_grantUriPermissionToNearbyComponent() {
+ doReturn(true).when(mMethodProxy).bluetoothAdapterIsEnabled(any());
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .when(mMethodProxy)
+ .componentCallerCheckContentUriPermission(any(), any(), anyInt());
+ String uriString = "content://test.provider/1";
+ Settings.Secure.putString(
+ mTargetContext.getContentResolver(),
+ "nearby_sharing_component",
+ "com.example/.BComponent");
+
+ ActivityScenario<BluetoothOppLauncherActivity> unused =
+ ActivityScenario.launch(createSendIntent(uriString));
+
+ verify(mMethodProxy)
+ .grantUriPermission(
+ any(),
+ eq("com.example"),
+ eq(Uri.parse(uriString)),
+ eq(Intent.FLAG_GRANT_READ_URI_PERMISSION));
+ }
+
@Ignore("b/263724420")
@Test
public void launchDevicePicker_bluetoothNotEnabled_launchEnableActivity() throws Exception {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java
index 23e7a5ed1c..65d00773ba 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java
@@ -36,15 +36,20 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.platform.test.flag.junit.SetFlagsRule;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link A2dpServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class A2dpServiceBinderTest {
- private static final AttributionSource sSource = new AttributionSource.Builder(0).build();
- private static final BluetoothDevice sDevice = getTestDevice(0);
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -53,7 +58,10 @@ public class A2dpServiceBinderTest {
@Mock private A2dpService mA2dpService;
@Mock private PackageManager mPackageManager;
- private A2dpService.BluetoothA2dpBinder mBinder;
+ private final AttributionSource sSource = new AttributionSource.Builder(0).build();
+ private final BluetoothDevice sDevice = getTestDevice(0);
+
+ private A2dpServiceBinder mBinder;
@Before
public void setUp() throws Exception {
@@ -62,7 +70,7 @@ public class A2dpServiceBinderTest {
appInfo.targetSdkVersion = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
doReturn(appInfo).when(mPackageManager).getApplicationInfo(any(), anyInt());
- mBinder = new A2dpService.BluetoothA2dpBinder(mA2dpService);
+ mBinder = new A2dpServiceBinder(mA2dpService);
}
@After
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java
index aad4f1019a..6f7723caa9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java
@@ -27,21 +27,29 @@ import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothDevice;
import android.content.AttributionSource;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link A2dpSinkServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class A2dpSinkServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private A2dpSinkService mService;
- private A2dpSinkService.A2dpSinkServiceBinder mBinder;
+ private A2dpSinkServiceBinder mBinder;
@Before
public void setUp() throws Exception {
- mBinder = new A2dpSinkService.A2dpSinkServiceBinder(mService);
+ mBinder = new A2dpSinkServiceBinder(mService);
}
@After
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java
index 2cbd8f356a..efd371dfdd 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java
@@ -34,41 +34,41 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link AvrcpControllerServiceBinder} */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AvrcpControllerServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private AvrcpControllerService mService;
private final BluetoothDevice mDevice = getTestDevice(49);
- AvrcpControllerService.AvrcpControllerServiceBinder mBinder;
+ private AvrcpControllerServiceBinder mBinder;
@Before
public void setUp() throws Exception {
- mBinder = new AvrcpControllerService.AvrcpControllerServiceBinder(mService);
+ mBinder = new AvrcpControllerServiceBinder(mService);
}
@Test
public void getConnectedDevices_callsServiceMethod() {
mBinder.getConnectedDevices(null);
-
verify(mService).getConnectedDevices();
}
@Test
public void getDevicesMatchingConnectionStates_callsServiceMethod() {
int[] states = new int[] {STATE_CONNECTED};
- mBinder.getDevicesMatchingConnectionStates(states, null);
+ mBinder.getDevicesMatchingConnectionStates(states, null);
verify(mService).getDevicesMatchingConnectionStates(states);
}
@Test
public void getConnectionState_callsServiceMethod() {
mBinder.getConnectionState(mDevice, null);
-
verify(mService).getConnectionState(mDevice);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BleBroadcastAssistantBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceBinderTest.java
index ae402d7d0f..584f0cdeea 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BleBroadcastAssistantBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceBinderTest.java
@@ -36,21 +36,24 @@ import android.bluetooth.IBluetoothLeBroadcastAssistantCallback;
import android.bluetooth.le.ScanFilter;
import android.content.AttributionSource;
+import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.util.Collections;
import java.util.List;
-@RunWith(JUnit4.class)
-public class BleBroadcastAssistantBinderTest {
+/** Test cases for {@link BassClientServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BassClientServiceBinderTest {
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@@ -64,17 +67,16 @@ public class BleBroadcastAssistantBinderTest {
@Mock private BassClientService mService;
- private BassClientService.BluetoothLeBroadcastAssistantBinder mBinder;
+ private BassClientServiceBinder mBinder;
@Before
public void setUp() {
- mBinder = new BassClientService.BluetoothLeBroadcastAssistantBinder(mService);
+ mBinder = new BassClientServiceBinder(mService);
}
@Test
public void cleanUp() {
mBinder.cleanup();
- assertThat(mBinder.mService).isNull();
}
@Test
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 955f579ce4..7e143a2851 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
@@ -30,15 +30,21 @@ import android.bluetooth.IBluetoothOobDataCallback;
import android.content.AttributionSource;
import android.os.ParcelUuid;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.io.FileDescriptor;
-/** Test cases for {@link AdapterServiceBinder}. */
+/** Test cases for {@link AdapterServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class AdapterServiceBinderTest {
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java
index 1b057b3a7e..ae3db21382 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java
@@ -20,12 +20,24 @@ import static com.android.bluetooth.TestUtils.getTestDevice;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
import android.bluetooth.BluetoothDevice;
+import android.content.ContentResolver;
+import android.provider.Settings;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.BluetoothMetricsProto.BluetoothRemoteDeviceInformation;
+import com.android.bluetooth.BluetoothStatsLog;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
@@ -37,9 +49,12 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.util.HashMap;
import java.util.Map;
@@ -236,6 +251,51 @@ public class MetricsLoggerTest {
assertThat(mTestableMetricsLogger.logAllowlistedDeviceNameHash(1, "")).isEmpty();
}
+ @Test
+ public void testUpdateHearingDeviceActiveTime() {
+ BluetoothDevice bluetoothDevice = getTestDevice(0);
+ int day = BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__DAY;
+ int week = BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__WEEK;
+ int month = BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__MONTH;
+ doReturn(ApplicationProvider.getApplicationContext().getContentResolver())
+ .when(mAdapterService)
+ .getContentResolver();
+
+ // last active time is 2 days ago, should update last active day
+ TestableMetricsLogger logger = spy(mTestableMetricsLogger);
+ prepareLastActiveTimeDaysAgo(2);
+ logger.updateHearingDeviceActiveTime(bluetoothDevice, 1);
+ verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(day));
+ verify(logger, never()).logHearingDeviceActiveEvent(any(), anyInt(), eq(week));
+ verify(logger, never()).logHearingDeviceActiveEvent(any(), anyInt(), eq(month));
+
+ // last active time is 8 days ago, should update last active day and week
+ Mockito.reset(logger);
+ prepareLastActiveTimeDaysAgo(8);
+ logger.updateHearingDeviceActiveTime(bluetoothDevice, 1);
+ verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(day));
+ verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(week));
+ verify(logger, never()).logHearingDeviceActiveEvent(any(), anyInt(), eq(month));
+
+ // last active time is 60 days ago, should update last active day, week and month
+ Mockito.reset(logger);
+ prepareLastActiveTimeDaysAgo(60);
+ logger.updateHearingDeviceActiveTime(bluetoothDevice, 1);
+ verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(day));
+ verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(week));
+ verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(month));
+ }
+
+ private static void prepareLastActiveTimeDaysAgo(int days) {
+ final ContentResolver contentResolver =
+ ApplicationProvider.getApplicationContext().getContentResolver();
+ final LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault());
+ final String lastActive = now.minusDays(days).toString();
+ Settings.Secure.putString(contentResolver, "last_active_day", lastActive);
+ Settings.Secure.putString(contentResolver, "last_active_week", lastActive);
+ Settings.Secure.putString(contentResolver, "last_active_month", lastActive);
+ }
+
private void initTestingBloomfilter() throws IOException {
byte[] bloomfilterData =
DeviceBloomfilterGenerator.hexStringToByteArray(
diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/BluetoothCsisBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinderTest.java
index 9233602c47..81adae18f4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/csip/BluetoothCsisBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinderTest.java
@@ -30,12 +30,20 @@ import android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback;
import android.content.AttributionSource;
import android.os.ParcelUuid;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
-public class BluetoothCsisBinderTest {
+/** Test cases for {@link CsipSetCoordinatorServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CsipSetCoordinatorServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private CsipSetCoordinatorService mService;
@@ -43,11 +51,11 @@ public class BluetoothCsisBinderTest {
private final BluetoothDevice mDevice = getTestDevice(45);
private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build();
- private CsipSetCoordinatorService.BluetoothCsisBinder mBinder;
+ private CsipSetCoordinatorServiceBinder mBinder;
@Before
public void setUp() throws Exception {
- mBinder = new CsipSetCoordinatorService.BluetoothCsisBinder(mService);
+ mBinder = new CsipSetCoordinatorServiceBinder(mService);
}
@Test
@@ -59,6 +67,7 @@ public class BluetoothCsisBinderTest {
@Test
public void getDevicesMatchingConnectionStates() {
int[] states = new int[] {STATE_CONNECTED};
+
mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
verify(mService).getDevicesMatchingConnectionStates(states);
}
@@ -72,6 +81,7 @@ public class BluetoothCsisBinderTest {
@Test
public void setConnectionPolicy() {
int connectionPolicy = CONNECTION_POLICY_ALLOWED;
+
mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource);
verify(mService).setConnectionPolicy(mDevice, connectionPolicy);
}
@@ -87,6 +97,7 @@ public class BluetoothCsisBinderTest {
int groupId = 100;
IBluetoothCsipSetCoordinatorLockCallback cb =
mock(IBluetoothCsipSetCoordinatorLockCallback.class);
+
mBinder.lockGroup(groupId, cb, mAttributionSource);
verify(mService).lockGroup(groupId, cb);
}
@@ -94,6 +105,7 @@ public class BluetoothCsisBinderTest {
@Test
public void unlockGroup() {
ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+
mBinder.unlockGroup(uuid, mAttributionSource);
verify(mService).unlockGroup(uuid.getUuid());
}
@@ -101,6 +113,7 @@ public class BluetoothCsisBinderTest {
@Test
public void getAllGroupIds() {
ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+
mBinder.getAllGroupIds(uuid, mAttributionSource);
verify(mService).getAllGroupIds(uuid);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
index ac64b097ae..72fbd125b6 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
@@ -130,7 +130,6 @@ public class CsipSetCoordinatorServiceTest {
new CsipSetCoordinatorService(
mAdapterService, mLooper.getLooper(), mNativeInterface, mServiceFactory);
mService.setAvailable(true);
-
}
@After
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java
index 1a340840bc..f9bb1d556d 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java
@@ -42,6 +42,7 @@ import org.mockito.Mock;
import java.util.UUID;
+/** Test cases for {@link GattServiceBinder} */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class GattServiceBinderTest {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceBinderTest.java
index dd5ede57b2..3544578497 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceBinderTest.java
@@ -44,9 +44,10 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
+/** Test cases for {@link HapClientServiceBinder} */
@MediumTest
@RunWith(AndroidJUnit4.class)
-public class HapClientBinderTest {
+public class HapClientServiceBinderTest {
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private HapClientService mHapClientService;
@@ -59,11 +60,11 @@ public class HapClientBinderTest {
private final AttributionSource mAttributionSource = mAdapter.getAttributionSource();
private final BluetoothDevice mDevice = getTestDevice(0);
- private HapClientBinder mBinder;
+ private HapClientServiceBinder mBinder;
@Before
public void setUp() throws Exception {
- mBinder = new HapClientBinder(mHapClientService);
+ mBinder = new HapClientServiceBinder(mHapClientService);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceBinderTest.java
new file mode 100644
index 0000000000..8dae3bf594
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceBinderTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.hearingaid;
+
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import static com.android.bluetooth.TestUtils.MockitoRule;
+import static com.android.bluetooth.TestUtils.getTestDevice;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothManager;
+import android.content.AttributionSource;
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Test cases for {@link HearingAidServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HearingAidServiceBinderTest {
+
+ @Rule public final MockitoRule mMockitoRule = new MockitoRule();
+
+ @Mock private HearingAidService mService;
+
+ private HearingAidServiceBinder mBinder;
+ private AttributionSource mAttributionSource;
+ private BluetoothDevice mTestDevice;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mService.isAvailable()).thenReturn(true);
+ mBinder = new HearingAidServiceBinder(mService);
+ Context context = InstrumentationRegistry.getTargetContext();
+ mAttributionSource =
+ context.getSystemService(BluetoothManager.class)
+ .getAdapter()
+ .getAttributionSource();
+ mTestDevice = getTestDevice(0);
+ }
+
+ @Test
+ public void connect() {
+ mBinder.connect(mTestDevice, mAttributionSource);
+ verify(mService).connect(mTestDevice);
+ }
+
+ @Test
+ public void disconnect() {
+ mBinder.disconnect(mTestDevice, mAttributionSource);
+ verify(mService).disconnect(mTestDevice);
+ }
+
+ @Test
+ public void getConnectedDevices() {
+ List<BluetoothDevice> connectedDevices = new ArrayList<>();
+ connectedDevices.add(mTestDevice);
+ when(mService.getConnectedDevices()).thenReturn(connectedDevices);
+
+ mBinder.getConnectedDevices(mAttributionSource);
+ verify(mService).getConnectedDevices();
+ }
+
+ @Test
+ public void getDevicesMatchingConnectionStates() {
+ int[] states = new int[] {STATE_CONNECTED, STATE_DISCONNECTED};
+ List<BluetoothDevice> devices = new ArrayList<>();
+ devices.add(mTestDevice);
+ when(mService.getDevicesMatchingConnectionStates(states)).thenReturn(devices);
+
+ mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
+ verify(mService).getDevicesMatchingConnectionStates(states);
+ }
+
+ @Test
+ public void getConnectionState() {
+ when(mService.getConnectionState(mTestDevice)).thenReturn(STATE_CONNECTED);
+
+ mBinder.getConnectionState(mTestDevice, mAttributionSource);
+ verify(mService).getConnectionState(mTestDevice);
+ }
+
+ @Test
+ public void setActiveDevice() {
+ mBinder.setActiveDevice(mTestDevice, mAttributionSource);
+ verify(mService).setActiveDevice(mTestDevice);
+ }
+
+ @Test
+ public void removeActiveDevice() {
+ mBinder.setActiveDevice(null, mAttributionSource);
+ verify(mService).removeActiveDevice(false);
+ }
+
+ @Test
+ public void getActiveDevices() {
+ List<BluetoothDevice> activeDevices = new ArrayList<>();
+ activeDevices.add(mTestDevice);
+ when(mService.getActiveDevices()).thenReturn(activeDevices);
+
+ mBinder.getActiveDevices(mAttributionSource);
+ verify(mService).getActiveDevices();
+ }
+
+ @Test
+ public void setConnectionPolicy() {
+ mBinder.setConnectionPolicy(mTestDevice, CONNECTION_POLICY_ALLOWED, mAttributionSource);
+ verify(mService).setConnectionPolicy(mTestDevice, CONNECTION_POLICY_ALLOWED);
+ }
+
+ @Test
+ public void getConnectionPolicy() {
+ when(mService.getConnectionPolicy(mTestDevice)).thenReturn(CONNECTION_POLICY_FORBIDDEN);
+
+ mBinder.getConnectionPolicy(mTestDevice, mAttributionSource);
+ verify(mService).getConnectionPolicy(mTestDevice);
+ }
+
+ @Test
+ public void setVolume() {
+ int volume = 50;
+
+ mBinder.setVolume(volume, mAttributionSource);
+ verify(mService).setVolume(volume);
+ }
+
+ @Test
+ public void getHiSyncId() {
+ long hiSyncId = 1234567890L;
+ when(mService.getHiSyncId(mTestDevice)).thenReturn(hiSyncId);
+
+ mBinder.getHiSyncId(mTestDevice, mAttributionSource);
+ verify(mService).getHiSyncId(mTestDevice);
+ }
+
+ @Test
+ public void getDeviceSide() {
+ int side = BluetoothHearingAid.SIDE_LEFT;
+ when(mService.getCapabilities(mTestDevice)).thenReturn(side);
+
+ mBinder.getDeviceSide(mTestDevice, mAttributionSource);
+ verify(mService).getCapabilities(mTestDevice);
+ }
+
+ @Test
+ public void getDeviceMode() {
+ int mode = BluetoothHearingAid.MODE_BINAURAL;
+ when(mService.getCapabilities(mTestDevice)).thenReturn(mode << 1);
+
+ mBinder.getDeviceMode(mTestDevice, mAttributionSource);
+ verify(mService).getCapabilities(mTestDevice);
+ }
+
+ @Test
+ public void getAdvertisementServiceData() {
+ BluetoothHearingAid.AdvertisementServiceData data =
+ new BluetoothHearingAid.AdvertisementServiceData(0, 0);
+ when(mService.getAdvertisementServiceData(mTestDevice)).thenReturn(data);
+
+ mBinder.getAdvertisementServiceData(mTestDevice, mAttributionSource);
+ verify(mService).getAdvertisementServiceData(mTestDevice);
+ }
+
+ @Test
+ public void cleanup_doesNotCrash() {
+ mBinder.cleanup();
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
index 757a6488be..a284d6a414 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
@@ -97,7 +97,7 @@ public class HearingAidServiceTest {
private final BluetoothDevice mSingleDevice = getTestDevice(13);
private HearingAidService mService;
- private HearingAidService.BluetoothHearingAidBinder mBinder;
+ private HearingAidServiceBinder mBinder;
private InOrder mInOrder;
private TestLooper mLooper;
@@ -124,7 +124,7 @@ public class HearingAidServiceTest {
mService = new HearingAidService(mAdapterService, mLooper.getLooper(), mNativeInterface);
mService.setAvailable(true);
- mBinder = (HearingAidService.BluetoothHearingAidBinder) mService.initBinder();
+ mBinder = (HearingAidServiceBinder) mService.initBinder();
}
@After
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/BluetoothHeadsetBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceBinderTest.java
index d125fbee4c..73f3610505 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/BluetoothHeadsetBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceBinderTest.java
@@ -27,12 +27,20 @@ import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothDevice;
import android.content.AttributionSource;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
-public class BluetoothHeadsetBinderTest {
+/** Test cases for {@link HeadsetServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HeadsetServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private HeadsetService mService;
@@ -40,11 +48,11 @@ public class BluetoothHeadsetBinderTest {
private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build();
private final BluetoothDevice mDevice = getTestDevice(39);
- private HeadsetService.BluetoothHeadsetBinder mBinder;
+ private HeadsetServiceBinder mBinder;
@Before
public void setUp() throws Exception {
- mBinder = new HeadsetService.BluetoothHeadsetBinder(mService);
+ mBinder = new HeadsetServiceBinder(mService);
}
@Test
@@ -68,6 +76,7 @@ public class BluetoothHeadsetBinderTest {
@Test
public void getDevicesMatchingConnectionStates() {
int[] states = new int[] {STATE_CONNECTED};
+
mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
verify(mService).getDevicesMatchingConnectionStates(states);
}
@@ -142,6 +151,7 @@ public class BluetoothHeadsetBinderTest {
@Test
public void setAudioRouteAllowed() {
boolean allowed = true;
+
mBinder.setAudioRouteAllowed(allowed, mAttributionSource);
verify(mService).setAudioRouteAllowed(allowed);
}
@@ -155,6 +165,7 @@ public class BluetoothHeadsetBinderTest {
@Test
public void setForceScoAudio() {
boolean forced = true;
+
mBinder.setForceScoAudio(forced, mAttributionSource);
verify(mService).setForceScoAudio(forced);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java
index b3171bc191..3a1a31d085 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java
@@ -35,6 +35,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link HeadsetClientServiceBinder} */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class HeadsetClientServiceBinderTest {
@@ -44,11 +45,11 @@ public class HeadsetClientServiceBinderTest {
private final BluetoothDevice mDevice = getTestDevice(54);
- HeadsetClientService.BluetoothHeadsetClientBinder mBinder;
+ private HeadsetClientServiceBinder mBinder;
@Before
public void setUp() throws Exception {
- mBinder = new HeadsetClientService.BluetoothHeadsetClientBinder(mService);
+ mBinder = new HeadsetClientServiceBinder(mService);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/BluetoothHidDeviceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceBinderTest.java
index 8a03fe7186..4369e85310 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/BluetoothHidDeviceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceBinderTest.java
@@ -33,12 +33,20 @@ import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
import android.bluetooth.IBluetoothHidDeviceCallback;
import android.content.AttributionSource;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
-public class BluetoothHidDeviceBinderTest {
+/** Test cases for {@link HidDeviceServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HidDeviceServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private HidDeviceService mService;
@@ -46,12 +54,12 @@ public class BluetoothHidDeviceBinderTest {
private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build();
private final BluetoothDevice mDevice = getTestDevice(29);
- private HidDeviceService.BluetoothHidDeviceBinder mBinder;
+ private HidDeviceServiceBinder mBinder;
@Before
public void setUp() throws Exception {
when(mService.isAvailable()).thenReturn(true);
- mBinder = new HidDeviceService.BluetoothHidDeviceBinder(mService);
+ mBinder = new HidDeviceServiceBinder(mService);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceTest.java
index 5ddb36e4c6..a242f054a7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceTest.java
@@ -76,7 +76,7 @@ import org.mockito.hamcrest.MockitoHamcrest;
@MediumTest
@RunWith(AndroidJUnit4.class)
-public class HidDeviceTest {
+public class HidDeviceServiceTest {
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private AdapterService mAdapterService;
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java
index 02bee889e8..e2b15ebdba 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java
@@ -36,9 +36,11 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link HidHostServiceBinder}. */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class HidHostServiceBinderTest {
+
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@@ -46,98 +48,90 @@ public class HidHostServiceBinderTest {
private final BluetoothDevice mDevice = getTestDevice(50);
- private HidHostService.BluetoothHidHostBinder mBinder;
+ private HidHostServiceBinder mBinder;
@Before
public void setUp() {
- mBinder = new HidHostService.BluetoothHidHostBinder(mService);
+ mBinder = new HidHostServiceBinder(mService);
}
@Test
public void connect_callsServiceMethod() {
mBinder.connect(mDevice, null);
-
verify(mService).connect(mDevice);
}
@Test
public void disconnect_callsServiceMethod() {
mBinder.disconnect(mDevice, null);
-
verify(mService).disconnect(mDevice);
}
@Test
public void getConnectedDevices_callsServiceMethod() {
mBinder.getConnectedDevices(null);
-
verify(mService).getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED});
}
@Test
public void getDevicesMatchingConnectionStates_callsServiceMethod() {
int[] states = new int[] {STATE_CONNECTED};
- mBinder.getDevicesMatchingConnectionStates(states, null);
+ mBinder.getDevicesMatchingConnectionStates(states, null);
verify(mService).getDevicesMatchingConnectionStates(states);
}
@Test
public void getConnectionState_callsServiceMethod() {
mBinder.getConnectionState(mDevice, null);
-
verify(mService).getConnectionState(mDevice);
}
@Test
public void setConnectionPolicy_callsServiceMethod() {
int connectionPolicy = CONNECTION_POLICY_ALLOWED;
- mBinder.setConnectionPolicy(mDevice, connectionPolicy, null);
+ mBinder.setConnectionPolicy(mDevice, connectionPolicy, null);
verify(mService).setConnectionPolicy(mDevice, connectionPolicy);
}
@Test
public void getConnectionPolicy_callsServiceMethod() {
mBinder.getConnectionPolicy(mDevice, null);
-
verify(mService).getConnectionPolicy(mDevice);
}
@Test
public void setPreferredTransport_callsServiceMethod() {
int preferredTransport = BluetoothDevice.TRANSPORT_AUTO;
- mBinder.setPreferredTransport(mDevice, preferredTransport, null);
+ mBinder.setPreferredTransport(mDevice, preferredTransport, null);
verify(mService).setPreferredTransport(mDevice, preferredTransport);
}
@Test
public void getPreferredTransport_callsServiceMethod() {
mBinder.getPreferredTransport(mDevice, null);
-
verify(mService).getPreferredTransport(mDevice);
}
@Test
public void getProtocolMode_callsServiceMethod() {
mBinder.getProtocolMode(mDevice, null);
-
verify(mService).getProtocolMode(mDevice);
}
@Test
public void virtualUnplug_callsServiceMethod() {
mBinder.virtualUnplug(mDevice, null);
-
verify(mService).virtualUnplug(mDevice);
}
@Test
public void setProtocolMode_callsServiceMethod() {
int protocolMode = 1;
- mBinder.setProtocolMode(mDevice, protocolMode, null);
+ mBinder.setProtocolMode(mDevice, protocolMode, null);
verify(mService).setProtocolMode(mDevice, protocolMode);
}
@@ -146,8 +140,8 @@ public class HidHostServiceBinderTest {
byte reportType = 1;
byte reportId = 2;
int bufferSize = 16;
- mBinder.getReport(mDevice, reportType, reportId, bufferSize, null);
+ mBinder.getReport(mDevice, reportType, reportId, bufferSize, null);
verify(mService).getReport(mDevice, reportType, reportId, bufferSize);
}
@@ -155,31 +149,30 @@ public class HidHostServiceBinderTest {
public void setReport_callsServiceMethod() {
byte reportType = 1;
String report = "test_report";
- mBinder.setReport(mDevice, reportType, report, null);
+ mBinder.setReport(mDevice, reportType, report, null);
verify(mService).setReport(mDevice, reportType, report);
}
@Test
public void sendData_callsServiceMethod() {
String report = "test_report";
- mBinder.sendData(mDevice, report, null);
+ mBinder.sendData(mDevice, report, null);
verify(mService).sendData(mDevice, report);
}
@Test
public void setIdleTime_callsServiceMethod() {
byte idleTime = 1;
- mBinder.setIdleTime(mDevice, idleTime, null);
+ mBinder.setIdleTime(mDevice, idleTime, null);
verify(mService).setIdleTime(mDevice, idleTime);
}
@Test
public void getIdleTime_callsServiceMethod() {
mBinder.getIdleTime(mDevice, null);
-
verify(mService).getIdleTime(mDevice);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
index a41f93d5d6..10f5f3257f 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.bluetooth.hid;
import static android.bluetooth.BluetoothDevice.BOND_BONDED;
@@ -53,6 +54,7 @@ import java.util.List;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class HidHostServiceTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private AdapterService mAdapterService;
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceBinderTest.java
index 15b3563df1..68677ffedb 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceBinderTest.java
@@ -36,15 +36,23 @@ import android.content.AttributionSource;
import android.os.ParcelUuid;
import android.platform.test.flag.junit.SetFlagsRule;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.util.UUID;
-public class LeAudioBinderTest {
+/** Test cases for {@link LeAudioServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LeAudioServiceBinderTest {
+
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@@ -53,11 +61,11 @@ public class LeAudioBinderTest {
private static final String TEST_BROADCAST_NAME = "TEST";
private static final int TEST_QUALITY = BluetoothLeBroadcastSubgroupSettings.QUALITY_STANDARD;
- private LeAudioService.BluetoothLeAudioBinder mBinder;
+ private LeAudioServiceBinder mBinder;
@Before
public void setUp() {
- mBinder = new LeAudioService.BluetoothLeAudioBinder(mService);
+ mBinder = new LeAudioServiceBinder(mService);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java
index fa0c3d3f35..b3125460c4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java
@@ -35,77 +35,73 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link BluetoothMapServiceBinder} */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BluetoothMapServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private BluetoothMapService mService;
private final BluetoothDevice mDevice = getTestDevice(98);
- private BluetoothMapService.BluetoothMapBinder mBinder;
+ private BluetoothMapServiceBinder mBinder;
@Before
public void setUp() throws Exception {
- mBinder = new BluetoothMapService.BluetoothMapBinder(mService);
+ mBinder = new BluetoothMapServiceBinder(mService);
}
@Test
public void disconnect_callsServiceMethod() {
mBinder.disconnect(mDevice, null);
-
verify(mService).disconnect(mDevice);
}
@Test
public void getConnectedDevices_callsServiceMethod() {
mBinder.getConnectedDevices(null);
-
verify(mService).getConnectedDevices();
}
@Test
public void getDevicesMatchingConnectionStates_callsServiceMethod() {
int[] states = new int[] {STATE_CONNECTED};
- mBinder.getDevicesMatchingConnectionStates(states, null);
+ mBinder.getDevicesMatchingConnectionStates(states, null);
verify(mService).getDevicesMatchingConnectionStates(states);
}
@Test
public void getConnectionState_callsServiceMethod() {
mBinder.getConnectionState(mDevice, null);
-
verify(mService).getConnectionState(mDevice);
}
@Test
public void setConnectionPolicy_callsServiceMethod() {
int connectionPolicy = CONNECTION_POLICY_ALLOWED;
- mBinder.setConnectionPolicy(mDevice, connectionPolicy, null);
+ mBinder.setConnectionPolicy(mDevice, connectionPolicy, null);
verify(mService).setConnectionPolicy(mDevice, connectionPolicy);
}
@Test
public void getConnectionPolicy_callsServiceMethod() {
mBinder.getConnectionPolicy(mDevice, null);
-
verify(mService).getConnectionPolicy(mDevice);
}
@Test
public void getState_callsServiceMethod() {
mBinder.getState(null);
-
verify(mService).getState();
}
@Test
public void isConnected_callsServiceStaticMethod() {
mBinder.isConnected(mDevice, null);
-
verify(mService).getConnectionState(mDevice);
}
@@ -116,7 +112,7 @@ public class BluetoothMapServiceBinderTest {
}
@Test
- public void cleanUp_doesNotCrash() {
+ public void cleanup_doesNotCrash() {
mBinder.cleanup();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java
index 0830f8b9f9..a67a5c3981 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java
@@ -35,70 +35,67 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link MapClientServiceBinder} */
@MediumTest
@RunWith(AndroidJUnit4.class)
public class MapClientServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private MapClientService mService;
private final BluetoothDevice mDevice = getTestDevice(65);
- MapClientService.Binder mBinder;
+ private MapClientServiceBinder mBinder;
@Before
public void setUp() {
- mBinder = new MapClientService.Binder(mService);
+ mBinder = new MapClientServiceBinder(mService);
}
@Test
public void connect_callsServiceMethod() {
mBinder.connect(mDevice, null);
-
verify(mService).connect(mDevice);
}
@Test
public void disconnect_callsServiceMethod() {
mBinder.disconnect(mDevice, null);
-
verify(mService).disconnect(mDevice);
}
@Test
public void getConnectedDevices_callsServiceMethod() {
mBinder.getConnectedDevices(null);
-
verify(mService).getConnectedDevices();
}
@Test
public void getDevicesMatchingConnectionStates_callsServiceMethod() {
int[] states = new int[] {STATE_CONNECTED};
- mBinder.getDevicesMatchingConnectionStates(states, null);
+ mBinder.getDevicesMatchingConnectionStates(states, null);
verify(mService).getDevicesMatchingConnectionStates(states);
}
@Test
public void getConnectionState_callsServiceMethod() {
mBinder.getConnectionState(mDevice, null);
-
verify(mService).getConnectionState(mDevice);
}
@Test
public void setConnectionPolicy_callsServiceMethod() {
int connectionPolicy = CONNECTION_POLICY_ALLOWED;
- mBinder.setConnectionPolicy(mDevice, connectionPolicy, null);
+ mBinder.setConnectionPolicy(mDevice, connectionPolicy, null);
verify(mService).setConnectionPolicy(mDevice, connectionPolicy);
}
@Test
public void getConnectionPolicy_callsServiceMethod() {
mBinder.getConnectionPolicy(mDevice, null);
-
verify(mService).getConnectionPolicy(mDevice);
}
@@ -106,13 +103,13 @@ public class MapClientServiceBinderTest {
public void sendMessage_callsServiceMethod() {
Uri[] contacts = new Uri[] {};
String message = "test_message";
- mBinder.sendMessage(mDevice, contacts, message, null, null, null);
+ mBinder.sendMessage(mDevice, contacts, message, null, null, null);
verify(mService).sendMessage(mDevice, contacts, message, null, null);
}
@Test
- public void cleanUp_doesNotCrash() {
+ public void cleanup_doesNotCrash() {
mBinder.cleanup();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceBinderTest.java
new file mode 100644
index 0000000000..dc21e810b8
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceBinderTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.mcp;
+
+import static com.android.bluetooth.TestUtils.MockitoRule;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.AttributionSource;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/** Test cases for {@link McpServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class McpServiceBinderTest {
+
+ @Rule public final MockitoRule mMockitoRule = new MockitoRule();
+
+ @Mock private McpService mService;
+
+ private AttributionSource mAttributionSource;
+ private McpServiceBinder mBinder;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mService.isAvailable()).thenReturn(true);
+ mBinder = new McpServiceBinder(mService);
+ mAttributionSource = new AttributionSource.Builder(1).build();
+ }
+
+ @Test
+ public void setDeviceAuthorized() {
+ BluetoothDevice device = mock(BluetoothDevice.class);
+ boolean isAuthorized = true;
+
+ mBinder.setDeviceAuthorized(device, isAuthorized, mAttributionSource);
+ verify(mService).setDeviceAuthorized(device, isAuthorized);
+ }
+
+ @Test
+ public void cleanup_doesNotCrash() {
+ mBinder.cleanup();
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java
index 705c91d518..cadff94862 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java
@@ -44,6 +44,7 @@ import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.provider.Settings;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -432,4 +433,22 @@ public class BluetoothOppUtilityTest {
assertWithMessage("Exception should not happen. " + e).fail();
}
}
+
+ @Test
+ public void grantPermissionToNearbyComponent() {
+ Uri originalUri = Uri.parse("content://test.provider/1");
+ Settings.Secure.putString(
+ mContext.getContentResolver(),
+ "nearby_sharing_component",
+ "com.example/.BComponent");
+ Context spiedContext = spy(new ContextWrapper(mContext));
+
+ BluetoothOppUtility.grantPermissionToNearbyComponent(spiedContext, List.of(originalUri));
+
+ verify(spiedContext)
+ .grantUriPermission(
+ eq("com.example"),
+ eq(originalUri),
+ eq(Intent.FLAG_GRANT_READ_URI_PERMISSION));
+ }
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java
index d798666437..1ba4881c90 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java
@@ -35,75 +35,72 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link PanServiceBinder} */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PanServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private PanService mService;
private final BluetoothDevice mDevice = getTestDevice(64);
- PanService.BluetoothPanBinder mBinder;
+ private PanServiceBinder mBinder;
@Before
public void setUp() throws Exception {
- mBinder = new PanService.BluetoothPanBinder(mService);
+ mBinder = new PanServiceBinder(mService);
}
@Test
public void connect_callsServiceMethod() {
mBinder.connect(mDevice, null);
-
verify(mService).connect(mDevice);
}
@Test
public void disconnect_callsServiceMethod() {
mBinder.disconnect(mDevice, null);
-
verify(mService).disconnect(mDevice);
}
@Test
public void getConnectedDevices_callsServiceMethod() {
mBinder.getConnectedDevices(null);
-
verify(mService).getConnectedDevices();
}
@Test
public void getDevicesMatchingConnectionStates_callsServiceMethod() {
int[] states = new int[] {STATE_CONNECTED};
- mBinder.getDevicesMatchingConnectionStates(states, null);
+ mBinder.getDevicesMatchingConnectionStates(states, null);
verify(mService).getDevicesMatchingConnectionStates(states);
}
@Test
public void getConnectionState_callsServiceMethod() {
mBinder.getConnectionState(mDevice, null);
-
verify(mService).getConnectionState(mDevice);
}
@Test
public void setConnectionPolicy_callsServiceMethod() {
int connectionPolicy = CONNECTION_POLICY_ALLOWED;
- mBinder.setConnectionPolicy(mDevice, connectionPolicy, null);
+ mBinder.setConnectionPolicy(mDevice, connectionPolicy, null);
verify(mService).setConnectionPolicy(mDevice, connectionPolicy);
}
@Test
public void isTetheringOn_callsServiceMethod() {
mBinder.isTetheringOn(null);
-
verify(mService).isTetheringOn();
}
@Test
- public void cleanUp_doesNotCrash() {
+ public void cleanup_doesNotCrash() {
mBinder.cleanup();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java
index 95b2bb25ef..2397f3897a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java
@@ -35,20 +35,22 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+/** Test cases for {@link BluetoothPbapServiceBinder} */
@MediumTest
@RunWith(AndroidJUnit4.class)
public class BluetoothPbapServiceBinderTest {
+
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private BluetoothPbapService mService;
private final BluetoothDevice mDevice = getTestDevice(60);
- BluetoothPbapService.PbapBinder mBinder;
+ private BluetoothPbapServiceBinder mBinder;
@Before
public void setUp() {
- mBinder = new BluetoothPbapService.PbapBinder(mService);
+ mBinder = new BluetoothPbapServiceBinder(mService);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceBinderTest.java
index aff8f250a7..94b51136dd 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceBinderTest.java
@@ -47,29 +47,30 @@ import org.mockito.Mock;
import java.util.List;
+/** Test cases for {@link PbapClientServiceBinder} */
@MediumTest
@RunWith(AndroidJUnit4.class)
-public class PbapClientBinderTest {
+public class PbapClientServiceBinderTest {
@Rule public final MockitoRule mMockitoRule = new MockitoRule();
@Mock private PbapClientService mMockService;
private BluetoothDevice mTestDevice;
private AttributionSource mAttributionSource;
- private PbapClientBinder mPbapClientBinder;
+ private PbapClientServiceBinder mPbapClientServiceBinder;
@Before
public void setUp() throws Exception {
mTestDevice = getTestDevice(1);
mAttributionSource = new AttributionSource.Builder(1).build();
- mPbapClientBinder = new PbapClientBinder(mMockService);
+ mPbapClientServiceBinder = new PbapClientServiceBinder(mMockService);
}
@After
public void tearDown() throws Exception {
- if (mPbapClientBinder != null) {
- mPbapClientBinder.cleanup();
- mPbapClientBinder = null;
+ if (mPbapClientServiceBinder != null) {
+ mPbapClientServiceBinder.cleanup();
+ mPbapClientServiceBinder = null;
}
}
@@ -79,45 +80,46 @@ public class PbapClientBinderTest {
@Test
public void testConnect() {
- mPbapClientBinder.connect(mTestDevice, mAttributionSource);
+ mPbapClientServiceBinder.connect(mTestDevice, mAttributionSource);
verify(mMockService).connect(eq(mTestDevice));
}
@Test
public void testDisconnect() {
- mPbapClientBinder.disconnect(mTestDevice, mAttributionSource);
+ mPbapClientServiceBinder.disconnect(mTestDevice, mAttributionSource);
verify(mMockService).disconnect(eq(mTestDevice));
}
@Test
public void testGetConnectedDevices() {
- mPbapClientBinder.getConnectedDevices(mAttributionSource);
+ mPbapClientServiceBinder.getConnectedDevices(mAttributionSource);
verify(mMockService).getConnectedDevices();
}
@Test
public void testGetDevicesMatchingConnectionStates() {
int[] states = new int[] {STATE_CONNECTED};
- mPbapClientBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
+ mPbapClientServiceBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
verify(mMockService).getDevicesMatchingConnectionStates(eq(states));
}
@Test
public void testGetConnectionState() {
- mPbapClientBinder.getConnectionState(mTestDevice, mAttributionSource);
+ mPbapClientServiceBinder.getConnectionState(mTestDevice, mAttributionSource);
verify(mMockService).getConnectionState(eq(mTestDevice));
}
@Test
public void testSetConnectionPolicy() {
int connectionPolicy = CONNECTION_POLICY_ALLOWED;
- mPbapClientBinder.setConnectionPolicy(mTestDevice, connectionPolicy, mAttributionSource);
+ mPbapClientServiceBinder.setConnectionPolicy(
+ mTestDevice, connectionPolicy, mAttributionSource);
verify(mMockService).setConnectionPolicy(eq(mTestDevice), eq(connectionPolicy));
}
@Test
public void testGetConnectionPolicy() {
- mPbapClientBinder.getConnectionPolicy(mTestDevice, mAttributionSource);
+ mPbapClientServiceBinder.getConnectionPolicy(mTestDevice, mAttributionSource);
verify(mMockService).getConnectionPolicy(eq(mTestDevice));
}
@@ -127,52 +129,54 @@ public class PbapClientBinderTest {
@Test
public void testConnect_afterCleanup_returnsFalse() {
- mPbapClientBinder.cleanup();
- boolean result = mPbapClientBinder.connect(mTestDevice, mAttributionSource);
+ mPbapClientServiceBinder.cleanup();
+ boolean result = mPbapClientServiceBinder.connect(mTestDevice, mAttributionSource);
verify(mMockService, never()).connect(any(BluetoothDevice.class));
assertThat(result).isFalse();
}
@Test
public void testDisconnect_afterCleanup_returnsFalse() {
- mPbapClientBinder.cleanup();
- boolean result = mPbapClientBinder.disconnect(mTestDevice, mAttributionSource);
+ mPbapClientServiceBinder.cleanup();
+ boolean result = mPbapClientServiceBinder.disconnect(mTestDevice, mAttributionSource);
verify(mMockService, never()).disconnect(any(BluetoothDevice.class));
assertThat(result).isFalse();
}
@Test
public void testGetConnectedDevices_afterCleanup_returnsEmptyList() {
- mPbapClientBinder.cleanup();
- List<BluetoothDevice> devices = mPbapClientBinder.getConnectedDevices(mAttributionSource);
+ mPbapClientServiceBinder.cleanup();
+ List<BluetoothDevice> devices =
+ mPbapClientServiceBinder.getConnectedDevices(mAttributionSource);
verify(mMockService, never()).getConnectedDevices();
assertThat(devices).isEmpty();
}
@Test
public void testGetDevicesMatchingConnectionStates_afterCleanup_returnsEmptyList() {
- mPbapClientBinder.cleanup();
+ mPbapClientServiceBinder.cleanup();
int[] states = new int[] {STATE_CONNECTED};
List<BluetoothDevice> devices =
- mPbapClientBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
+ mPbapClientServiceBinder.getDevicesMatchingConnectionStates(
+ states, mAttributionSource);
verify(mMockService, never()).getDevicesMatchingConnectionStates(any(int[].class));
assertThat(devices).isEmpty();
}
@Test
public void testGetConnectionState_afterCleanup_returnsDisconnected() {
- mPbapClientBinder.cleanup();
- int state = mPbapClientBinder.getConnectionState(mTestDevice, mAttributionSource);
+ mPbapClientServiceBinder.cleanup();
+ int state = mPbapClientServiceBinder.getConnectionState(mTestDevice, mAttributionSource);
verify(mMockService, never()).getConnectionState(any(BluetoothDevice.class));
assertThat(state).isEqualTo(STATE_DISCONNECTED);
}
@Test
public void testSetConnectionPolicy_afterCleanup_returnsFalse() {
- mPbapClientBinder.cleanup();
+ mPbapClientServiceBinder.cleanup();
int connectionPolicy = CONNECTION_POLICY_ALLOWED;
boolean result =
- mPbapClientBinder.setConnectionPolicy(
+ mPbapClientServiceBinder.setConnectionPolicy(
mTestDevice, connectionPolicy, mAttributionSource);
verify(mMockService, never()).setConnectionPolicy(any(BluetoothDevice.class), anyInt());
assertThat(result).isFalse();
@@ -180,8 +184,8 @@ public class PbapClientBinderTest {
@Test
public void testGetConnectionPolicy_afterCleanup_returnsUnknown() {
- mPbapClientBinder.cleanup();
- int result = mPbapClientBinder.getConnectionPolicy(mTestDevice, mAttributionSource);
+ mPbapClientServiceBinder.cleanup();
+ int result = mPbapClientServiceBinder.getConnectionPolicy(mTestDevice, mAttributionSource);
verify(mMockService, never()).getConnectionPolicy(any(BluetoothDevice.class));
assertThat(result).isEqualTo(CONNECTION_POLICY_UNKNOWN);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceBinderTest.java
new file mode 100644
index 0000000000..10d1a092e0
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceBinderTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.sap;
+
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
+import static com.android.bluetooth.TestUtils.MockitoRule;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.AttributionSource;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/** Test cases for {@link SapServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SapServiceBinderTest {
+
+ @Rule public final MockitoRule mMockitoRule = new MockitoRule();
+
+ @Mock private SapService mService;
+
+ private AttributionSource mAttributionSource;
+ private SapServiceBinder mBinder;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mService.isAvailable()).thenReturn(true);
+ mBinder = new SapServiceBinder(mService);
+ mAttributionSource = new AttributionSource.Builder(1).build();
+ }
+
+ @Test
+ public void getState() {
+ mBinder.getState(mAttributionSource);
+ verify(mService).getState();
+ }
+
+ @Test
+ public void getClient() {
+ mBinder.getClient(mAttributionSource);
+ // times(2) due to the Log
+ verify(mService, times(2)).getRemoteDevice();
+ }
+
+ @Test
+ public void isConnected() {
+ BluetoothDevice device = mock(BluetoothDevice.class);
+
+ mBinder.isConnected(device, mAttributionSource);
+ verify(mService).getConnectionState(device);
+ }
+
+ @Test
+ public void disconnect() {
+ BluetoothDevice device = mock(BluetoothDevice.class);
+
+ mBinder.disconnect(device, mAttributionSource);
+ verify(mService).disconnect(device);
+ }
+
+ @Test
+ public void getConnectedDevices() {
+ mBinder.getConnectedDevices(mAttributionSource);
+ verify(mService).getConnectedDevices();
+ }
+
+ @Test
+ public void getDevicesMatchingConnectionStates() {
+ int[] states = new int[] {STATE_CONNECTED, STATE_DISCONNECTED};
+
+ mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
+ verify(mService).getDevicesMatchingConnectionStates(states);
+ }
+
+ @Test
+ public void getConnectionState() {
+ BluetoothDevice device = mock(BluetoothDevice.class);
+
+ mBinder.getConnectionState(device, mAttributionSource);
+ verify(mService).getConnectionState(device);
+ }
+
+ @Test
+ public void setConnectionPolicy() {
+ BluetoothDevice device = mock(BluetoothDevice.class);
+ int connectionPolicy = 1;
+
+ mBinder.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ verify(mService).setConnectionPolicy(device, connectionPolicy);
+ }
+
+ @Test
+ public void getConnectionPolicy() {
+ BluetoothDevice device = mock(BluetoothDevice.class);
+
+ mBinder.getConnectionPolicy(device, mAttributionSource);
+ verify(mService).getConnectionPolicy(device);
+ }
+
+ @Test
+ public void cleanup_doesNotCrash() {
+ mBinder.cleanup();
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsServiceBinderTest.java
new file mode 100644
index 0000000000..7758d101b1
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsServiceBinderTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.tbs;
+
+import static com.android.bluetooth.TestUtils.MockitoRule;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothLeCall;
+import android.bluetooth.IBluetoothLeCallControlCallback;
+import android.content.AttributionSource;
+import android.os.ParcelUuid;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/** Test cases for {@link TbsServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TbsServiceBinderTest {
+
+ @Rule public final MockitoRule mMockitoRule = new MockitoRule();
+
+ @Mock private TbsService mService;
+
+ private AttributionSource mAttributionSource;
+ private TbsServiceBinder mBinder;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mService.isAvailable()).thenReturn(true);
+ mBinder = new TbsServiceBinder(mService);
+ mAttributionSource = new AttributionSource.Builder(1).build();
+ }
+
+ @Test
+ public void registerBearer() {
+ String token = "token";
+ IBluetoothLeCallControlCallback callback = mock(IBluetoothLeCallControlCallback.class);
+ String uci = "uci";
+ List<String> uriSchemes = new ArrayList<>();
+ int capabilities = 1;
+ String providerName = "providerName";
+ int technology = 2;
+
+ mBinder.registerBearer(
+ token,
+ callback,
+ uci,
+ uriSchemes,
+ capabilities,
+ providerName,
+ technology,
+ mAttributionSource);
+ verify(mService)
+ .registerBearer(
+ token, callback, uci, uriSchemes, capabilities, providerName, technology);
+ }
+
+ @Test
+ public void unregisterBearer() {
+ String token = "token";
+
+ mBinder.unregisterBearer(token, mAttributionSource);
+ verify(mService).unregisterBearer(token);
+ }
+
+ @Test
+ public void requestResult() {
+ int ccid = 1;
+ int requestId = 2;
+ int result = 3;
+
+ mBinder.requestResult(ccid, requestId, result, mAttributionSource);
+ verify(mService).requestResult(ccid, requestId, result);
+ }
+
+ @Test
+ public void callAdded() {
+ int ccid = 1;
+ BluetoothLeCall call = mock(BluetoothLeCall.class);
+
+ mBinder.callAdded(ccid, call, mAttributionSource);
+ verify(mService).callAdded(ccid, call);
+ }
+
+ @Test
+ public void callRemoved() {
+ int ccid = 1;
+ UUID callId = UUID.randomUUID();
+ int reason = 2;
+
+ mBinder.callRemoved(ccid, new ParcelUuid(callId), reason, mAttributionSource);
+ verify(mService).callRemoved(ccid, callId, reason);
+ }
+
+ @Test
+ public void callStateChanged() {
+ int ccid = 1;
+ UUID callId = UUID.randomUUID();
+ int state = 2;
+
+ mBinder.callStateChanged(ccid, new ParcelUuid(callId), state, mAttributionSource);
+ verify(mService).callStateChanged(ccid, callId, state);
+ }
+
+ @Test
+ public void currentCallsList() {
+ int ccid = 1;
+ List<BluetoothLeCall> calls = new ArrayList<>();
+
+ mBinder.currentCallsList(ccid, calls, mAttributionSource);
+ verify(mService).currentCallsList(ccid, calls);
+ }
+
+ @Test
+ public void cleanup_doesNotCrash() {
+ mBinder.cleanup();
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceBinderTest.java
new file mode 100644
index 0000000000..32296381c8
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceBinderTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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.vc;
+
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+
+import static com.android.bluetooth.TestUtils.MockitoRule;
+import static com.android.bluetooth.TestUtils.getTestDevice;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IAudioInputCallback;
+import android.content.AttributionSource;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/** Test cases for {@link VolumeControlServiceBinder} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VolumeControlServiceBinderTest {
+
+ @Rule public final MockitoRule mMockitoRule = new MockitoRule();
+
+ @Mock private VolumeControlService mService;
+
+ private final BluetoothDevice mDevice = getTestDevice(25);
+
+ private AttributionSource mAttributionSource;
+ private VolumeControlServiceBinder mBinder;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mService.isAvailable()).thenReturn(true);
+ mBinder = new VolumeControlServiceBinder(mService);
+ mAttributionSource = new AttributionSource.Builder(1).build();
+ }
+
+ @Test
+ public void getConnectedDevices() {
+ mBinder.getConnectedDevices(mAttributionSource);
+ verify(mService).getConnectedDevices();
+ }
+
+ @Test
+ public void getDevicesMatchingConnectionStates() {
+ int[] states = new int[] {STATE_CONNECTED};
+
+ mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
+ verify(mService).getDevicesMatchingConnectionStates(states);
+ }
+
+ @Test
+ public void getConnectionState() {
+ mBinder.getConnectionState(mDevice, mAttributionSource);
+ verify(mService).getConnectionState(mDevice);
+ }
+
+ @Test
+ public void setConnectionPolicy() {
+ int connectionPolicy = 1;
+
+ mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource);
+ verify(mService).setConnectionPolicy(mDevice, connectionPolicy);
+ }
+
+ @Test
+ public void getConnectionPolicy() {
+ mBinder.getConnectionPolicy(mDevice, mAttributionSource);
+ verify(mService).getConnectionPolicy(mDevice);
+ }
+
+ @Test
+ public void isVolumeOffsetAvailable() {
+ mBinder.isVolumeOffsetAvailable(mDevice, mAttributionSource);
+ verify(mService).isVolumeOffsetAvailable(mDevice);
+ }
+
+ @Test
+ public void getNumberOfVolumeOffsetInstances() {
+ mBinder.getNumberOfVolumeOffsetInstances(mDevice, mAttributionSource);
+ verify(mService).getNumberOfVolumeOffsetInstances(mDevice);
+ }
+
+ @Test
+ public void setVolumeOffset() {
+ int instanceId = 1;
+ int volumeOffset = 2;
+
+ mBinder.setVolumeOffset(mDevice, instanceId, volumeOffset, mAttributionSource);
+ verify(mService).setVolumeOffset(mDevice, instanceId, volumeOffset);
+ }
+
+ @Test
+ public void setDeviceVolume() {
+ int volume = 1;
+ boolean isGroupOp = true;
+
+ mBinder.setDeviceVolume(mDevice, volume, isGroupOp, mAttributionSource);
+ verify(mService).setDeviceVolume(mDevice, volume, isGroupOp);
+ }
+
+ @Test
+ public void setGroupVolume() {
+ int groupId = 1;
+ int volume = 2;
+
+ mBinder.setGroupVolume(groupId, volume, mAttributionSource);
+ verify(mService).setGroupVolume(groupId, volume);
+ }
+
+ @Test
+ public void getGroupVolume() {
+ int groupId = 1;
+
+ mBinder.getGroupVolume(groupId, mAttributionSource);
+ verify(mService).getGroupVolume(groupId);
+ }
+
+ @Test
+ public void setGroupActive() {
+ int groupId = 1;
+ boolean active = true;
+
+ mBinder.setGroupActive(groupId, active, mAttributionSource);
+ verify(mService).setGroupActive(groupId, active);
+ }
+
+ @Test
+ public void mute() {
+ mBinder.mute(mDevice, mAttributionSource);
+ verify(mService).mute(mDevice);
+ }
+
+ @Test
+ public void muteGroup() {
+ int groupId = 1;
+ mBinder.muteGroup(groupId, mAttributionSource);
+ verify(mService).muteGroup(groupId);
+ }
+
+ @Test
+ public void unmute() {
+ mBinder.unmute(mDevice, mAttributionSource);
+ verify(mService).unmute(mDevice);
+ }
+
+ @Test
+ public void unmuteGroup() {
+ int groupId = 1;
+
+ mBinder.unmuteGroup(groupId, mAttributionSource);
+ verify(mService).unmuteGroup(groupId);
+ }
+
+ @Test
+ public void getNumberOfAudioInputControlServices() {
+ mBinder.getNumberOfAudioInputControlServices(mAttributionSource, mDevice);
+ }
+
+ @Test
+ public void registerAudioInputControlCallback() {
+ int instanceId = 1;
+ IAudioInputCallback callback = mock(IAudioInputCallback.class);
+
+ mBinder.registerAudioInputControlCallback(
+ mAttributionSource, mDevice, instanceId, callback);
+ }
+
+ @Test
+ public void unregisterAudioInputControlCallback() {
+ int instanceId = 1;
+ IAudioInputCallback callback = mock(IAudioInputCallback.class);
+
+ mBinder.unregisterAudioInputControlCallback(
+ mAttributionSource, mDevice, instanceId, callback);
+ }
+
+ @Test
+ public void getAudioInputGainSettingUnit() {
+ int instanceId = 1;
+ mBinder.getAudioInputGainSettingUnit(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void getAudioInputGainSettingMin() {
+ int instanceId = 1;
+ mBinder.getAudioInputGainSettingMin(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void getAudioInputGainSettingMax() {
+ int instanceId = 1;
+ mBinder.getAudioInputGainSettingMax(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void getAudioInputDescription() {
+ int instanceId = 1;
+ mBinder.getAudioInputDescription(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void isAudioInputDescriptionWritable() {
+ int instanceId = 1;
+ mBinder.isAudioInputDescriptionWritable(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void setAudioInputDescription() {
+ int instanceId = 1;
+ String description = "test";
+ mBinder.setAudioInputDescription(mAttributionSource, mDevice, instanceId, description);
+ }
+
+ @Test
+ public void getAudioInputStatus() {
+ int instanceId = 1;
+ mBinder.getAudioInputStatus(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void getAudioInputType() {
+ int instanceId = 1;
+ mBinder.getAudioInputType(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void getAudioInputGainSetting() {
+ int instanceId = 1;
+ mBinder.getAudioInputGainSetting(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void setAudioInputGainSetting() {
+ int instanceId = 1;
+ int gainSetting = 2;
+ mBinder.setAudioInputGainSetting(mAttributionSource, mDevice, instanceId, gainSetting);
+ }
+
+ @Test
+ public void getAudioInputGainMode() {
+ int instanceId = 1;
+ mBinder.getAudioInputGainMode(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void setAudioInputGainMode() {
+ int instanceId = 1;
+ int gainMode = 2;
+ mBinder.setAudioInputGainMode(mAttributionSource, mDevice, instanceId, gainMode);
+ }
+
+ @Test
+ public void getAudioInputMute() {
+ int instanceId = 1;
+ mBinder.getAudioInputMute(mAttributionSource, mDevice, instanceId);
+ }
+
+ @Test
+ public void setAudioInputMute() {
+ int instanceId = 1;
+ int mute = 2;
+ mBinder.setAudioInputMute(mAttributionSource, mDevice, instanceId, mute);
+ }
+
+ @Test
+ public void cleanup_doesNotCrash() {
+ mBinder.cleanup();
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
index 35d8ee3ce1..9412ebfe40 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
@@ -128,7 +128,7 @@ public class VolumeControlServiceTest {
private AttributionSource mAttributionSource;
private VolumeControlService mService;
- private VolumeControlService.BluetoothVolumeControlBinder mBinder;
+ private VolumeControlServiceBinder mBinder;
private InOrder mInOrder;
private TestLooper mLooper;
@@ -174,7 +174,7 @@ public class VolumeControlServiceTest {
mService.setAvailable(true);
mService.mFactory = mServiceFactory;
- mBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder();
+ mBinder = (VolumeControlServiceBinder) mService.initBinder();
}
@After
diff --git a/flags/gap.aconfig b/flags/gap.aconfig
index 4a03337332..edc7874fb5 100644
--- a/flags/gap.aconfig
+++ b/flags/gap.aconfig
@@ -97,13 +97,6 @@ flag {
}
flag {
- name: "non_wake_alarm_for_rpa_rotation"
- namespace: "bluetooth"
- description: "Use non-wake alarm for LE RPA rotation. go/non-wake-alarm-for-rpa-rotation"
- bug: "360743527"
-}
-
-flag {
name: "gatt_disconnect_fix"
namespace: "bluetooth"
description: "Fix GATT disconnect handling"
@@ -199,16 +192,6 @@ flag {
}
flag {
- name: "drop_acl_fragment_on_disconnect"
- namespace: "bluetooth"
- description: "Drop pending ACL packet fragments for disconnected connection"
- bug: "376379859"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "configure_scan_on_resume"
namespace: "bluetooth"
description: "Configure scan paramters when start scanning from onResume"
diff --git a/flags/hid.aconfig b/flags/hid.aconfig
index d40e55821f..ee5728c94f 100644
--- a/flags/hid.aconfig
+++ b/flags/hid.aconfig
@@ -118,3 +118,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "hh_state_update_race_fix"
+ namespace: "bluetooth"
+ description: "Fix stuck in connecting state due to race when updating state"
+ bug: "403420458"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/leaudio.aconfig b/flags/leaudio.aconfig
index 9d2c86afa3..a2f77b5658 100644
--- a/flags/leaudio.aconfig
+++ b/flags/leaudio.aconfig
@@ -344,3 +344,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "leaudio_use_aggressive_params"
+ namespace: "bluetooth"
+ description: "use aggressive parameters for LE Audio device"
+ bug: "400607635"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/opp.aconfig b/flags/opp.aconfig
index 73604961fe..cdba75b756 100644
--- a/flags/opp.aconfig
+++ b/flags/opp.aconfig
@@ -2,16 +2,6 @@ package: "com.android.bluetooth.flags"
container: "com.android.bt"
flag {
- name: "opp_ignore_content_observer_after_service_stop"
- namespace: "bluetooth"
- description: "When OppService.stop() is called, previously posted ContentObserver.onChange() should not run"
- bug: "346467641"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "opp_set_insets_for_edge_to_edge"
namespace: "bluetooth"
description: "Set proper insets in BluetoothOppTransferHistory to adapt to edge-to-edge."
@@ -41,3 +31,20 @@ flag {
}
}
+flag {
+ name: "opp_device_picker_extra_intent_apis"
+ is_exported: true
+ namespace: "bluetooth"
+ description: "New API to get the original intent in Bluetooth Device Picker"
+ bug: "395796600"
+}
+
+flag {
+ name: "send_opp_device_picker_extra_intent"
+ namespace: "bluetooth"
+ description: "Send the original intent when opening Bluetooth device picker"
+ bug: "397852103"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index cdb9bf0670..ef922c92e1 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -369,6 +369,7 @@ package android.bluetooth {
public interface BluetoothDevicePicker {
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_DEVICE_SELECTED = "android.bluetooth.devicepicker.action.DEVICE_SELECTED";
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH";
+ field @FlaggedApi("com.android.bluetooth.flags.opp_device_picker_extra_intent_apis") public static final String EXTRA_DEVICE_PICKER_ORIGINAL_SEND_INTENT = "android.bluetooth.extra.DEVICE_PICKER_ORIGINAL_SEND_INTENT";
field public static final String EXTRA_FILTER_TYPE = "android.bluetooth.devicepicker.extra.FILTER_TYPE";
field public static final String EXTRA_LAUNCH_CLASS = "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS";
field public static final String EXTRA_LAUNCH_PACKAGE = "android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE";
diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java
index ee788dae18..7e7cd6d6b7 100644
--- a/framework/java/android/bluetooth/BluetoothDevicePicker.java
+++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java
@@ -18,6 +18,7 @@ package android.bluetooth;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import android.annotation.FlaggedApi;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -25,6 +26,8 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import com.android.bluetooth.flags.Flags;
+
/**
* A helper to show a system "Device Picker" activity to the user.
*
@@ -64,6 +67,14 @@ public interface BluetoothDevicePicker {
String EXTRA_LAUNCH_CLASS = "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS";
/**
+ * Extra for the original ACTION_SEND or ACTION_SEND_MULTIPLE intent that triggered the BT
+ * sharing.
+ */
+ @FlaggedApi(Flags.FLAG_OPP_DEVICE_PICKER_EXTRA_INTENT_APIS)
+ String EXTRA_DEVICE_PICKER_ORIGINAL_SEND_INTENT =
+ "android.bluetooth.extra.DEVICE_PICKER_ORIGINAL_SEND_INTENT";
+
+ /**
* Broadcast when one BT device is selected from BT device picker screen. Selected {@link
* BluetoothDevice} is returned in extra data named {@link BluetoothDevice#EXTRA_DEVICE}.
*/
diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java
index 5f13b6266a..b797d6ceac 100644
--- a/service/src/com/android/server/bluetooth/BluetoothManagerService.java
+++ b/service/src/com/android/server/bluetooth/BluetoothManagerService.java
@@ -1391,15 +1391,31 @@ class BluetoothManagerService {
}
// If we tried to enable BT while BT was in the process of shutting down,
// wait for the BT process to fully tear down and then force a restart
- // here. This is a bit of a hack (b/29363429).
+ // here. This is a bit of a hack (b/29363429).
if (prevState == STATE_BLE_TURNING_OFF && newState == STATE_OFF) {
- if (mEnable) {
- Log.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
- if (!Flags.systemServerRemoveExtraThreadJump()) {
- waitForState(STATE_OFF);
+ if (Flags.enableBleWhileDisablingAirplane()) {
+ if (mHandler.hasMessages(0, ON_AIRPLANE_MODE_CHANGED_TOKEN)) {
+ mHandler.removeCallbacksAndMessages(ON_AIRPLANE_MODE_CHANGED_TOKEN);
+ Log.d(TAG, "Handling delayed airplane mode event");
+ handleAirplaneModeChanged(AirplaneModeListener.isOnOverrode());
+ }
+ if (mEnable && !isBinding()) {
+ Log.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
+ if (!Flags.systemServerRemoveExtraThreadJump()) {
+ waitForState(STATE_OFF);
+ }
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_RESTART_BLUETOOTH_SERVICE, getServiceRestartMs());
+ }
+ } else {
+ if (mEnable) {
+ Log.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
+ if (!Flags.systemServerRemoveExtraThreadJump()) {
+ waitForState(STATE_OFF);
+ }
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_RESTART_BLUETOOTH_SERVICE, getServiceRestartMs());
}
- mHandler.sendEmptyMessageDelayed(
- MESSAGE_RESTART_BLUETOOTH_SERVICE, getServiceRestartMs());
}
}
if (newState == STATE_ON || newState == STATE_BLE_ON) {
diff --git a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
index 5e2b66ac06..e825940e90 100644
--- a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
+++ b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
@@ -617,6 +617,35 @@ public class BluetoothManagerServiceTest {
assertThat(mLooper.nextMessage()).isNull(); // Must not create a MESSAGE_ENABLE
}
+ @Test
+ @EnableFlags({
+ Flags.FLAG_SYSTEM_SERVER_REMOVE_EXTRA_THREAD_JUMP,
+ Flags.FLAG_ENABLE_BLE_WHILE_DISABLING_AIRPLANE
+ })
+ public void enableBle_whenDisableAirplaneIsDelayed_startBluetooth() throws Exception {
+ mManagerService.enable("enableBle_whenDisableAirplaneIsDelayed_startBluetooth");
+ IBluetoothCallback btCallback = transition_offToOn();
+ mManagerService.onAirplaneModeChanged(true);
+ assertThat(mManagerService.getState()).isEqualTo(STATE_TURNING_OFF);
+
+ // Generate an event that will be delayed due to the TURNING_OFF state
+ mManagerService.onAirplaneModeChanged(false);
+
+ transition_onToBleOn(btCallback);
+ mInOrder.verify(mAdapterBinder).bleOnToOff(any());
+ assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_TURNING_OFF);
+
+ // As soon as we left BLE_ON, generate a call from 3p app that request to turn on Bluetooth
+ mManagerService.enableBle("enableBle_whenDisableAirplaneIsDelayed_startBluetooth", mBinder);
+
+ // When all the profile are started, adapterService consider it is ON
+ btCallback.onBluetoothStateChange(STATE_BLE_TURNING_OFF, STATE_OFF);
+ syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE);
+
+ transition_offToOn();
+ assertThat(mManagerService.getState()).isEqualTo(STATE_ON);
+ }
+
@SafeVarargs
private void verifyIntentSent(Matcher<Intent>... matchers) {
mInOrder.verify(mContext)
diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc
index 23b150b829..1541f391ca 100644
--- a/system/bta/gatt/bta_gattc_utils.cc
+++ b/system/bta/gatt/bta_gattc_utils.cc
@@ -142,7 +142,7 @@ tBTA_GATTC_CLCB* bta_gattc_find_clcb_by_cif(uint8_t client_if, const RawAddress&
tBTA_GATTC_CLCB* bta_gattc_find_clcb_by_conn_id(tCONN_ID conn_id) {
if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) {
for (auto& p_clcb : bta_gattc_cb.clcb_set) {
- if (p_clcb->in_use && p_clcb->bta_conn_id == conn_id) {
+ if (p_clcb != NULL && p_clcb->in_use && p_clcb->bta_conn_id == conn_id) {
return p_clcb.get();
}
}
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 44b876af4e..e10606b9dd 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -1486,7 +1486,7 @@ static void btif_dm_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH*
auto triple = eir_uuids_cache.try_emplace(bdaddr, std::set<Uuid>{});
uuid_iter = std::get<0>(triple);
}
- log::info("EIR UUIDs for {}:", bdaddr);
+ log::info("EIR UUIDs for {}", bdaddr);
for (int i = 0; i < num_uuids; ++i) {
Uuid uuid = Uuid::From16Bit(p_uuid16[i]);
log::info("{}", uuid.ToString());
@@ -1609,7 +1609,7 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
if (results_for_bonding_device) {
// success for SDP
bluetooth::metrics::LogSDPComplete(bd_addr, tBTA_STATUS::BTA_SUCCESS);
- log::info("SDP finished for {}:", bd_addr);
+ log::info("SDP finished for {}", bd_addr);
pairing_cb.sdp_over_classic = btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED;
}
@@ -1620,7 +1620,7 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
bt_property_t& le_prop = uuid_props[1];
if ((result == BTA_SUCCESS) && !uuids_param.empty()) {
- log::info("New UUIDs for {}:", bd_addr);
+ log::info("New UUIDs for {}", bd_addr);
for (const auto& uuid : uuids_param) {
if (btif_should_ignore_uuid(uuid)) {
continue;
@@ -1758,7 +1758,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
bool lea_supported = is_le_audio_capable_during_service_discovery(bd_addr);
if (is_transport_le) {
- log::info("New GATT over LE UUIDs for {}:", bd_addr);
+ log::info("New GATT over LE UUIDs for {}", bd_addr);
BTM_LogHistory(kBtmLogTag, bd_addr, "Discovered GATT services using LE transport");
if (btif_is_gatt_service_discovery_post_pairing(bd_addr)) {
pairing_cb.gatt_over_le = btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED;
@@ -1786,7 +1786,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
}
}
} else {
- log::debug("New GATT over SDP UUIDs for {}:", bd_addr);
+ log::debug("New GATT over SDP UUIDs for {}", bd_addr);
BTM_LogHistory(kBtmLogTag, bd_addr, "Discovered GATT services using SDP transport");
}
diff --git a/system/gd/hal/hci_hal_android_test.cc b/system/gd/hal/hci_hal_android_test.cc
index 247f15745f..f128f508cf 100644
--- a/system/gd/hal/hci_hal_android_test.cc
+++ b/system/gd/hal/hci_hal_android_test.cc
@@ -20,6 +20,7 @@
#include <queue>
#include <thread>
+#include "com_android_bluetooth_flags.h"
#include "hal/hci_backend.h"
#include "hal/hci_hal.h"
#include "os/thread.h"
@@ -79,10 +80,13 @@ protected:
}
void TearDown() override {
- fake_registry_.StopAll();
handler_->Clear();
- delete thread_;
+ if (com::android::bluetooth::flags::same_handler_for_all_modules()) {
+ handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout);
+ }
+ fake_registry_.StopAll();
delete handler_;
+ delete thread_;
}
HciHal* hal;
diff --git a/system/gd/hal/hci_hal_host_test.cc b/system/gd/hal/hci_hal_host_test.cc
index 4f4c6bc6d0..d4539d6c3b 100644
--- a/system/gd/hal/hci_hal_host_test.cc
+++ b/system/gd/hal/hci_hal_host_test.cc
@@ -32,6 +32,7 @@
#include <utility>
#include <vector>
+#include "com_android_bluetooth_flags.h"
#include "hal/hci_hal.h"
#include "hal/serialize_packet.h"
#include "os/thread.h"
@@ -155,12 +156,15 @@ protected:
void TearDown() override {
hal_->unregisterIncomingPacketCallback();
+ handler_->Clear();
+ if (com::android::bluetooth::flags::same_handler_for_all_modules()) {
+ handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout);
+ }
fake_registry_.StopAll();
+ delete handler_;
close(fake_server_socket_);
- handler_->Clear();
delete fake_server_;
delete thread_;
- delete handler_;
}
void SetFakeServerSocketToBlocking() {
diff --git a/system/gd/hci/acl_manager.cc b/system/gd/hci/acl_manager.cc
index 26b0ce70eb..280cc237b4 100644
--- a/system/gd/hci/acl_manager.cc
+++ b/system/gd/hci/acl_manager.cc
@@ -260,16 +260,6 @@ void AclManager::CreateLeConnection(AddressWithType address_with_type, bool is_d
CallOn(pimpl_->le_impl_, &le_impl::create_le_connection, address_with_type, true, is_direct);
}
-void AclManager::SetLeSuggestedDefaultDataParameters(uint16_t octets, uint16_t time) {
- CallOn(pimpl_->le_impl_, &le_impl::set_le_suggested_default_data_parameters, octets, time);
-}
-
-void AclManager::LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max,
- uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout) {
- CallOn(pimpl_->le_impl_, &le_impl::LeSetDefaultSubrate, subrate_min, subrate_max, max_latency,
- cont_num, sup_tout);
-}
-
void AclManager::SetPrivacyPolicyForInitiatorAddress(
LeAddressManager::AddressPolicy address_policy, AddressWithType fixed_address,
std::chrono::milliseconds minimum_rotation_time,
diff --git a/system/gd/hci/acl_manager.h b/system/gd/hci/acl_manager.h
index 2b3d523b1f..f38f1ff689 100644
--- a/system/gd/hci/acl_manager.h
+++ b/system/gd/hci/acl_manager.h
@@ -86,12 +86,6 @@ public:
// Generates OnLeConnectSuccess if connected, or OnLeConnectFail otherwise
virtual void CreateLeConnection(AddressWithType address_with_type, bool is_direct);
- // Ask the controller for specific data parameters
- virtual void SetLeSuggestedDefaultDataParameters(uint16_t octets, uint16_t time);
-
- virtual void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t sup_tout);
-
virtual void SetPrivacyPolicyForInitiatorAddress(LeAddressManager::AddressPolicy address_policy,
AddressWithType fixed_address,
std::chrono::milliseconds minimum_rotation_time,
diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h
index ce93f46e7e..ef5bfb0d7d 100644
--- a/system/gd/hci/acl_manager/le_impl.h
+++ b/system/gd/hci/acl_manager/le_impl.h
@@ -1149,25 +1149,6 @@ public:
remove_device_from_accept_list(address_with_type);
}
- void set_le_suggested_default_data_parameters(uint16_t length, uint16_t time) {
- auto packet = LeWriteSuggestedDefaultDataLengthBuilder::Create(length, time);
- le_acl_connection_interface_->EnqueueCommand(
- std::move(packet), handler_->BindOnce([](CommandCompleteView /* complete */) {}));
- }
-
- void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t sup_tout) {
- le_acl_connection_interface_->EnqueueCommand(
- LeSetDefaultSubrateBuilder::Create(subrate_min, subrate_max, max_latency, cont_num,
- sup_tout),
- handler_->BindOnce([](CommandCompleteView complete) {
- auto complete_view = LeSetDefaultSubrateCompleteView::Create(complete);
- log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
- ErrorCode status = complete_view.GetStatus();
- log::assert_that(status == ErrorCode::SUCCESS, "Status = {}", ErrorCodeText(status));
- }));
- }
-
void clear_resolving_list() { le_address_manager_->ClearResolvingList(); }
void set_privacy_policy_for_initiator_address(LeAddressManager::AddressPolicy address_policy,
diff --git a/system/gd/hci/acl_manager/le_impl_test.cc b/system/gd/hci/acl_manager/le_impl_test.cc
index 66bc16bbd8..274b6827c9 100644
--- a/system/gd/hci/acl_manager/le_impl_test.cc
+++ b/system/gd/hci/acl_manager/le_impl_test.cc
@@ -1475,29 +1475,6 @@ TEST_F(LeImplTest, cancel_connect) {
ASSERT_TRUE(le_impl_->create_connection_timeout_alarms_.empty());
}
-TEST_F(LeImplTest, set_le_suggested_default_data_parameters) {
- le_impl_->set_le_suggested_default_data_parameters(kLength, kTime);
- sync_handler();
- auto view = CreateLeConnectionManagementCommandView<LeWriteSuggestedDefaultDataLengthView>(
- hci_layer_->GetCommand());
- ASSERT_TRUE(view.IsValid());
- ASSERT_EQ(kLength, view.GetTxOctets());
- ASSERT_EQ(kTime, view.GetTxTime());
-}
-
-TEST_F(LeImplTest, LeSetDefaultSubrate) {
- le_impl_->LeSetDefaultSubrate(kIntervalMin, kIntervalMax, kLatency, kContinuationNumber,
- kTimeout);
- sync_handler();
- auto view = CreateAclCommandView<LeSetDefaultSubrateView>(hci_layer_->GetCommand());
- ASSERT_TRUE(view.IsValid());
- ASSERT_EQ(kIntervalMin, view.GetSubrateMin());
- ASSERT_EQ(kIntervalMax, view.GetSubrateMax());
- ASSERT_EQ(kLatency, view.GetMaxLatency());
- ASSERT_EQ(kContinuationNumber, view.GetContinuationNumber());
- ASSERT_EQ(kTimeout, view.GetSupervisionTimeout());
-}
-
enum class ConnectionCompleteType { CONNECTION_COMPLETE, ENHANCED_CONNECTION_COMPLETE };
class LeImplTestParameterizedByConnectionCompleteEventType
diff --git a/system/gd/hci/acl_manager/round_robin_scheduler.cc b/system/gd/hci/acl_manager/round_robin_scheduler.cc
index ad0e6dc621..89897a82e5 100644
--- a/system/gd/hci/acl_manager/round_robin_scheduler.cc
+++ b/system/gd/hci/acl_manager/round_robin_scheduler.cc
@@ -17,7 +17,6 @@
#include "hci/acl_manager/round_robin_scheduler.h"
#include <bluetooth/log.h>
-#include <com_android_bluetooth_flags.h>
#include <memory>
#include <utility>
@@ -65,10 +64,8 @@ void RoundRobinScheduler::Unregister(uint16_t handle) {
log::assert_that(acl_queue_handlers_.count(handle) == 1,
"assert failed: acl_queue_handlers_.count(handle) == 1");
- if (com::android::bluetooth::flags::drop_acl_fragment_on_disconnect()) {
- // Drop the pending fragments and recalculate number_of_sent_packets_
- drop_packet_fragments(handle);
- }
+ // Drop the pending fragments and recalculate number_of_sent_packets_
+ drop_packet_fragments(handle);
auto& acl_queue_handler = acl_queue_handlers_.find(handle)->second;
log::info("unregistering acl_queue handle={}, sent_packets={}", handle,
@@ -94,8 +91,7 @@ void RoundRobinScheduler::Unregister(uint16_t handle) {
starting_point_ = acl_queue_handlers_.begin();
// Restart sending packets if we got acl credits
- if (com::android::bluetooth::flags::drop_acl_fragment_on_disconnect() &&
- credits_reclaimed_from_zero) {
+ if (credits_reclaimed_from_zero) {
start_round_robin();
}
}
diff --git a/system/gd/hci/acl_manager/round_robin_scheduler_test.cc b/system/gd/hci/acl_manager/round_robin_scheduler_test.cc
index 25bd2aff80..b990332c9e 100644
--- a/system/gd/hci/acl_manager/round_robin_scheduler_test.cc
+++ b/system/gd/hci/acl_manager/round_robin_scheduler_test.cc
@@ -16,7 +16,6 @@
#include "hci/acl_manager/round_robin_scheduler.h"
-#include <com_android_bluetooth_flags.h>
#include <gtest/gtest.h>
#include "common/bidi_queue.h"
@@ -422,8 +421,6 @@ TEST_F(RoundRobinSchedulerTest, receive_le_credit_when_next_fragment_is_classic)
}
TEST_F(RoundRobinSchedulerTest, unregister_reclaim_credits) {
- com::android::bluetooth::flags::provider_->drop_acl_fragment_on_disconnect(true);
-
uint16_t handle = 0x01;
auto connection_queue = std::make_shared<AclConnection::Queue>(20);
auto new_connection_queue = std::make_shared<AclConnection::Queue>(20);
diff --git a/system/gd/hci/le_address_manager.cc b/system/gd/hci/le_address_manager.cc
index 804f88985e..327ea0f0ca 100644
--- a/system/gd/hci/le_address_manager.cc
+++ b/system/gd/hci/le_address_manager.cc
@@ -165,12 +165,8 @@ void LeAddressManager::SetPrivacyPolicyForInitiatorAddress(
min_seconds.count(), max_seconds.count());
enqueue_command_.Run(std::move(packet));
} else {
- if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) {
- address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_, true);
- address_rotation_non_wake_alarm_ = std::make_unique<os::Alarm>(handler_, false);
- } else {
- address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_);
- }
+ address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_, true);
+ address_rotation_non_wake_alarm_ = std::make_unique<os::Alarm>(handler_, false);
}
set_random_address();
break;
@@ -229,12 +225,8 @@ void LeAddressManager::SetPrivacyPolicyForInitiatorAddressForTest(
min_seconds.count(), max_seconds.count());
enqueue_command_.Run(std::move(packet));
} else {
- if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) {
- address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_, true);
- address_rotation_non_wake_alarm_ = std::make_unique<os::Alarm>(handler_, false);
- } else {
- address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_);
- }
+ address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_, true);
+ address_rotation_non_wake_alarm_ = std::make_unique<os::Alarm>(handler_, false);
set_random_address();
}
break;
@@ -422,31 +414,25 @@ void LeAddressManager::prepare_to_rotate() {
}
void LeAddressManager::schedule_rotate_random_address() {
- if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) {
- std::string client_name = "LeAddressManager";
- auto privateAddressIntervalRange = GetNextPrivateAddressIntervalRange(client_name);
- address_rotation_wake_alarm_->Schedule(
- common::BindOnce(
- []() { log::info("deadline wakeup in schedule_rotate_random_address"); }),
- privateAddressIntervalRange.max);
- address_rotation_non_wake_alarm_->Schedule(
- common::BindOnce(&LeAddressManager::prepare_to_rotate, common::Unretained(this)),
- privateAddressIntervalRange.min);
-
- auto now = std::chrono::system_clock::now();
- if (address_rotation_interval_min.has_value()) {
- CheckAddressRotationHappenedInExpectedTimeInterval(
- *address_rotation_interval_min, *address_rotation_interval_max, now, client_name);
- }
-
- // Update the expected range here.
- address_rotation_interval_min.emplace(now + privateAddressIntervalRange.min);
- address_rotation_interval_max.emplace(now + privateAddressIntervalRange.max);
- } else {
- address_rotation_wake_alarm_->Schedule(
- common::BindOnce(&LeAddressManager::prepare_to_rotate, common::Unretained(this)),
- GetNextPrivateAddressIntervalMs());
+ std::string client_name = "LeAddressManager";
+ auto privateAddressIntervalRange = GetNextPrivateAddressIntervalRange(client_name);
+ address_rotation_wake_alarm_->Schedule(
+ common::BindOnce(
+ []() { log::info("deadline wakeup in schedule_rotate_random_address"); }),
+ privateAddressIntervalRange.max);
+ address_rotation_non_wake_alarm_->Schedule(
+ common::BindOnce(&LeAddressManager::prepare_to_rotate, common::Unretained(this)),
+ privateAddressIntervalRange.min);
+
+ auto now = std::chrono::system_clock::now();
+ if (address_rotation_interval_min.has_value()) {
+ CheckAddressRotationHappenedInExpectedTimeInterval(
+ *address_rotation_interval_min, *address_rotation_interval_max, now, client_name);
}
+
+ // Update the expected range here.
+ address_rotation_interval_min.emplace(now + privateAddressIntervalRange.min);
+ address_rotation_interval_max.emplace(now + privateAddressIntervalRange.max);
}
void LeAddressManager::set_random_address() {
diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc
index f9a80f44f8..d626b2ee15 100644
--- a/system/gd/hci/le_advertising_manager.cc
+++ b/system/gd/hci/le_advertising_manager.cc
@@ -374,39 +374,29 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
log::info("Reenable advertising");
if (was_rotating_address) {
log::info("Scheduling address rotation for advertiser_id={}", advertiser_id);
- if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) {
- advertising_sets_[advertiser_id].address_rotation_wake_alarm_ =
- std::make_unique<os::Alarm>(module_handler_, true);
- advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_ =
- std::make_unique<os::Alarm>(module_handler_, false);
-
- std::string client_name = "advertising_set_" + std::to_string(advertiser_id);
- auto privateAddressIntervalRange =
- le_address_manager_->GetNextPrivateAddressIntervalRange(client_name);
-
- advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule(
- common::BindOnce(
- []() { log::info("deadline wakeup in handle_set_terminated"); }),
- privateAddressIntervalRange.max);
- advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_->Schedule(
- common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
- common::Unretained(this), advertiser_id),
- privateAddressIntervalRange.min);
-
- // Update the expected range here.
- auto now = std::chrono::system_clock::now();
- advertising_sets_[advertiser_id].address_rotation_interval_min.emplace(
- now + privateAddressIntervalRange.min);
- advertising_sets_[advertiser_id].address_rotation_interval_max.emplace(
- now + privateAddressIntervalRange.max);
- } else {
- advertising_sets_[advertiser_id].address_rotation_wake_alarm_ =
- std::make_unique<os::Alarm>(module_handler_);
- advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule(
- common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
- common::Unretained(this), advertiser_id),
- le_address_manager_->GetNextPrivateAddressIntervalMs());
- }
+ advertising_sets_[advertiser_id].address_rotation_wake_alarm_ =
+ std::make_unique<os::Alarm>(module_handler_, true);
+ advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_ =
+ std::make_unique<os::Alarm>(module_handler_, false);
+
+ std::string client_name = "advertising_set_" + std::to_string(advertiser_id);
+ auto privateAddressIntervalRange =
+ le_address_manager_->GetNextPrivateAddressIntervalRange(client_name);
+
+ advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule(
+ common::BindOnce([]() { log::info("deadline wakeup in handle_set_terminated"); }),
+ privateAddressIntervalRange.max);
+ advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_->Schedule(
+ common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
+ common::Unretained(this), advertiser_id),
+ privateAddressIntervalRange.min);
+
+ // Update the expected range here.
+ auto now = std::chrono::system_clock::now();
+ advertising_sets_[advertiser_id].address_rotation_interval_min.emplace(
+ now + privateAddressIntervalRange.min);
+ advertising_sets_[advertiser_id].address_rotation_interval_max.emplace(
+ now + privateAddressIntervalRange.max);
}
enable_advertiser(advertiser_id, true, 0, 0);
}
@@ -673,40 +663,31 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
!leaudio_requested_nrpa && (!controller_->IsRpaGenerationSupported())) {
// start timer for random address
log::info("Scheduling address rotation for advertiser_id={}", id);
- if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) {
- advertising_sets_[id].address_rotation_wake_alarm_ =
- std::make_unique<os::Alarm>(module_handler_, true);
- advertising_sets_[id].address_rotation_non_wake_alarm_ =
- std::make_unique<os::Alarm>(module_handler_, false);
-
- std::string client_name = "advertising_set_" + std::to_string(id);
- auto privateAddressIntervalRange =
- le_address_manager_->GetNextPrivateAddressIntervalRange(client_name);
-
- advertising_sets_[id].address_rotation_wake_alarm_->Schedule(
- common::BindOnce([]() {
- log::info("deadline wakeup in create_extended_advertiser_with_id");
- }),
- privateAddressIntervalRange.max);
- advertising_sets_[id].address_rotation_non_wake_alarm_->Schedule(
- common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
- common::Unretained(this), id),
- privateAddressIntervalRange.min);
-
- // Update the expected range here.
- auto now = std::chrono::system_clock::now();
- advertising_sets_[id].address_rotation_interval_min.emplace(
- now + privateAddressIntervalRange.min);
- advertising_sets_[id].address_rotation_interval_max.emplace(
- now + privateAddressIntervalRange.max);
- } else {
- advertising_sets_[id].address_rotation_wake_alarm_ =
- std::make_unique<os::Alarm>(module_handler_);
- advertising_sets_[id].address_rotation_wake_alarm_->Schedule(
- common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
- common::Unretained(this), id),
- le_address_manager_->GetNextPrivateAddressIntervalMs());
- }
+ advertising_sets_[id].address_rotation_wake_alarm_ =
+ std::make_unique<os::Alarm>(module_handler_, true);
+ advertising_sets_[id].address_rotation_non_wake_alarm_ =
+ std::make_unique<os::Alarm>(module_handler_, false);
+
+ std::string client_name = "advertising_set_" + std::to_string(id);
+ auto privateAddressIntervalRange =
+ le_address_manager_->GetNextPrivateAddressIntervalRange(client_name);
+
+ advertising_sets_[id].address_rotation_wake_alarm_->Schedule(
+ common::BindOnce([]() {
+ log::info("deadline wakeup in create_extended_advertiser_with_id");
+ }),
+ privateAddressIntervalRange.max);
+ advertising_sets_[id].address_rotation_non_wake_alarm_->Schedule(
+ common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
+ common::Unretained(this), id),
+ privateAddressIntervalRange.min);
+
+ // Update the expected range here.
+ auto now = std::chrono::system_clock::now();
+ advertising_sets_[id].address_rotation_interval_min.emplace(
+ now + privateAddressIntervalRange.min);
+ advertising_sets_[id].address_rotation_interval_max.emplace(
+ now + privateAddressIntervalRange.max);
}
}
if (config.advertising_type == AdvertisingType::ADV_IND ||
@@ -859,39 +840,31 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
}
log::info("Scheduling address rotation for advertiser_id={}", advertiser_id);
- if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) {
- std::string client_name = "advertising_set_" + std::to_string(advertiser_id);
- auto privateAddressIntervalRange =
- le_address_manager_->GetNextPrivateAddressIntervalRange(client_name);
- advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule(
- common::BindOnce([]() {
- log::info("deadline wakeup in set_advertising_set_random_address_on_timer");
- }),
- privateAddressIntervalRange.max);
- advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_->Schedule(
- common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
- common::Unretained(this), advertiser_id),
- privateAddressIntervalRange.min);
-
- auto now = std::chrono::system_clock::now();
- if (advertising_sets_[advertiser_id].address_rotation_interval_min.has_value()) {
- le_address_manager_->CheckAddressRotationHappenedInExpectedTimeInterval(
- *(advertising_sets_[advertiser_id].address_rotation_interval_min),
- *(advertising_sets_[advertiser_id].address_rotation_interval_max), now,
- client_name);
- }
-
- // Update the expected range here.
- advertising_sets_[advertiser_id].address_rotation_interval_min.emplace(
- now + privateAddressIntervalRange.min);
- advertising_sets_[advertiser_id].address_rotation_interval_max.emplace(
- now + privateAddressIntervalRange.max);
- } else {
- advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule(
- common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
- common::Unretained(this), advertiser_id),
- le_address_manager_->GetNextPrivateAddressIntervalMs());
+ std::string client_name = "advertising_set_" + std::to_string(advertiser_id);
+ auto privateAddressIntervalRange =
+ le_address_manager_->GetNextPrivateAddressIntervalRange(client_name);
+ advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule(
+ common::BindOnce([]() {
+ log::info("deadline wakeup in set_advertising_set_random_address_on_timer");
+ }),
+ privateAddressIntervalRange.max);
+ advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_->Schedule(
+ common::BindOnce(&impl::set_advertising_set_random_address_on_timer,
+ common::Unretained(this), advertiser_id),
+ privateAddressIntervalRange.min);
+
+ auto now = std::chrono::system_clock::now();
+ if (advertising_sets_[advertiser_id].address_rotation_interval_min.has_value()) {
+ le_address_manager_->CheckAddressRotationHappenedInExpectedTimeInterval(
+ *(advertising_sets_[advertiser_id].address_rotation_interval_min),
+ *(advertising_sets_[advertiser_id].address_rotation_interval_max), now, client_name);
}
+
+ // Update the expected range here.
+ advertising_sets_[advertiser_id].address_rotation_interval_min.emplace(
+ now + privateAddressIntervalRange.min);
+ advertising_sets_[advertiser_id].address_rotation_interval_max.emplace(
+ now + privateAddressIntervalRange.max);
}
void register_advertiser(
diff --git a/system/gd/module.cc b/system/gd/module.cc
index 8904f0c559..76b386bbfe 100644
--- a/system/gd/module.cc
+++ b/system/gd/module.cc
@@ -128,10 +128,18 @@ void ModuleRegistry::StopAll() {
auto module = Get(*it);
last_instance_ = "stopping " + module->ToString();
- // Clear the handler before stopping the module to allow it to shut down gracefully.
- log::info("Stopping Handler of Module {}", module->ToString());
- module->handler_->Clear();
- module->handler_->WaitUntilStopped(kModuleStopTimeout);
+ /*
+ * b/393449774 since we have now shifted to a single handler for all modules, we don't need
+ * to clear the handler here, it will be done in the respective teardown.
+ * Since we have a single handler, we need to make sure that the handler instance is deleted
+ * only once, otherwise we will see a crash as a handler can only be cleared once.
+ */
+ if (!com::android::bluetooth::flags::same_handler_for_all_modules()) {
+ // Clear the handler before stopping the module to allow it to shut down gracefully.
+ log::info("Stopping Handler of Module {}", module->ToString());
+ module->handler_->Clear();
+ module->handler_->WaitUntilStopped(kModuleStopTimeout);
+ }
log::info("Stopping Module {}", module->ToString());
module->Stop();
}
@@ -144,7 +152,9 @@ void ModuleRegistry::StopAll() {
auto instance = started_modules_.find(*it);
log::assert_that(instance != started_modules_.end(),
"assert failed: instance != started_modules_.end()");
- delete instance->second->handler_;
+ if (!com::android::bluetooth::flags::same_handler_for_all_modules()) {
+ delete instance->second->handler_;
+ }
delete instance->second;
started_modules_.erase(instance);
}
@@ -165,4 +175,15 @@ os::Handler* ModuleRegistry::GetModuleHandler(const ModuleFactory* module) const
return nullptr;
}
+// Override the StopAll method to use the test thread and handler.
+// This function will take care of releasing the handler instances.
+void TestModuleRegistry::StopAll() {
+ os::Handler* handler = GetTestHandler();
+ handler->Clear();
+ if (com::android::bluetooth::flags::same_handler_for_all_modules()) {
+ handler->WaitUntilStopped(kHandlerStopTimeout);
+ }
+ ModuleRegistry::StopAll(); // call the base class StopAll
+ delete handler;
+}
} // namespace bluetooth
diff --git a/system/gd/module.h b/system/gd/module.h
index 816d455482..57e4db7d16 100644
--- a/system/gd/module.h
+++ b/system/gd/module.h
@@ -32,6 +32,9 @@
#include "os/thread.h"
namespace bluetooth {
+// Timeout for waiting for a handler to stop, used in Handler::WaitUntilStopped()
+constexpr std::chrono::milliseconds kHandlerStopTimeout = std::chrono::milliseconds(2000);
+
namespace shim {
class Stack;
} // namespace shim
@@ -191,6 +194,9 @@ public:
os::Thread& GetTestThread() { return test_thread; }
os::Handler* GetTestHandler() { return test_handler_; }
+ // Override the StopAll method to use the test thread and handler.
+ void StopAll();
+
bool SynchronizeModuleHandler(const ModuleFactory* module,
std::chrono::milliseconds timeout) const {
return SynchronizeHandler(GetTestModuleHandler(module), timeout);
diff --git a/system/gd/module_unittest.cc b/system/gd/module_unittest.cc
index d1bf59ac73..8fb140c229 100644
--- a/system/gd/module_unittest.cc
+++ b/system/gd/module_unittest.cc
@@ -22,6 +22,7 @@
#include <sstream>
#include <string>
+#include "com_android_bluetooth_flags.h"
#include "gtest/gtest.h"
#include "os/handler.h"
#include "os/thread.h"
@@ -41,9 +42,12 @@ protected:
void TearDown() override {
handler_->Clear();
+ if (com::android::bluetooth::flags::same_handler_for_all_modules()) {
+ handler_->WaitUntilStopped(kHandlerStopTimeout);
+ }
delete registry_;
- delete thread_;
delete handler_;
+ delete thread_;
}
ModuleRegistry* registry_;
diff --git a/system/gd/os/linux_generic/alarm.cc b/system/gd/os/linux_generic/alarm.cc
index 8846ede579..a378ca93cd 100644
--- a/system/gd/os/linux_generic/alarm.cc
+++ b/system/gd/os/linux_generic/alarm.cc
@@ -17,7 +17,6 @@
#include "os/alarm.h"
#include <bluetooth/log.h>
-#include <com_android_bluetooth_flags.h>
#include <sys/timerfd.h>
#include <unistd.h>
@@ -41,8 +40,7 @@ using common::OnceClosure;
Alarm::Alarm(Handler* handler) : Alarm(handler, true) {}
Alarm::Alarm(Handler* handler, bool isWakeAlarm) : handler_(handler) {
- int timerfd_flag =
- com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation() ? TFD_NONBLOCK : 0;
+ int timerfd_flag = TFD_NONBLOCK;
fd_ = TIMERFD_CREATE(isWakeAlarm ? ALARM_CLOCK : CLOCK_BOOTTIME, timerfd_flag);
@@ -85,7 +83,7 @@ void Alarm::on_fire() {
auto bytes_read = read(fd_, &times_invoked, sizeof(uint64_t));
lock.unlock();
- if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation() && bytes_read == -1) {
+ if (bytes_read == -1) {
log::debug("No data to read.");
if (errno == EAGAIN || errno == EWOULDBLOCK) {
log::debug("Alarm is already canceled or rescheduled.");
diff --git a/system/main/shim/acl.cc b/system/main/shim/acl.cc
index b2f0e23975..b01bd1f92d 100644
--- a/system/main/shim/acl.cc
+++ b/system/main/shim/acl.cc
@@ -969,11 +969,6 @@ struct shim::Acl::impl {
maximum_latency, minimum_remote_timeout, minimum_local_timeout);
}
- void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t sup_tout) {
- GetAclManager()->LeSetDefaultSubrate(subrate_min, subrate_max, max_latency, cont_num, sup_tout);
- }
-
void LeSubrateRequest(HciHandle handle, uint16_t subrate_min, uint16_t subrate_max,
uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout) {
if (IsLeAcl(handle)) {
@@ -1573,12 +1568,6 @@ void shim::Acl::UpdateConnectionParameters(uint16_t handle, uint16_t conn_int_mi
conn_int_max, conn_latency, conn_timeout, min_ce_len, max_ce_len);
}
-void shim::Acl::LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max,
- uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout) {
- handler_->CallOn(pimpl_.get(), &Acl::impl::LeSetDefaultSubrate, subrate_min, subrate_max,
- max_latency, cont_num, sup_tout);
-}
-
void shim::Acl::LeSubrateRequest(uint16_t hci_handle, uint16_t subrate_min, uint16_t subrate_max,
uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout) {
handler_->CallOn(pimpl_.get(), &Acl::impl::LeSubrateRequest, hci_handle, subrate_min, subrate_max,
diff --git a/system/main/shim/acl.h b/system/main/shim/acl.h
index bbe15d0acf..8253faf013 100644
--- a/system/main/shim/acl.h
+++ b/system/main/shim/acl.h
@@ -80,8 +80,6 @@ public:
void RemoveFromAddressResolution(const hci::AddressWithType& address_with_type);
void ClearAddressResolution();
- void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t sup_tout);
void LeSubrateRequest(uint16_t hci_handle, uint16_t subrate_min, uint16_t subrate_max,
uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout);
diff --git a/system/main/shim/acl_api.cc b/system/main/shim/acl_api.cc
index 56e63e1977..0868f40f2c 100644
--- a/system/main/shim/acl_api.cc
+++ b/system/main/shim/acl_api.cc
@@ -166,12 +166,6 @@ void bluetooth::shim::ACL_ClearAddressResolution() {
void bluetooth::shim::ACL_ClearFilterAcceptList() {
Stack::GetInstance()->GetAcl()->ClearFilterAcceptList();
}
-void bluetooth::shim::ACL_LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max,
- uint16_t max_latency, uint16_t cont_num,
- uint16_t sup_tout) {
- Stack::GetInstance()->GetAcl()->LeSetDefaultSubrate(subrate_min, subrate_max, max_latency,
- cont_num, sup_tout);
-}
void bluetooth::shim::ACL_LeSubrateRequest(uint16_t hci_handle, uint16_t subrate_min,
uint16_t subrate_max, uint16_t max_latency,
diff --git a/system/main/shim/acl_api.h b/system/main/shim/acl_api.h
index 6b28988ca6..b0bac16e02 100644
--- a/system/main/shim/acl_api.h
+++ b/system/main/shim/acl_api.h
@@ -49,8 +49,6 @@ void ACL_AddToAddressResolution(const tBLE_BD_ADDR& legacy_address_with_type,
void ACL_RemoveFromAddressResolution(const tBLE_BD_ADDR& legacy_address_with_type);
void ACL_ClearAddressResolution();
void ACL_ClearFilterAcceptList();
-void ACL_LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t sup_tout);
void ACL_SendConnectionParameterUpdateRequest(uint16_t handle, uint16_t conn_int_min,
uint16_t conn_int_max, uint16_t conn_latency,
uint16_t conn_timeout, uint16_t min_ce_len,
diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc
index 14447d75b3..4560a6180f 100644
--- a/system/main/shim/stack.cc
+++ b/system/main/shim/stack.cc
@@ -170,6 +170,9 @@ void Stack::Stop() {
is_running_ = false;
stack_handler_->Clear();
+ if(com::android::bluetooth::flags::same_handler_for_all_modules()) {
+ stack_handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout);
+ }
WakelockManager::Get().Acquire();
diff --git a/system/stack/btm/btm_ble_sec.cc b/system/stack/btm/btm_ble_sec.cc
index cef553c79e..b8e2002c60 100644
--- a/system/stack/btm/btm_ble_sec.cc
+++ b/system/stack/btm/btm_ble_sec.cc
@@ -1662,6 +1662,13 @@ static bool btm_ble_complete_evt_ignore(const tBTM_SEC_DEV_REC* p_dev_rec,
p_dev_rec->bd_addr);
l2cu_start_post_bond_timer(p_dev_rec->ble_hci_handle);
return true;
+ } else if (com::android::bluetooth::flags::le_peripheral_enc_failure() &&
+ !p_dev_rec->role_central) {
+ log::warn("Peripheral encryption request failed for the bonded device {} with reason {}",
+ p_dev_rec->bd_addr, smp_status_text(p_data->complt.reason));
+ btm_sec_disconnect(p_dev_rec->ble_hci_handle, HCI_ERR_AUTH_FAILURE,
+ smp_status_text(p_data->complt.reason));
+ return true;
}
}
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index 439fa7bc0f..6244b951c6 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -3130,12 +3130,12 @@ void btm_sec_auth_complete(uint16_t handle, tHCI_STATUS status) {
p_dev_rec->sec_rec.security_required &= ~BTM_SEC_OUT_AUTHENTICATE;
if (status != HCI_SUCCESS) {
- if ((status != HCI_ERR_PEER_USER) && (status != HCI_ERR_CONN_CAUSE_LOCAL_HOST)) {
+ if (status != HCI_ERR_PEER_USER && status != HCI_ERR_CONN_CAUSE_LOCAL_HOST) {
btm_sec_send_hci_disconnect(
p_dev_rec, HCI_ERR_PEER_USER, p_dev_rec->hci_handle,
"stack::btm::btm_sec::btm_sec_auth_retry Auth fail while bonding");
}
- } else {
+ } else if (!com::android::bluetooth::flags::immediate_encryption_after_pairing()) {
BTM_LogHistory(kBtmLogTag, p_dev_rec->bd_addr, "Bonding completed",
hci_error_code_text(status));
@@ -3160,6 +3160,11 @@ void btm_sec_auth_complete(uint16_t handle, tHCI_STATUS status) {
}
l2cu_start_post_bond_timer(p_dev_rec->hci_handle);
+ } else {
+ BTM_LogHistory(kBtmLogTag, p_dev_rec->bd_addr, "Bonding completed",
+ hci_error_code_text(status));
+ BTM_SetEncryption(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, NULL, NULL, BTM_BLE_SEC_NONE);
+ l2cu_start_post_bond_timer(p_dev_rec->hci_handle);
}
return;
diff --git a/system/stack/include/l2cap_interface.h b/system/stack/include/l2cap_interface.h
index b1551d2a80..19f85d4d2e 100644
--- a/system/stack/include/l2cap_interface.h
+++ b/system/stack/include/l2cap_interface.h
@@ -871,21 +871,6 @@ public:
virtual void L2CA_SetEcosystemBaseInterval(uint32_t base_interval) = 0;
/*******************************************************************************
- *
- * Function L2CA_SetDefaultSubrate
- *
- * Description BLE Set Default Subrate.
- *
- * Parameters: Subrate parameters
- *
- * Return value: void
- *
- ******************************************************************************/
- virtual void L2CA_SetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max,
- uint16_t max_latency, uint16_t cont_num,
- uint16_t timeout) = 0;
-
- /*******************************************************************************
**
** Function L2CA_SetMediaStreamChannel
**
diff --git a/system/stack/l2cap/internal/l2c_api.h b/system/stack/l2cap/internal/l2c_api.h
index dd1dbf52e6..989eccf50e 100644
--- a/system/stack/l2cap/internal/l2c_api.h
+++ b/system/stack/l2cap/internal/l2c_api.h
@@ -719,20 +719,6 @@ void L2CA_SetEcosystemBaseInterval(uint32_t base_interval);
/*******************************************************************************
*
- * Function L2CA_SetDefaultSubrate
- *
- * Description BLE Set Default Subrate.
- *
- * Parameters: Subrate parameters
- *
- * Return value: void
- *
- ******************************************************************************/
-void L2CA_SetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t timeout);
-
-/*******************************************************************************
- *
* Function L2CA_SubrateRequest
*
* Description BLE Subrate request.
diff --git a/system/stack/l2cap/l2c_api.h b/system/stack/l2cap/l2c_api.h
index 1ff09ffaf5..f9eab67d55 100644
--- a/system/stack/l2cap/l2c_api.h
+++ b/system/stack/l2cap/l2c_api.h
@@ -73,8 +73,6 @@ public:
uint8_t* p_chnl_mask) override;
[[nodiscard]] bool L2CA_SetAclPriority(const RawAddress& bd_addr,
tL2CAP_PRIORITY priority) override;
- void L2CA_SetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t timeout) override;
void L2CA_AdjustConnectionIntervals(uint16_t* min_interval, uint16_t* max_interval,
uint16_t floor_interval) override;
void L2CA_SetEcosystemBaseInterval(uint32_t base_interval) override;
diff --git a/system/stack/l2cap/l2c_ble_conn_params.cc b/system/stack/l2cap/l2c_ble_conn_params.cc
index 49eeb06155..392b1f5a74 100644
--- a/system/stack/l2cap/l2c_ble_conn_params.cc
+++ b/system/stack/l2cap/l2c_ble_conn_params.cc
@@ -510,26 +510,6 @@ static void l2cble_start_subrate_change(tL2C_LCB* p_lcb) {
/*******************************************************************************
*
- * Function L2CA_SetDefaultSubrate
- *
- * Description BLE Set Default Subrate
- *
- * Parameters: Subrate parameters
- *
- * Return value: void
- *
- ******************************************************************************/
-void L2CA_SetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t timeout) {
- log::verbose("subrate_min={}, subrate_max={}, max_latency={}, cont_num={}, timeout={}",
- subrate_min, subrate_max, max_latency, cont_num, timeout);
-
- bluetooth::shim::ACL_LeSetDefaultSubrate(subrate_min, subrate_max, max_latency, cont_num,
- timeout);
-}
-
-/*******************************************************************************
- *
* Function L2CA_SubrateRequest
*
* Description BLE Subrate request.
diff --git a/system/stack/l2cap/l2cap_api.cc b/system/stack/l2cap/l2cap_api.cc
index 0752c995c0..e9ccaeaf2a 100644
--- a/system/stack/l2cap/l2cap_api.cc
+++ b/system/stack/l2cap/l2cap_api.cc
@@ -234,13 +234,6 @@ void bluetooth::stack::l2cap::Impl::L2CA_SetEcosystemBaseInterval(uint32_t base_
return ::L2CA_IsLinkEstablished(bd_addr, transport);
}
-void bluetooth::stack::l2cap::Impl::L2CA_SetDefaultSubrate(uint16_t subrate_min,
- uint16_t subrate_max,
- uint16_t max_latency, uint16_t cont_num,
- uint16_t timeout) {
- ::L2CA_SetDefaultSubrate(subrate_min, subrate_max, max_latency, cont_num, timeout);
-}
-
[[nodiscard]] bool bluetooth::stack::l2cap::Impl::L2CA_SubrateRequest(
const RawAddress& rem_bda, uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
uint16_t cont_num, uint16_t timeout) {
diff --git a/system/test/mock/mock_main_shim_acl.cc b/system/test/mock/mock_main_shim_acl.cc
index a98e809ad0..5f58860347 100644
--- a/system/test/mock/mock_main_shim_acl.cc
+++ b/system/test/mock/mock_main_shim_acl.cc
@@ -129,12 +129,6 @@ void shim::Acl::DisconnectLe(uint16_t /* handle */, tHCI_STATUS /* reason */,
inc_func_call_count(__func__);
}
-void shim::Acl::LeSetDefaultSubrate(uint16_t /* subrate_min */, uint16_t /* subrate_max */,
- uint16_t /* max_latency */, uint16_t /* cont_num */,
- uint16_t /* sup_tout */) {
- inc_func_call_count(__func__);
-}
-
void shim::Acl::LeSubrateRequest(uint16_t /* hci_handle */, uint16_t /* subrate_min */,
uint16_t /* subrate_max */, uint16_t /* max_latency */,
uint16_t /* cont_num */, uint16_t /* sup_tout */) {
diff --git a/system/test/mock/mock_main_shim_acl_api.cc b/system/test/mock/mock_main_shim_acl_api.cc
index 3d257f1a5a..e3a3c9d63b 100644
--- a/system/test/mock/mock_main_shim_acl_api.cc
+++ b/system/test/mock/mock_main_shim_acl_api.cc
@@ -73,12 +73,6 @@ void bluetooth::shim::ACL_RemoveFromAddressResolution(
inc_func_call_count(__func__);
}
void bluetooth::shim::ACL_ClearAddressResolution() { inc_func_call_count(__func__); }
-void bluetooth::shim::ACL_LeSetDefaultSubrate(uint16_t /* subrate_min */,
- uint16_t /* subrate_max */,
- uint16_t /* max_latency */, uint16_t /* cont_num */,
- uint16_t /* sup_tout */) {
- inc_func_call_count(__func__);
-}
void bluetooth::shim::ACL_LeSubrateRequest(uint16_t /* hci_handle */, uint16_t /* subrate_min */,
uint16_t /* subrate_max */, uint16_t /* max_latency */,
uint16_t /* cont_num */, uint16_t /* sup_tout */) {
diff --git a/system/test/mock/mock_main_shim_acl_api.h b/system/test/mock/mock_main_shim_acl_api.h
index ab891673e3..29e27dbad3 100644
--- a/system/test/mock/mock_main_shim_acl_api.h
+++ b/system/test/mock/mock_main_shim_acl_api.h
@@ -84,21 +84,6 @@ struct ACL_ClearFilterAcceptList {
};
extern struct ACL_ClearFilterAcceptList ACL_ClearFilterAcceptList;
-// Name: ACL_LeSetDefaultSubrate
-// Params:
-// Return: void
-struct ACL_LeSetDefaultSubrate {
- std::function<void(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t sup_tout)>
- body{[](uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t sup_tout) {}};
- void operator()(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency,
- uint16_t cont_num, uint16_t sup_tout) {
- body(subrate_min, subrate_max, max_latency, cont_num, sup_tout);
- }
-};
-extern struct ACL_LeSetDefaultSubrate ACL_LeSetDefaultSubrate;
-
// Name: ACL_LeSubrateRequest
// Params:
// Return: void
diff --git a/system/test/mock/mock_stack_l2cap_interface.h b/system/test/mock/mock_stack_l2cap_interface.h
index a5cd536777..b2fc3e0ae9 100644
--- a/system/test/mock/mock_stack_l2cap_interface.h
+++ b/system/test/mock/mock_stack_l2cap_interface.h
@@ -64,9 +64,6 @@ public:
MOCK_METHOD(bool, L2CA_GetPeerFeatures,
(const RawAddress& bd_addr, uint32_t* p_ext_feat, uint8_t* p_chnl_mask));
MOCK_METHOD(bool, L2CA_SetAclPriority, (const RawAddress& bd_addr, tL2CAP_PRIORITY priority));
- MOCK_METHOD(void, L2CA_SetDefaultSubrate,
- (uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, uint16_t cont_num,
- uint16_t timeout));
MOCK_METHOD(void, L2CA_AdjustConnectionIntervals,
(uint16_t* min_interval, uint16_t* max_interval, uint16_t floor_interval));
MOCK_METHOD(void, L2CA_SetEcosystemBaseInterval, (uint32_t base_interval));