blob: 592bee77fce8f91c2fb02e7d6263ede1c010f215 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth.cts;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static org.junit.Assert.assertThrows;
import android.annotation.NonNull;
import android.app.UiAutomation;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothStatusCodes;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.test.AndroidTestCase;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.ApiLevelUtil;
import java.io.IOException;
import java.time.Duration;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Very basic test, just of the static methods of {@link
* BluetoothAdapter}.
*/
public class BluetoothAdapterTest extends AndroidTestCase {
private static final String TAG = "BluetoothAdapterTest";
private static final int SET_NAME_TIMEOUT = 5000; // ms timeout for setting adapter name
private boolean mHasBluetooth;
private ReentrantLock mAdapterNameChangedlock;
private Condition mConditionAdapterNameChanged;
private boolean mIsAdapterNameChanged;
private BluetoothAdapter mAdapter;
private UiAutomation mUiAutomation;
@Override
public void setUp() throws Exception {
super.setUp();
mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH);
if (mHasBluetooth) {
mAdapter = getContext().getSystemService(BluetoothManager.class).getAdapter();
assertNotNull(mAdapter);
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
}
mAdapterNameChangedlock = new ReentrantLock();
mConditionAdapterNameChanged = mAdapterNameChangedlock.newCondition();
mIsAdapterNameChanged = false;
}
@Override
public void tearDown() throws Exception {
if (mHasBluetooth) {
mUiAutomation.dropShellPermissionIdentity();
}
}
public void test_getDefaultAdapter() {
/*
* Note: If the target doesn't support Bluetooth at all, then
* this method should return null.
*/
if (mHasBluetooth) {
assertNotNull(BluetoothAdapter.getDefaultAdapter());
} else {
assertNull(BluetoothAdapter.getDefaultAdapter());
}
}
public void test_checkBluetoothAddress() {
// Can't be null.
assertFalse(BluetoothAdapter.checkBluetoothAddress(null));
// Must be 17 characters long.
assertFalse(BluetoothAdapter.checkBluetoothAddress(""));
assertFalse(BluetoothAdapter.checkBluetoothAddress("0"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:0"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:0"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:0"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:00:"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:00:0"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:00:00:"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:00:00:0"));
// Must have colons between octets.
assertFalse(BluetoothAdapter.checkBluetoothAddress("00x00:00:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00.00:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00-00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:00900:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:00:00:00?00"));
// Hex letters must be uppercase.
assertFalse(BluetoothAdapter.checkBluetoothAddress("a0:00:00:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("0b:00:00:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:c0:00:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:0d:00:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:e0:00:00:00"));
assertFalse(BluetoothAdapter.checkBluetoothAddress("00:00:0f:00:00:00"));
assertTrue(BluetoothAdapter.checkBluetoothAddress("00:00:00:00:00:00"));
assertTrue(BluetoothAdapter.checkBluetoothAddress("12:34:56:78:9A:BC"));
assertTrue(BluetoothAdapter.checkBluetoothAddress("DE:F0:FE:DC:B8:76"));
}
/** Checks enable(), disable(), getState(), isEnabled() */
public void test_enableDisable() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
for (int i = 0; i < 5; i++) {
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
}
}
public void test_getAddress() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
assertTrue(BluetoothAdapter.checkBluetoothAddress(mAdapter.getAddress()));
mUiAutomation.dropShellPermissionIdentity();
assertThrows(SecurityException.class, () -> mAdapter.getAddress());
}
public void test_setName_getName() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mAdapterNameChangeReceiver, filter);
String name = mAdapter.getName();
assertNotNull(name);
// Check renaming the adapter
String genericName = "Generic Device 1";
mIsAdapterNameChanged = false;
assertTrue(mAdapter.setName(genericName));
assertTrue(waitForAdapterNameChange());
mIsAdapterNameChanged = false;
assertEquals(genericName, mAdapter.getName());
// Check setting adapter back to original name
assertTrue(mAdapter.setName(name));
assertTrue(waitForAdapterNameChange());
mIsAdapterNameChanged = false;
assertEquals(name, mAdapter.getName());
mUiAutomation.dropShellPermissionIdentity();
assertThrows(SecurityException.class, () -> mAdapter.setName("The name"));
assertThrows(SecurityException.class, () -> mAdapter.getName());
}
public void test_getBondedDevices() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
// empty value is returned when Bluetooth is disabled
Set<BluetoothDevice> devices = mAdapter.getBondedDevices();
assertNotNull(devices);
assertTrue(devices.isEmpty());
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
devices = mAdapter.getBondedDevices();
assertNotNull(devices);
for (BluetoothDevice device : devices) {
assertTrue(BluetoothAdapter.checkBluetoothAddress(device.getAddress()));
}
mUiAutomation.dropShellPermissionIdentity();
assertThrows(SecurityException.class, () -> mAdapter.getBondedDevices());
}
public void test_getRemoteDevice() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
// getRemoteDevice() should work even with Bluetooth disabled
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
mUiAutomation.dropShellPermissionIdentity();
// test bad addresses
assertThrows(IllegalArgumentException.class, () -> mAdapter.getRemoteDevice((String) null));
assertThrows(IllegalArgumentException.class, () ->
mAdapter.getRemoteDevice("00:00:00:00:00:00:00:00"));
assertThrows(IllegalArgumentException.class, () -> mAdapter.getRemoteDevice((byte[]) null));
assertThrows(IllegalArgumentException.class, () ->
mAdapter.getRemoteDevice(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00}));
// test success
BluetoothDevice device = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
assertNotNull(device);
assertEquals("00:11:22:AA:BB:CC", device.getAddress());
device = mAdapter.getRemoteDevice(
new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
assertNotNull(device);
assertEquals("01:02:03:04:05:06", device.getAddress());
}
public void test_getRemoteLeDevice() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
// getRemoteLeDevice() should work even with Bluetooth disabled
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
mUiAutomation.dropShellPermissionIdentity();
// test bad addresses
assertThrows(IllegalArgumentException.class,
() -> mAdapter.getRemoteLeDevice((String) null,
BluetoothDevice.ADDRESS_TYPE_PUBLIC));
assertThrows(IllegalArgumentException.class,
() -> mAdapter.getRemoteLeDevice("01:02:03:04:05:06:07:08",
BluetoothDevice.ADDRESS_TYPE_PUBLIC));
assertThrows(IllegalArgumentException.class,
() -> mAdapter.getRemoteLeDevice("01:02:03:04:05",
BluetoothDevice.ADDRESS_TYPE_PUBLIC));
assertThrows(IllegalArgumentException.class,
() -> mAdapter.getRemoteLeDevice("00:01:02:03:04:05",
BluetoothDevice.ADDRESS_TYPE_RANDOM + 1));
assertThrows(IllegalArgumentException.class,
() -> mAdapter.getRemoteLeDevice("00:01:02:03:04:05",
BluetoothDevice.ADDRESS_TYPE_PUBLIC - 1));
// test success
BluetoothDevice device = mAdapter.getRemoteLeDevice("00:11:22:AA:BB:CC",
BluetoothDevice.ADDRESS_TYPE_PUBLIC);
assertNotNull(device);
assertEquals("00:11:22:AA:BB:CC", device.getAddress());
device = mAdapter.getRemoteLeDevice("01:02:03:04:05:06",
BluetoothDevice.ADDRESS_TYPE_RANDOM);
assertNotNull(device);
assertEquals("01:02:03:04:05:06", device.getAddress());
}
public void test_isLeAudioSupported() throws IOException {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
assertNotSame(BluetoothStatusCodes.ERROR_UNKNOWN, mAdapter.isLeAudioSupported());
}
public void test_isLeAudioBroadcastSourceSupported() throws IOException {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
assertNotSame(BluetoothStatusCodes.ERROR_UNKNOWN,
mAdapter.isLeAudioBroadcastSourceSupported());
}
public void test_isLeAudioBroadcastAssistantSupported() throws IOException {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
assertNotSame(BluetoothStatusCodes.ERROR_UNKNOWN,
mAdapter.isLeAudioBroadcastAssistantSupported());
}
public void test_getMaxConnectedAudioDevices() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
// Defined in com.android.bluetooth.btservice.AdapterProperties
int maxConnectedAudioDevicesLowerBound = 1;
// Defined in com.android.bluetooth.btservice.AdapterProperties
int maxConnectedAudioDevicesUpperBound = 5;
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
assertTrue(mAdapter.getMaxConnectedAudioDevices() >= maxConnectedAudioDevicesLowerBound);
assertTrue(mAdapter.getMaxConnectedAudioDevices() <= maxConnectedAudioDevicesUpperBound);
mUiAutomation.dropShellPermissionIdentity();
assertThrows(SecurityException.class, () -> mAdapter.getMaxConnectedAudioDevices());
}
public void test_listenUsingRfcommWithServiceRecord() throws IOException {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
BluetoothServerSocket socket = mAdapter.listenUsingRfcommWithServiceRecord(
"test", UUID.randomUUID());
assertNotNull(socket);
socket.close();
mUiAutomation.dropShellPermissionIdentity();
assertThrows(SecurityException.class, () -> mAdapter.listenUsingRfcommWithServiceRecord(
"test", UUID.randomUUID()));
}
public void test_discoverableTimeout() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
Duration minutes = Duration.ofMinutes(2);
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
assertEquals(null, mAdapter.getDiscoverableTimeout());
assertEquals(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
mAdapter.setDiscoverableTimeout(minutes));
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
assertThrows(IllegalArgumentException.class, () -> mAdapter.setDiscoverableTimeout(
Duration.ofDays(25000)));
assertEquals(BluetoothStatusCodes.SUCCESS,
mAdapter.setDiscoverableTimeout(minutes));
assertEquals(minutes, mAdapter.getDiscoverableTimeout());
}
public void test_getConnectionState() {
if (!mHasBluetooth) return;
// Verify return value if Bluetooth is not enabled
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
assertEquals(BluetoothProfile.STATE_DISCONNECTED, mAdapter.getConnectionState());
}
public void test_getMostRecentlyConnectedDevices() {
if (!mHasBluetooth) return;
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
// Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
assertThrows(SecurityException.class, () -> mAdapter.getMostRecentlyConnectedDevices());
// Verify return value if Bluetooth is not enabled
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
List<BluetoothDevice> devices = mAdapter.getMostRecentlyConnectedDevices();
assertTrue(devices.isEmpty());
}
public void test_getUuids() {
if (!mHasBluetooth) return;
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
// Verify return value without permission.BLUETOOTH_CONNECT
mUiAutomation.dropShellPermissionIdentity();
assertThrows(SecurityException.class, () -> mAdapter.getUuidsList());
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
assertNotNull(mAdapter.getUuidsList());
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
// Verify return value if Bluetooth is not enabled
assertEquals(0, mAdapter.getUuidsList().size());
}
public void test_nameForState() {
assertEquals("ON", BluetoothAdapter.nameForState(BluetoothAdapter.STATE_ON));
assertEquals("OFF", BluetoothAdapter.nameForState(BluetoothAdapter.STATE_OFF));
assertEquals("TURNING_ON",
BluetoothAdapter.nameForState(BluetoothAdapter.STATE_TURNING_ON));
assertEquals("TURNING_OFF",
BluetoothAdapter.nameForState(BluetoothAdapter.STATE_TURNING_OFF));
assertEquals("BLE_ON", BluetoothAdapter.nameForState(BluetoothAdapter.STATE_BLE_ON));
// Check value before state range
for (int state = 0; state < BluetoothAdapter.STATE_OFF; state++) {
assertEquals("?!?!? (" + state + ")", BluetoothAdapter.nameForState(state));
}
// Check value after state range (skip TURNING_OFF)
for (int state = BluetoothAdapter.STATE_BLE_ON + 2; state < 100; state++) {
assertEquals("?!?!? (" + state + ")", BluetoothAdapter.nameForState(state));
}
}
public void test_BluetoothConnectionCallback_disconnectReasonText() {
assertEquals("Reason unknown", BluetoothAdapter.BluetoothConnectionCallback
.disconnectReasonToString(BluetoothStatusCodes.ERROR_UNKNOWN));
}
public void test_registerBluetoothConnectionCallback() {
if (!mHasBluetooth) return;
Executor executor = mContext.getMainExecutor();
BluetoothAdapter.BluetoothConnectionCallback callback =
new BluetoothAdapter.BluetoothConnectionCallback() {
@Override
public void onDeviceConnected(@NonNull BluetoothDevice device) {}
@Override
public void onDeviceDisconnected(BluetoothDevice device, int reason) {}
};
// placeholder call for coverage
callback.onDeviceConnected(null);
callback.onDeviceDisconnected(null, BluetoothStatusCodes.ERROR_UNKNOWN);
// Verify parameter
assertFalse(mAdapter.registerBluetoothConnectionCallback(null, callback));
assertFalse(mAdapter.registerBluetoothConnectionCallback(executor, null));
assertFalse(mAdapter.unregisterBluetoothConnectionCallback(null));
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
// Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
assertThrows(SecurityException.class,
() -> mAdapter.registerBluetoothConnectionCallback(executor, callback));
mUiAutomation.dropShellPermissionIdentity();
// Verify throws SecurityException without permission.BLUETOOTH_CONNECT
assertThrows(SecurityException.class, () ->
mAdapter.registerBluetoothConnectionCallback(executor, callback));
assertThrows(SecurityException.class, () ->
mAdapter.unregisterBluetoothConnectionCallback(callback));
}
public void test_requestControllerActivityEnergyInfo() {
if (!mHasBluetooth) return;
BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback callback =
new BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback() {
@Override
public void onBluetoothActivityEnergyInfoAvailable(
BluetoothActivityEnergyInfo info) {
assertNotNull(info);
}
@Override
public void onBluetoothActivityEnergyInfoError(int errorCode) {}
};
// Verify parameter
assertThrows(NullPointerException.class,
() -> mAdapter.requestControllerActivityEnergyInfo(null, callback));
}
public void test_clearBluetooth() {
if (!mHasBluetooth) return;
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
// Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
assertThrows(SecurityException.class, () -> mAdapter.clearBluetooth());
mUiAutomation.dropShellPermissionIdentity();
// Verify throws SecurityException without permission.BLUETOOTH_CONNECT
assertThrows(SecurityException.class, () -> mAdapter.clearBluetooth());
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
// Verify throws RuntimeException when trying to save sysprop for later (permission denied)
assertThrows(RuntimeException.class, () -> mAdapter.clearBluetooth());
}
public void test_BluetoothProfile_getConnectionStateName() {
if (!mHasBluetooth) return;
assertEquals("STATE_DISCONNECTED",
BluetoothProfile.getConnectionStateName(BluetoothProfile.STATE_DISCONNECTED));
assertEquals("STATE_CONNECTED",
BluetoothProfile.getConnectionStateName(BluetoothProfile.STATE_CONNECTED));
assertEquals("STATE_CONNECTING",
BluetoothProfile.getConnectionStateName(BluetoothProfile.STATE_CONNECTING));
assertEquals("STATE_CONNECTED",
BluetoothProfile.getConnectionStateName(BluetoothProfile.STATE_CONNECTED));
assertEquals("STATE_DISCONNECTING",
BluetoothProfile.getConnectionStateName(BluetoothProfile.STATE_DISCONNECTING));
assertEquals("STATE_UNKNOWN",
BluetoothProfile.getConnectionStateName(BluetoothProfile.STATE_DISCONNECTING + 1));
}
public void test_BluetoothProfile_getProfileName() {
if (!mHasBluetooth) return;
assertEquals("HEADSET",
BluetoothProfile.getProfileName(BluetoothProfile.HEADSET));
assertEquals("A2DP",
BluetoothProfile.getProfileName(BluetoothProfile.A2DP));
assertEquals("HID_HOST",
BluetoothProfile.getProfileName(BluetoothProfile.HID_HOST));
assertEquals("PAN",
BluetoothProfile.getProfileName(BluetoothProfile.PAN));
assertEquals("PBAP",
BluetoothProfile.getProfileName(BluetoothProfile.PBAP));
assertEquals("GATT",
BluetoothProfile.getProfileName(BluetoothProfile.GATT));
assertEquals("GATT_SERVER",
BluetoothProfile.getProfileName(BluetoothProfile.GATT_SERVER));
assertEquals("MAP",
BluetoothProfile.getProfileName(BluetoothProfile.MAP));
assertEquals("SAP",
BluetoothProfile.getProfileName(BluetoothProfile.SAP));
assertEquals("A2DP_SINK",
BluetoothProfile.getProfileName(BluetoothProfile.A2DP_SINK));
assertEquals("AVRCP_CONTROLLER",
BluetoothProfile.getProfileName(BluetoothProfile.AVRCP_CONTROLLER));
// assertEquals("AVRCP",
// BluetoothProfile.getProfileName(BluetoothProfile.AVRCP));
assertEquals("HEADSET_CLIENT",
BluetoothProfile.getProfileName(BluetoothProfile.HEADSET_CLIENT));
assertEquals("PBAP_CLIENT",
BluetoothProfile.getProfileName(BluetoothProfile.PBAP_CLIENT));
assertEquals("MAP_CLIENT",
BluetoothProfile.getProfileName(BluetoothProfile.MAP_CLIENT));
assertEquals("HID_DEVICE",
BluetoothProfile.getProfileName(BluetoothProfile.HID_DEVICE));
assertEquals("OPP",
BluetoothProfile.getProfileName(BluetoothProfile.OPP));
assertEquals("HEARING_AID",
BluetoothProfile.getProfileName(BluetoothProfile.HEARING_AID));
assertEquals("LE_AUDIO",
BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO));
assertEquals("HAP_CLIENT",
BluetoothProfile.getProfileName(BluetoothProfile.HAP_CLIENT));
if (!ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
return;
}
assertEquals("VOLUME_CONTROL",
BluetoothProfile.getProfileName(BluetoothProfile.VOLUME_CONTROL));
assertEquals("CSIP_SET_COORDINATOR",
BluetoothProfile.getProfileName(BluetoothProfile.CSIP_SET_COORDINATOR));
assertEquals("LE_AUDIO_BROADCAST",
BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO_BROADCAST));
assertEquals("LE_AUDIO_BROADCAST_ASSISTANT",
BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT));
}
public void test_getSetBluetoothHciSnoopLoggingMode() {
if (!mHasBluetooth) {
return;
}
assertThrows(SecurityException.class, () -> mAdapter
.setBluetoothHciSnoopLoggingMode(BluetoothAdapter.BT_SNOOP_LOG_MODE_FULL));
assertThrows(SecurityException.class, () -> mAdapter
.getBluetoothHciSnoopLoggingMode());
TestUtils.adoptPermissionAsShellUid(BLUETOOTH_PRIVILEGED);
assertThrows(IllegalArgumentException.class, () -> mAdapter
.setBluetoothHciSnoopLoggingMode(-1));
assertEquals(BluetoothStatusCodes.SUCCESS, mAdapter
.setBluetoothHciSnoopLoggingMode(BluetoothAdapter.BT_SNOOP_LOG_MODE_FULL));
assertEquals(mAdapter.getBluetoothHciSnoopLoggingMode(),
BluetoothAdapter.BT_SNOOP_LOG_MODE_FULL);
assertEquals(BluetoothStatusCodes.SUCCESS, mAdapter
.setBluetoothHciSnoopLoggingMode(BluetoothAdapter.BT_SNOOP_LOG_MODE_FILTERED));
assertEquals(mAdapter.getBluetoothHciSnoopLoggingMode(),
BluetoothAdapter.BT_SNOOP_LOG_MODE_FILTERED);
assertEquals(BluetoothStatusCodes.SUCCESS, mAdapter
.setBluetoothHciSnoopLoggingMode(BluetoothAdapter.BT_SNOOP_LOG_MODE_DISABLED));
assertEquals(mAdapter.getBluetoothHciSnoopLoggingMode(),
BluetoothAdapter.BT_SNOOP_LOG_MODE_DISABLED);
}
public void test_setPreferredAudioProfiles_getPreferredAudioProfiles() {
if (!mHasBluetooth) {
// Skip the test if bluetooth or companion device are not present.
return;
}
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
String deviceAddress = "00:11:22:AA:BB:CC";
BluetoothDevice device = mAdapter.getRemoteDevice(deviceAddress);
Bundle preferences = new Bundle();
preferences.putInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY, BluetoothProfile.HEADSET);
// Test invalid input
assertThrows(NullPointerException.class, () ->
mAdapter.setPreferredAudioProfiles(device, null));
assertThrows(IllegalArgumentException.class,
() -> mAdapter.setPreferredAudioProfiles(device, preferences));
assertThrows(NullPointerException.class, () -> mAdapter.getPreferredAudioProfiles(null));
preferences.putInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY, BluetoothProfile.HID_HOST);
assertThrows(IllegalArgumentException.class,
() -> mAdapter.setPreferredAudioProfiles(device, preferences));
preferences.putInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY, BluetoothProfile.LE_AUDIO);
preferences.putInt(BluetoothAdapter.AUDIO_MODE_DUPLEX, BluetoothProfile.A2DP);
assertThrows(IllegalArgumentException.class,
() -> mAdapter.setPreferredAudioProfiles(device, preferences));
preferences.putInt(BluetoothAdapter.AUDIO_MODE_DUPLEX, BluetoothProfile.GATT);
assertThrows(IllegalArgumentException.class,
() -> mAdapter.setPreferredAudioProfiles(device, preferences));
preferences.putInt(BluetoothAdapter.AUDIO_MODE_DUPLEX, BluetoothProfile.HEADSET);
assertThrows(NullPointerException.class, () ->
mAdapter.setPreferredAudioProfiles(null, preferences));
// Check what happens when the device is not bonded
assertTrue(mAdapter.getPreferredAudioProfiles(device).isEmpty());
assertEquals(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
mAdapter.setPreferredAudioProfiles(device, preferences));
}
public void test_preferredAudioProfileCallbacks() {
if (!mHasBluetooth) {
// Skip the test if bluetooth or companion device are not present.
return;
}
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
String deviceAddress = "00:11:22:AA:BB:CC";
BluetoothDevice device = mAdapter.getRemoteDevice(deviceAddress);
Executor executor = mContext.getMainExecutor();
BluetoothAdapter.PreferredAudioProfilesChangedCallback callback =
new BluetoothAdapter.PreferredAudioProfilesChangedCallback() {
@Override
public void onPreferredAudioProfilesChanged(
@androidx.annotation.NonNull BluetoothDevice device,
@androidx.annotation.NonNull Bundle preferredAudioProfiles, int status) {}
};
callback.onPreferredAudioProfilesChanged(device, Bundle.EMPTY,
BluetoothStatusCodes.SUCCESS);
assertThrows(NullPointerException.class, () ->
mAdapter.registerPreferredAudioProfilesChangedCallback(null, callback));
assertThrows(NullPointerException.class, () ->
mAdapter.registerPreferredAudioProfilesChangedCallback(executor, null));
assertThrows(NullPointerException.class, () ->
mAdapter.unregisterPreferredAudiProfilesChangedCallback(null));
// This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
assertThrows(SecurityException.class, () ->
mAdapter.registerPreferredAudioProfilesChangedCallback(executor, callback));
assertThrows(IllegalArgumentException.class, () ->
mAdapter.unregisterPreferredAudiProfilesChangedCallback(callback));
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
// Try the happy path
assertEquals(BluetoothStatusCodes.SUCCESS,
mAdapter.registerPreferredAudioProfilesChangedCallback(executor, callback));
assertEquals(BluetoothStatusCodes.SUCCESS,
mAdapter.unregisterPreferredAudiProfilesChangedCallback(callback));
}
public void test_notifyPreferredAudioProfileChangeApplied() {
if (!mHasBluetooth) {
// Skip the test if bluetooth or companion device are not present.
return;
}
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
String deviceAddress = "00:11:22:AA:BB:CC";
BluetoothDevice device = mAdapter.getRemoteDevice(deviceAddress);
assertThrows(NullPointerException.class, () ->
mAdapter.notifyPreferredAudioProfileChangeApplied(null));
assertEquals(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
mAdapter.notifyPreferredAudioProfileChangeApplied(device));
}
private static void sleep(long t) {
try {
Thread.sleep(t);
} catch (InterruptedException e) { }
}
private boolean waitForAdapterNameChange() {
mAdapterNameChangedlock.lock();
try {
// Wait for the Adapter name to be changed
while (!mIsAdapterNameChanged) {
if (!mConditionAdapterNameChanged.await(
SET_NAME_TIMEOUT, TimeUnit.MILLISECONDS)) {
Log.e(TAG, "Timeout while waiting for adapter name change");
break;
}
}
} catch (InterruptedException e) {
Log.e(TAG, "waitForAdapterNameChange: interrrupted");
} finally {
mAdapterNameChangedlock.unlock();
}
return mIsAdapterNameChanged;
}
private final BroadcastReceiver mAdapterNameChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)) {
mAdapterNameChangedlock.lock();
mIsAdapterNameChanged = true;
try {
mConditionAdapterNameChanged.signal();
} catch (IllegalMonitorStateException ex) {
} finally {
mAdapterNameChangedlock.unlock();
}
}
}
};
}