summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AndroidTestTemplate.xml15
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterService.java16
-rw-r--r--android/app/src/com/android/bluetooth/le_audio/LeAudioService.java39
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java161
-rw-r--r--system/bta/csis/csis_client.cc12
-rw-r--r--system/bta/hearing_aid/hearing_aid.cc4
-rw-r--r--system/bta/le_audio/client.cc13
-rw-r--r--system/bta/vc/vc.cc13
-rw-r--r--system/gd/AndroidTestTemplate.xml15
-rw-r--r--system/test/Android.bp1
10 files changed, 250 insertions, 39 deletions
diff --git a/AndroidTestTemplate.xml b/AndroidTestTemplate.xml
index 601be93108..4fb4bf9f76 100644
--- a/AndroidTestTemplate.xml
+++ b/AndroidTestTemplate.xml
@@ -17,10 +17,11 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
- </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="settings put global ble_scan_always_enabled 0" />
<option name="run-command" value="svc bluetooth disable" />
@@ -33,4 +34,10 @@
<option name="module-name" value="{MODULE}" />
<option name="run-test-as" value="0" />
</test>
+
+ <!-- Only run tests in MTS if the Bluetooth Mainline module is installed. -->
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.bluetooth" />
+ </object>
</configuration>
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index b60b01ff81..e0185f8b6f 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -148,13 +148,14 @@ import java.time.Duration;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
@@ -282,8 +283,7 @@ public class AdapterService extends Service {
private boolean mQuietmode = false;
private HashMap<String, CallerInfo> mBondAttemptCallerInfo = new HashMap<>();
- private final Map<UUID, RfcommListenerData> mBluetoothServerSockets =
- Collections.synchronizedMap(new HashMap<>());
+ private final Map<UUID, RfcommListenerData> mBluetoothServerSockets = new ConcurrentHashMap<>();
private final Executor mSocketServersExecutor = r -> new Thread(r).start();
private AlarmManager mAlarmManager;
@@ -1491,11 +1491,11 @@ public class AdapterService extends Service {
}
private void stopRfcommServerSockets() {
- synchronized (mBluetoothServerSockets) {
- mBluetoothServerSockets.forEach((key, value) -> {
- mBluetoothServerSockets.remove(key);
- value.closeServerAndPendingSockets(mHandler);
- });
+ Iterator<Map.Entry<UUID, RfcommListenerData>> socketsIterator =
+ mBluetoothServerSockets.entrySet().iterator();
+ while (socketsIterator.hasNext()) {
+ socketsIterator.next().getValue().closeServerAndPendingSockets(mHandler);
+ socketsIterator.remove();
}
}
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 42c4c5aeeb..33fa96f3c6 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -139,7 +139,7 @@ public class LeAudioService extends ProfileService {
mIsActive = false;
mActiveContexts = ACTIVE_CONTEXTS_NONE;
mCodecStatus = null;
- mLostDevicesWhileStreaming = new ArrayList<>();
+ mLostLeadDeviceWhileStreaming = null;
}
public Boolean mIsConnected;
@@ -147,7 +147,7 @@ public class LeAudioService extends ProfileService {
public Integer mActiveContexts;
public BluetoothLeAudioCodecStatus mCodecStatus;
/* This can be non empty only for the streaming time */
- List<BluetoothDevice> mLostDevicesWhileStreaming;
+ BluetoothDevice mLostLeadDeviceWhileStreaming;
}
List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>();
@@ -1066,18 +1066,19 @@ public class LeAudioService extends ProfileService {
}
private void clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor) {
- for (BluetoothDevice dev : descriptor.mLostDevicesWhileStreaming) {
- LeAudioStateMachine sm = mStateMachines.get(dev);
- if (sm == null) {
- continue;
- }
+ if (DBG) {
+ Log.d(TAG, " lost dev: " + descriptor.mLostLeadDeviceWhileStreaming);
+ }
+ LeAudioStateMachine sm = mStateMachines.get(descriptor.mLostLeadDeviceWhileStreaming);
+ if (sm != null) {
LeAudioStackEvent stackEvent =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
- stackEvent.device = dev;
+ stackEvent.device = descriptor.mLostLeadDeviceWhileStreaming;
stackEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED;
sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent);
}
+ descriptor.mLostLeadDeviceWhileStreaming = null;
}
// Suppressed since this is part of a local process
@@ -1110,14 +1111,17 @@ public class LeAudioService extends ProfileService {
mActiveAudioOutDevice)
|| Objects.equals(device, mActiveAudioInDevice))
&& (getConnectedPeerDevices(groupId).size() > 1)) {
- descriptor.mLostDevicesWhileStreaming.add(device);
+
+ if (DBG) Log.d(TAG, "Adding to lost devices : " + device);
+ descriptor.mLostLeadDeviceWhileStreaming = device;
return;
}
break;
case LeAudioStackEvent.CONNECTION_STATE_CONNECTED:
case LeAudioStackEvent.CONNECTION_STATE_CONNECTING:
if (descriptor != null) {
- descriptor.mLostDevicesWhileStreaming.remove(device);
+ if (DBG) Log.d(TAG, "Removing from lost devices : " + device);
+ descriptor.mLostLeadDeviceWhileStreaming = null;
/* Try to connect other devices from the group */
connectSet(device);
}
@@ -1254,6 +1258,7 @@ public class LeAudioService extends ProfileService {
ACTIVE_CONTEXTS_NONE, descriptor.mIsActive);
notifyGroupStatus = true;
/* Clear lost devices */
+ if (DBG) Log.d(TAG, "Clear for group: " + groupId);
clearLostDevicesWhileStreaming(descriptor);
}
} else {
@@ -1538,6 +1543,15 @@ public class LeAudioService extends ProfileService {
return;
}
+ List<BluetoothDevice> connectedDevices = getConnectedPeerDevices(myGroupId);
+ /* Let's check if the last connected device is really connected */
+ if (connectedDevices.size() == 1
+ && Objects.equals(connectedDevices.get(0),
+ descriptor.mLostLeadDeviceWhileStreaming)) {
+ clearLostDevicesWhileStreaming(descriptor);
+ return;
+ }
+
if (getConnectedPeerDevices(myGroupId).isEmpty()){
descriptor.mIsConnected = false;
if (descriptor.mIsActive) {
@@ -2584,9 +2598,8 @@ public class LeAudioService extends ProfileService {
ProfileService.println(sb, " mActiveContexts: " + descriptor.mActiveContexts);
ProfileService.println(sb, " group lead: " + getConnectedGroupLeadDevice(groupId));
ProfileService.println(sb, " first device: " + getFirstDeviceFromGroup(groupId));
- for (BluetoothDevice dev : descriptor.mLostDevicesWhileStreaming) {
- ProfileService.println(sb, " lost device: " + dev);
- }
+ ProfileService.println(sb, " lost lead device: "
+ + descriptor.mLostLeadDeviceWhileStreaming);
}
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
index 8a8901e9e0..ae41ce046c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
@@ -26,7 +26,11 @@ import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -42,6 +46,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
+import android.media.BluetoothProfileConnectionInfo;
import android.os.ParcelUuid;
import androidx.test.InstrumentationRegistry;
@@ -68,6 +73,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeoutException;
@@ -457,6 +463,23 @@ public class LeAudioServiceTest {
.isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
}
+ private void injectNoVerifyDeviceConnected(BluetoothDevice device) {
+ generateUnexpectedConnectionMessageFromNative(device,
+ LeAudioStackEvent.CONNECTION_STATE_CONNECTED,
+ LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED);
+ }
+
+ private void injectAndVerifyDeviceDisconnected(BluetoothDevice device) {
+ generateConnectionMessageFromNative(device,
+ LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED,
+ LeAudioStackEvent.CONNECTION_STATE_CONNECTED);
+ }
+
+ private void injectNoVerifyDeviceDisconnected(BluetoothDevice device) {
+ generateUnexpectedConnectionMessageFromNative(device,
+ LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED,
+ LeAudioStackEvent.CONNECTION_STATE_CONNECTED);
+ }
/**
* Test that the outgoing connect/disconnect and audio switch is successful.
*/
@@ -1220,4 +1243,142 @@ public class LeAudioServiceTest {
onGroupCodecConfChangedCallbackCalled = false;
mService.mLeAudioCallbacks.unregister(leAudioCallbacks);
}
+
+ private void verifyActiveDeviceStateIntent(int timeoutMs, BluetoothDevice device) {
+ Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device));
+ assertThat(intent).isNotNull();
+ assertThat(intent.getAction())
+ .isEqualTo(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+ assertThat(device).isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
+ }
+
+ /**
+ * Test native interface group status message handling
+ */
+ @Test
+ public void testLeadGroupDeviceDisconnects() {
+ int groupId = 1;
+ int direction = 1;
+ int snkAudioLocation = 3;
+ int srcAudioLocation = 4;
+ int availableContexts = 5;
+ int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
+ BluetoothDevice leadDevice;
+ BluetoothDevice memberDevice = mLeftDevice;
+
+ doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ connectTestDevice(mLeftDevice, groupId);
+ connectTestDevice(mRightDevice, groupId);
+
+ leadDevice = mService.getConnectedGroupLeadDevice(groupId);
+ if (Objects.equals(leadDevice, mLeftDevice)) {
+ memberDevice = mRightDevice;
+ }
+
+ assertThat(mService.setActiveDevice(leadDevice)).isTrue();
+
+ //Add location support
+ LeAudioStackEvent audioConfChangedEvent =
+ new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
+ audioConfChangedEvent.valueInt1 = direction;
+ audioConfChangedEvent.valueInt2 = groupId;
+ audioConfChangedEvent.valueInt3 = snkAudioLocation;
+ audioConfChangedEvent.valueInt4 = srcAudioLocation;
+ audioConfChangedEvent.valueInt5 = availableContexts;
+ mService.messageFromNative(audioConfChangedEvent);
+
+ //Set group and device as active
+ LeAudioStackEvent groupStatusChangedEvent =
+ new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
+ groupStatusChangedEvent.valueInt1 = groupId;
+ groupStatusChangedEvent.valueInt2 = groupStatus;
+ mService.messageFromNative(groupStatusChangedEvent);
+
+ assertThat(mService.getActiveDevices().contains(leadDevice)).isTrue();
+ verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(leadDevice), any(),
+ any(BluetoothProfileConnectionInfo.class));
+
+ verifyActiveDeviceStateIntent(TIMEOUT_MS, leadDevice);
+ injectNoVerifyDeviceDisconnected(leadDevice);
+
+ // We should not change the audio device
+ assertThat(mService.getConnectionState(leadDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
+ injectAndVerifyDeviceDisconnected(memberDevice);
+
+ // Verify the connection state broadcast, and that we are in Connecting state
+ verifyConnectionStateIntent(TIMEOUT_MS, leadDevice, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTED);
+
+ verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(leadDevice),
+ any(BluetoothProfileConnectionInfo.class));
+
+ }
+
+ /**
+ * Test native interface group status message handling
+ */
+ @Test
+ public void testLeadGroupDeviceReconnects() {
+ int groupId = 1;
+ int direction = 1;
+ int snkAudioLocation = 3;
+ int srcAudioLocation = 4;
+ int availableContexts = 5;
+ int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
+ BluetoothDevice leadDevice;
+ BluetoothDevice memberDevice = mLeftDevice;
+
+ doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ connectTestDevice(mLeftDevice, groupId);
+ connectTestDevice(mRightDevice, groupId);
+
+ leadDevice = mService.getConnectedGroupLeadDevice(groupId);
+ if (Objects.equals(leadDevice, mLeftDevice)) {
+ memberDevice = mRightDevice;
+ }
+
+ assertThat(mService.setActiveDevice(leadDevice)).isTrue();
+
+ //Add location support
+ LeAudioStackEvent audioConfChangedEvent =
+ new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
+ audioConfChangedEvent.valueInt1 = direction;
+ audioConfChangedEvent.valueInt2 = groupId;
+ audioConfChangedEvent.valueInt3 = snkAudioLocation;
+ audioConfChangedEvent.valueInt4 = srcAudioLocation;
+ audioConfChangedEvent.valueInt5 = availableContexts;
+ mService.messageFromNative(audioConfChangedEvent);
+
+ //Set group and device as active
+ LeAudioStackEvent groupStatusChangedEvent =
+ new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
+ groupStatusChangedEvent.valueInt1 = groupId;
+ groupStatusChangedEvent.valueInt2 = groupStatus;
+ mService.messageFromNative(groupStatusChangedEvent);
+
+ assertThat(mService.getActiveDevices().contains(leadDevice)).isTrue();
+ verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(leadDevice), any(),
+ any(BluetoothProfileConnectionInfo.class));
+
+ verifyActiveDeviceStateIntent(TIMEOUT_MS, leadDevice);
+ /* We don't want to distribute DISCONNECTION event, instead will try to reconnect
+ * (in native)
+ */
+ injectNoVerifyDeviceDisconnected(leadDevice);
+ assertThat(mService.getConnectionState(leadDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
+ /* Reconnect device, there should be no intent about that, as device was pretending
+ * connected
+ */
+ injectNoVerifyDeviceConnected(leadDevice);
+
+ injectAndVerifyDeviceDisconnected(memberDevice);
+ injectAndVerifyDeviceDisconnected(leadDevice);
+
+ verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), eq(leadDevice),
+ any(BluetoothProfileConnectionInfo.class));
+ }
}
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index 6fdf67c871..a1199f6c28 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -1558,9 +1558,15 @@ class CsisClientImpl : public CsisClient {
OnGattNotification(p_data->notify);
break;
- case BTA_GATTC_ENC_CMPL_CB_EVT:
- OnLeEncryptionComplete(p_data->enc_cmpl.remote_bda, BTM_SUCCESS);
- break;
+ case BTA_GATTC_ENC_CMPL_CB_EVT: {
+ uint8_t encryption_status;
+ if (BTM_IsEncrypted(p_data->enc_cmpl.remote_bda, BT_TRANSPORT_LE)) {
+ encryption_status = BTM_SUCCESS;
+ } else {
+ encryption_status = BTM_FAILED_ON_SECURITY;
+ }
+ OnLeEncryptionComplete(p_data->enc_cmpl.remote_bda, encryption_status);
+ } break;
case BTA_GATTC_SRVC_CHG_EVT:
OnGattServiceChangeEvent(p_data->remote_bda);
diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc
index 987aaec031..ce3c4c4a3a 100644
--- a/system/bta/hearing_aid/hearing_aid.cc
+++ b/system/bta/hearing_aid/hearing_aid.cc
@@ -1868,7 +1868,9 @@ void hearingaid_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
case BTA_GATTC_ENC_CMPL_CB_EVT:
if (!instance) return;
- instance->OnEncryptionComplete(p_data->enc_cmpl.remote_bda, true);
+ instance->OnEncryptionComplete(
+ p_data->enc_cmpl.remote_bda,
+ BTM_IsEncrypted(p_data->enc_cmpl.remote_bda, BT_TRANSPORT_LE));
break;
case BTA_GATTC_CONN_UPDATE_EVT:
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 2f877f0097..5206f7b8b0 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -3634,9 +3634,16 @@ void le_audio_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
p_data->open.transport, p_data->open.mtu);
break;
- case BTA_GATTC_ENC_CMPL_CB_EVT:
- instance->OnEncryptionComplete(p_data->enc_cmpl.remote_bda, BTM_SUCCESS);
- break;
+ case BTA_GATTC_ENC_CMPL_CB_EVT: {
+ uint8_t encryption_status;
+ if (BTM_IsEncrypted(p_data->enc_cmpl.remote_bda, BT_TRANSPORT_LE)) {
+ encryption_status = BTM_SUCCESS;
+ } else {
+ encryption_status = BTM_FAILED_ON_SECURITY;
+ }
+ instance->OnEncryptionComplete(p_data->enc_cmpl.remote_bda,
+ encryption_status);
+ } break;
case BTA_GATTC_CLOSE_EVT:
instance->OnGattDisconnected(
diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc
index d81f00c6aa..43d9ac19ec 100644
--- a/system/bta/vc/vc.cc
+++ b/system/bta/vc/vc.cc
@@ -35,6 +35,7 @@
#include "gd/common/strings.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
+#include "stack/btm/btm_sec.h"
#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"
@@ -1014,9 +1015,15 @@ class VolumeControlImpl : public VolumeControl {
OnNotificationEvent(n.conn_id, n.handle, n.len, n.value);
} break;
- case BTA_GATTC_ENC_CMPL_CB_EVT:
- OnEncryptionComplete(p_data->enc_cmpl.remote_bda, BTM_SUCCESS);
- break;
+ case BTA_GATTC_ENC_CMPL_CB_EVT: {
+ uint8_t encryption_status;
+ if (!BTM_IsEncrypted(p_data->enc_cmpl.remote_bda, BT_TRANSPORT_LE)) {
+ encryption_status = BTM_SUCCESS;
+ } else {
+ encryption_status = BTM_FAILED_ON_SECURITY;
+ }
+ OnEncryptionComplete(p_data->enc_cmpl.remote_bda, encryption_status);
+ } break;
case BTA_GATTC_SRVC_CHG_EVT:
OnServiceChangeEvent(p_data->remote_bda);
diff --git a/system/gd/AndroidTestTemplate.xml b/system/gd/AndroidTestTemplate.xml
index 601be93108..4fb4bf9f76 100644
--- a/system/gd/AndroidTestTemplate.xml
+++ b/system/gd/AndroidTestTemplate.xml
@@ -17,10 +17,11 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
- </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="settings put global ble_scan_always_enabled 0" />
<option name="run-command" value="svc bluetooth disable" />
@@ -33,4 +34,10 @@
<option name="module-name" value="{MODULE}" />
<option name="run-test-as" value="0" />
</test>
+
+ <!-- Only run tests in MTS if the Bluetooth Mainline module is installed. -->
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.bluetooth" />
+ </object>
</configuration>
diff --git a/system/test/Android.bp b/system/test/Android.bp
index f99db16f61..8c93ff5255 100644
--- a/system/test/Android.bp
+++ b/system/test/Android.bp
@@ -456,6 +456,7 @@ cc_defaults {
name: "mts_defaults",
target: {
android: {
+ test_config_template: ":BluetoothTestConfigTemplate",
test_suites: ["mts-bluetooth",],
},
},