Merge "Create an Android Lint rule to prevent creation of log enforcement vars" into main
diff --git a/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java b/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java
index 6020043..efe155f 100644
--- a/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java
+++ b/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java
@@ -26,7 +26,6 @@
import com.android.bluetooth.R;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
class Util {
@@ -118,15 +117,15 @@
*/
public static List<Metadata> toMetadataList(Context context,
List<MediaSession.QueueItem> items) {
+ ArrayList<Metadata> list = new ArrayList<>();
- if (items == null) return Collections.emptyList();
+ if (items == null) return list;
- ArrayList<Metadata> list = new ArrayList<Metadata>();
for (int i = 0; i < items.size(); i++) {
Metadata data = toMetadata(context, items.get(i));
if (isEmptyData(data)) {
Log.e(TAG, "Received an empty Metadata item in list. Returning an empty queue");
- return Collections.emptyList();
+ return new ArrayList<>();
}
data.trackNum = "" + (i + 1);
data.numTracks = "" + items.size();
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 b835d91..2f6ba51 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -636,7 +636,7 @@
return;
}
- boolean isAssistantActive = isAnyReceiverReceivingBroadcast();
+ boolean isAssistantActive = isAnyReceiverReceivingBroadcast(getConnectedDevices());
if (isAssistantActive) {
/* Assistant become active */
@@ -1908,25 +1908,6 @@
return true;
}
- private boolean isAnyReceiverReceivingBroadcast() {
- for (BluetoothDevice device : getConnectedDevices()) {
- for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
- for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
- Long syncState = receiveState.getBisSyncState().get(i);
-
- /* Not synced to BIS of failed to sync to BIG */
- if (syncState == 0x00000000 || syncState == 0xFFFFFFFF) {
- continue;
- }
-
- return true;
- }
- }
- }
-
- return false;
- }
-
/** Return true if there is any non primary device receiving broadcast */
private boolean isAudioSharingModeOn(Integer broadcastId) {
if (mLocalBroadcastReceivers == null) {
@@ -2069,7 +2050,7 @@
mUnicastSourceStreamStatus = Optional.of(status);
if (status == STATUS_LOCAL_STREAM_REQUESTED) {
- if (isAnyReceiverReceivingBroadcast()) {
+ if (isAnyReceiverReceivingBroadcast(getConnectedDevices())) {
suspendAllReceiversSourceSynchronization();
}
} else if (status == STATUS_LOCAL_STREAM_SUSPENDED) {
@@ -2092,6 +2073,25 @@
}
}
+ /** Check if any sink receivers are receiving broadcast stream */
+ public boolean isAnyReceiverReceivingBroadcast(List<BluetoothDevice> devices) {
+ for (BluetoothDevice device : devices) {
+ for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
+ for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
+ Long syncState = receiveState.getBisSyncState().get(i);
+ /* Not synced to BIS of failed to sync to BIG */
+ if (syncState == 0x00000000 || syncState == 0xFFFFFFFF) {
+ continue;
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
/** Handle broadcast state changed */
public void notifyBroadcastStateChanged(int state, int broadcastId) {
switch (state) {
diff --git a/android/app/src/com/android/bluetooth/hid/HidHostService.java b/android/app/src/com/android/bluetooth/hid/HidHostService.java
index 48b791e..8cfd66d 100644
--- a/android/app/src/com/android/bluetooth/hid/HidHostService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidHostService.java
@@ -655,12 +655,11 @@
Log.e(
TAG,
"handleMessageConnectStateChanged: "
- + "remove unknown device: "
+ + "disconnect and disable the unknown device: "
+ device
+ " state: "
+ state);
- mNativeInterface.virtualUnPlug(
- getByteAddress(device), getAddressType(device), getTransport(device));
+ nativeDisconnect(device, transport, false);
return;
}
}
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 63bacb2..a96b53f 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -2196,7 +2196,9 @@
case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP:
if (Flags.leaudioUnicastInactivateDeviceBasedOnContext()) {
LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId);
- if (groupDescriptor != null && groupDescriptor.mIsActive) {
+ if (groupDescriptor != null
+ && groupDescriptor.mIsActive
+ && !isGroupReceivingBroadcast(groupId)) {
Log.i(
TAG,
"Group "
@@ -2332,6 +2334,19 @@
}
}
+ private boolean isGroupReceivingBroadcast(int groupId) {
+ if (!Flags.leaudioBroadcastAudioHandoverPolicies()) {
+ return false;
+ }
+
+ BassClientService bassClientService = getBassClientService();
+ if (bassClientService == null) {
+ return false;
+ }
+
+ return bassClientService.isAnyReceiverReceivingBroadcast(getGroupDevices(groupId));
+ }
+
private void notifyGroupStreamStatusChanged(int groupId, int groupStreamStatus) {
if (mLeAudioCallbacks != null) {
int n = mLeAudioCallbacks.beginBroadcast();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
index a4b11d3..263d911 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
@@ -1588,6 +1588,99 @@
}
}
+ @Test
+ public void testIsAnyReceiverReceivingBroadcast() {
+ prepareConnectedDeviceGroup();
+ BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
+ verifyAddSourceForGroup(meta);
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ if (sm.getDevice().equals(mCurrentDevice)) {
+ injectRemoteSourceStateSourceAdded(
+ sm,
+ meta,
+ TEST_SOURCE_ID,
+ BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
+ meta.isEncrypted()
+ ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+ : BluetoothLeBroadcastReceiveState
+ .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+ null);
+ injectRemoteSourceStateChanged(
+ sm,
+ meta,
+ TEST_SOURCE_ID,
+ BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+ meta.isEncrypted()
+ ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+ : BluetoothLeBroadcastReceiveState
+ .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+ null,
+ (long) 0x00000000);
+ } else if (sm.getDevice().equals(mCurrentDevice1)) {
+ injectRemoteSourceStateSourceAdded(
+ sm,
+ meta,
+ TEST_SOURCE_ID + 1,
+ BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
+ meta.isEncrypted()
+ ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+ : BluetoothLeBroadcastReceiveState
+ .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+ null);
+ injectRemoteSourceStateChanged(
+ sm,
+ meta,
+ TEST_SOURCE_ID + 1,
+ BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+ meta.isEncrypted()
+ ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+ : BluetoothLeBroadcastReceiveState
+ .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+ null,
+ (long) 0x00000000);
+ }
+ }
+
+ List<BluetoothDevice> devices = mBassClientService.getConnectedDevices();
+ // Verify isAnyReceiverReceivingBroadcast returns false if no BIS synced
+ assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(devices)).isFalse();
+
+ // Update receiver state with lost BIS sync
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ if (sm.getDevice().equals(mCurrentDevice)) {
+ injectRemoteSourceStateChanged(
+ sm,
+ meta,
+ TEST_SOURCE_ID,
+ BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+ meta.isEncrypted()
+ ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+ : BluetoothLeBroadcastReceiveState
+ .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+ null,
+ (long) 0x00000001);
+ } else if (sm.getDevice().equals(mCurrentDevice1)) {
+ injectRemoteSourceStateChanged(
+ sm,
+ meta,
+ TEST_SOURCE_ID + 1,
+ BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+ meta.isEncrypted()
+ ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+ : BluetoothLeBroadcastReceiveState
+ .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+ null,
+ (long) 0x00000002);
+ }
+ }
+ BluetoothDevice invalidDevice = TestUtils.getTestDevice(mBluetoothAdapter, 2);
+ // Verify isAnyReceiverReceivingBroadcast returns false if invalid device
+ assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(List.of(invalidDevice)))
+ .isFalse();
+ // Verify isAnyReceiverReceivingBroadcast returns true if BIS synced
+ assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(devices)).isTrue();
+ }
+
private void prepareTwoSynchronizedDevices() {
BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
index fc08492..6fa86d0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
@@ -17,6 +17,7 @@
package com.android.bluetooth.hfpclient;
import static android.content.pm.PackageManager.FEATURE_WATCH;
+import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -65,6 +66,7 @@
private boolean mIsHeadsetClientServiceStarted;
private static final int STANDARD_WAIT_MILLIS = 1000;
+ private static final int SERVICE_START_WAIT_MILLIS = 100;
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@@ -127,6 +129,8 @@
public void testUpdateBatteryLevel() throws Exception {
startService();
+ // Adding a wait to prevent potential failure caused by delayed broadcast intent.
+ TimeUnit.MILLISECONDS.sleep(SERVICE_START_WAIT_MILLIS);
// Put mock state machine
BluetoothDevice device =
BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
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 9ae8494..8b8b100 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
@@ -58,6 +58,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
+import com.android.bluetooth.bass_client.BassClientService;
import com.android.bluetooth.btservice.ActiveDeviceManager;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ServiceFactory;
@@ -132,6 +133,7 @@
@Mock private VolumeControlService mVolumeControlService;
@Mock private HapClientService mHapClientService;
@Mock private CsipSetCoordinatorService mCsipSetCoordinatorService;
+ @Mock private BassClientService mBassClientService;
@Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
@Spy private ServiceFactory mServiceFactory = new ServiceFactory();
@@ -202,10 +204,12 @@
mService.mMcpService = mMcpService;
mService.mTbsService = mTbsService;
mService.mHapClientService = mHapClientService;
+ mService.mBassClientService = mBassClientService;
mService.mServiceFactory = mServiceFactory;
when(mServiceFactory.getVolumeControlService()).thenReturn(mVolumeControlService);
when(mServiceFactory.getHapClientService()).thenReturn(mHapClientService);
when(mServiceFactory.getCsipSetCoordinatorService()).thenReturn(mCsipSetCoordinatorService);
+ when(mServiceFactory.getBassClientService()).thenReturn(mBassClientService);
LeAudioStackEvent stackEvent =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
@@ -1767,6 +1771,50 @@
eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class));
}
+ @Test
+ public void testMediaContextUnavailableWhileReceivingBroadcast() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
+ mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES);
+
+ doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ connectTestDevice(mSingleDevice, testGroupId);
+
+ Integer contexts = BluetoothLeAudio.CONTEXT_TYPE_MEDIA;
+ injectAudioConfChanged(testGroupId, contexts, 1);
+
+ // Set group and device as active.
+ injectGroupStatusChange(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
+
+ verify(mAudioManager, times(1))
+ .handleBluetoothActiveDeviceChanged(
+ eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class));
+
+ doReturn(true)
+ .when(mBassClientService)
+ .isAnyReceiverReceivingBroadcast(mService.getGroupDevices(testGroupId));
+ LeAudioStackEvent healthBasedGroupAction =
+ new LeAudioStackEvent(
+ LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION);
+ healthBasedGroupAction.valueInt1 = testGroupId;
+ healthBasedGroupAction.valueInt2 =
+ LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP;
+ mService.messageFromNative(healthBasedGroupAction);
+ // Verify skip setting device inactive if group is receiving broadcast
+ verify(mAudioManager, times(0))
+ .handleBluetoothActiveDeviceChanged(
+ eq(null), any(), any(BluetoothProfileConnectionInfo.class));
+
+ doReturn(false)
+ .when(mBassClientService)
+ .isAnyReceiverReceivingBroadcast(mService.getGroupDevices(testGroupId));
+ mService.messageFromNative(healthBasedGroupAction);
+ // Verify setting device inactive if group is not receiving broadcast
+ verify(mAudioManager, times(1))
+ .handleBluetoothActiveDeviceChanged(
+ eq(null), any(), any(BluetoothProfileConnectionInfo.class));
+ }
+
private void sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus) {
onGroupStatusCallbackCalled = false;
diff --git a/android/pandora/server/configs/PtsBotTest.xml b/android/pandora/server/configs/PtsBotTest.xml
index af62689..938be7d 100644
--- a/android/pandora/server/configs/PtsBotTest.xml
+++ b/android/pandora/server/configs/PtsBotTest.xml
@@ -27,6 +27,11 @@
<option name="host-background-command" value="adb -s $SERIAL shell am instrument --no-hidden-api-checks -w com.android.pandora/.Main" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setprop persist.bluetooth.btsnooplogmode full" />
+ <option name="run-command" value="setprop log.tag.bluetooth VERBOSE" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.PythonVirtualenvPreparer">
<option name="dep-module" value="grpcio" />
<option name="dep-module" value="protobuf==3.20.1" />
diff --git a/flags/gap.aconfig b/flags/gap.aconfig
index b40e1d7..455a50f 100644
--- a/flags/gap.aconfig
+++ b/flags/gap.aconfig
@@ -163,3 +163,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "scan_record_manufacturer_data_merge"
+ namespace: "bluetooth"
+ description: "If a scan record has multiple datas under same manufacturer id, merge the values"
+ bug: "331723396"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/hfp.aconfig b/flags/hfp.aconfig
index b50bb4a..0b8fb02 100644
--- a/flags/hfp.aconfig
+++ b/flags/hfp.aconfig
@@ -56,3 +56,13 @@
description: "during ongoing call, pretend network service in +CIND when network is unavailable"
bug: "317307596"
}
+
+flag {
+ name: "bta_ag_cmd_brsf_allow_uint32"
+ namespace: "bluetooth"
+ description: "Allow and ignore the RFU bits set to 0b1 rather than responding ERROR"
+ bug: "331129986"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/leaudio.aconfig b/flags/leaudio.aconfig
index be755c8..90052a3 100644
--- a/flags/leaudio.aconfig
+++ b/flags/leaudio.aconfig
@@ -222,3 +222,23 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "leaudio_quick_leaudio_toggle_switch_fix"
+ namespace: "bluetooth"
+ description: "Fix for one device being not connected when toggle LeAudio is switch during bonding"
+ bug: "328595942"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "leaudio_broadcast_assistant_handle_command_statuses"
+ namespace: "bluetooth"
+ description: "Handle command statuses to recover from errors"
+ bug: "331167629"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/floss/pandora/floss/audio_test_data.py b/floss/pandora/floss/audio_test_data.py
new file mode 100644
index 0000000..e2cf269
--- /dev/null
+++ b/floss/pandora/floss/audio_test_data.py
@@ -0,0 +1,50 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+"""This module provides audio test data."""
+
+import os
+
+
+class AudioTestDataException(Exception):
+ """Exception for audio test data."""
+ pass
+
+
+class AudioTestData(object):
+ """Class to represent audio test data."""
+
+ def __init__(self, data_format=None, path=None, frequencies=None, duration_secs=None):
+ """Initializes an audio test file.
+
+ Args:
+ data_format: A dict containing data format including
+ file_type, sample_format, channel, and rate.
+ file_type: file type e.g. 'raw' or 'wav'.
+ sample_format: One of the keys in audio_utils.SAMPLE_FORMAT.
+ channel: number of channels.
+ rate: sampling rate.
+ path: The path to the file.
+ frequencies: A list containing the frequency of each channel in this file.
+ Only applicable to data of sine tone.
+ duration_secs: Duration of test file in seconds.
+
+ Raises:
+ AudioTestDataException if the path does not exist.
+ """
+ self.data_format = data_format
+ if not os.path.exists(path):
+ raise AudioTestDataException('Can not find path %s' % path)
+ self.path = path
+ self.frequencies = frequencies
+ self.duration_secs = duration_secs
diff --git a/floss/pandora/floss/audio_utils.py b/floss/pandora/floss/audio_utils.py
new file mode 100644
index 0000000..98e1231
--- /dev/null
+++ b/floss/pandora/floss/audio_utils.py
@@ -0,0 +1,187 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+
+import logging
+import os
+import subprocess
+import wave
+
+from floss.pandora.floss import audio_test_data
+from floss.pandora.floss import cras_utils
+from floss.pandora.floss import sox_utils
+from floss.pandora.floss import utils
+
+CRAS_BLUETOOTH_OUTPUT_NODE_TYPE = 'BLUETOOTH'
+
+AUDIO_TEST_DIR = '/tmp/audio'
+
+A2DP_TEST_DATA = {
+ 'rate': 48000,
+ 'channels': 2,
+ 'frequencies': (440, 20000),
+ 'file': os.path.join(AUDIO_TEST_DIR, 'binaural_sine_440hz_20000hz_rate48000_5secs.wav'),
+ 'recorded_by_sink': os.path.join(AUDIO_TEST_DIR, 'a2dp_recorded_by_sink.wav'),
+ 'chunk_in_secs': 5,
+ 'bit_width': 16,
+ 'format': 'S16_LE',
+ 'duration': 5,
+}
+
+A2DP_PLAYBACK_DATA = {
+ 'rate': 44100,
+ 'channels': 2,
+ 'file': os.path.join(AUDIO_TEST_DIR, 'audio_playback.wav'),
+ 'sample_width': 2
+}
+
+SAMPLE_FORMATS = dict(S32_LE=dict(message='Signed 32-bit integer, little-endian', dtype_str='<i', size_bytes=4),
+ S16_LE=dict(message='Signed 16-bit integer, little-endian', dtype_str='<i', size_bytes=2))
+
+
+@utils.dbus_safe(None)
+def get_selected_output_device_type():
+ """Gets the selected audio output node type.
+
+ Returns:
+ The node type of the selected output device.
+ """
+ return str(cras_utils.get_selected_output_device_type())
+
+
+@utils.dbus_safe(None)
+def select_output_node(node_type):
+ """Selects the audio output node.
+
+ Args:
+ node_type: The node type of the Bluetooth peer device.
+
+ Returns:
+ True if the operation succeeds.
+ """
+ return cras_utils.set_single_selected_output_node(node_type)
+
+
+def select_audio_output_node():
+ """Selects the audio output node through cras."""
+
+ def bluetooth_type_selected(node_type):
+ """Checks if the bluetooth node type is selected."""
+ selected = get_selected_output_device_type()
+ logging.debug('active output node type: %s, expected %s', selected, node_type)
+ return selected == node_type
+
+ node_type = CRAS_BLUETOOTH_OUTPUT_NODE_TYPE
+ if not select_output_node(node_type):
+ return False
+
+ desc = 'waiting for %s as active cras audio output node type' % node_type
+ try:
+ utils.poll_for_condition(condition=lambda: bluetooth_type_selected(node_type),
+ timeout=20,
+ sleep_interval=1,
+ desc=desc)
+ except TimeoutError:
+ return False
+ return True
+
+
+def generate_audio_test_data(path, data_format=None, frequencies=None, duration_secs=None, volume_scale=None):
+ """Generates audio test data with specified format and frequencies.
+
+ Args:
+ path: The path to the file.
+ data_format: A dict containing data format including
+ file_type, sample_format, channel, and rate.
+ file_type: file type e.g. 'raw' or 'wav'.
+ sample_format: One of the keys in audio_data.SAMPLE_FORMAT.
+ channel: number of channels.
+ ate: sampling rate.
+ frequencies: A list containing the frequency of each channel in this file.
+ Only applicable to data of sine tone.
+ duration_secs: Duration of test file in seconds.
+ volume_scale: A float for volume scale used in sox command.
+ E.g. 0.5 to scale volume by half. -1.0 to invert.
+
+ Returns:
+ An AudioTestData object.
+ """
+ sox_file_path = path
+
+ if data_format is None:
+ data_format = dict(file_type='wav', sample_format='S16_LE', channel=2, rate=48000)
+
+ sample_format = SAMPLE_FORMATS[data_format['sample_format']]
+ bits = sample_format['size_bytes'] * 8
+
+ command = sox_utils.generate_sine_tone_cmd(filename=sox_file_path,
+ channels=data_format['channel'],
+ bits=bits,
+ rate=data_format['rate'],
+ duration=duration_secs,
+ frequencies=frequencies,
+ vol=volume_scale,
+ raw=(data_format['file_type'] == 'raw'))
+
+ logging.info(' '.join(command))
+ subprocess.check_call(command)
+
+ test_data = audio_test_data.AudioTestData(data_format=data_format,
+ path=sox_file_path,
+ frequencies=frequencies,
+ duration_secs=duration_secs)
+
+ return test_data
+
+
+def generate_playback_file(audio_data):
+ """Generates the playback file if it does not exist yet.
+
+ Some audio test files may be large. Generate them on the fly to save the storage of the source tree.
+
+ Args:
+ audio_data: The audio test data.
+ """
+ directory = os.path.dirname(audio_data['file'])
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+
+ if not os.path.exists(audio_data['file']):
+ data_format = dict(file_type='wav',
+ sample_format='S16_LE',
+ channel=audio_data['channels'],
+ rate=audio_data['rate'])
+ generate_audio_test_data(data_format=data_format,
+ path=audio_data['file'],
+ duration_secs=audio_data['duration'],
+ frequencies=audio_data['frequencies'])
+ logging.debug('Audio file generated: %s', audio_data['file'])
+
+
+def generate_playback_file_from_binary_data(audio_data):
+ """Generates wav audio file from binary audio data.
+
+ Args:
+ audio_data: The binary audio data.
+ """
+ directory = os.path.dirname(A2DP_PLAYBACK_DATA['file'])
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+
+ with wave.open(A2DP_PLAYBACK_DATA['file'], 'wb') as wav_file:
+ wav_file.setnchannels(A2DP_PLAYBACK_DATA['channels'])
+ wav_file.setframerate(A2DP_PLAYBACK_DATA['rate'])
+ wav_file.setsampwidth(A2DP_PLAYBACK_DATA['sample_width'])
+ wav_file.writeframes(audio_data)
+
+ logging.debug('wav file generated from binary data: %s', A2DP_PLAYBACK_DATA['file'])
diff --git a/floss/pandora/floss/bluetooth_gatt_service.py b/floss/pandora/floss/bluetooth_gatt_service.py
new file mode 100644
index 0000000..13ca763
--- /dev/null
+++ b/floss/pandora/floss/bluetooth_gatt_service.py
@@ -0,0 +1,181 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+"""Class to hold the GATT service/characteristic/descriptor object."""
+
+import uuid
+
+
+class Service:
+ """Class represents Bluetooth GATT service."""
+
+ def __init__(self,
+ instance_id=None,
+ service_type=None,
+ uuid=None,
+ characteristics=None,
+ included_services=None,
+ value=None):
+ self.instance_id = instance_id
+ self.service_type = service_type
+ self.uuid = uuid
+ self.characteristics = characteristics
+ self.included_services = included_services
+ self.value = value
+
+ def to_dict(self):
+ """Converts service object to dictionary.
+
+ Returns:
+ GATT service as dict.
+ """
+ return {
+ 'instance_id': self.instance_id,
+ 'service_type': self.service_type,
+ 'uuid': self.uuid,
+ 'included_services': [service.to_dict() for service in self.included_services],
+ 'characteristics': [characteristic.to_dict() for characteristic in self.characteristics],
+ 'value': self.value
+ }
+
+
+class Characteristic:
+ """Class represents Bluetooth GATT characteristic."""
+
+ def __init__(self,
+ instance_id=None,
+ permissions=None,
+ write_type=None,
+ descriptors=None,
+ uuid=None,
+ key_size=None,
+ properties=None,
+ value=None):
+ self.instance_id = instance_id
+ self.permissions = permissions
+ self.write_type = write_type
+ self.descriptors = descriptors
+ self.uuid = uuid
+ self.key_size = key_size
+ self.properties = properties
+ self.value = value
+
+ def to_dict(self):
+ """Converts characteristic object to dictionary.
+
+ Returns:
+ GATT characteristic as dict.
+ """
+ return {
+ 'properties': self.properties,
+ 'permissions': self.permissions,
+ 'uuid': self.uuid,
+ 'instance_id': self.instance_id,
+ 'descriptors': [descriptor.to_dict() for descriptor in self.descriptors],
+ 'key_size': self.key_size,
+ 'write_type': self.write_type,
+ 'value': self.value
+ }
+
+
+class Descriptor:
+ """Class represents Bluetooth GATT descriptor."""
+
+ def __init__(self, permissions=None, uuid=None, instance_id=None, value=None):
+ self.permissions = permissions
+ self.uuid = uuid
+ self.instance_id = instance_id
+ self.value = value
+
+ def to_dict(self):
+ """Converts descriptor object to dictionary.
+
+ Returns:
+ GATT descriptor as dict.
+ """
+ return {
+ 'instance_id': self.instance_id,
+ 'permissions': self.permissions,
+ 'uuid': self.uuid,
+ 'value': self.value
+ }
+
+
+def create_gatt_service(service):
+ """Creates GATT service from a dictionary.
+
+ Args:
+ service: Bluetooth GATT service as a dictionary.
+
+ Returns:
+ Bluetooth GATT service object.
+ """
+ return Service(
+ instance_id=service['instance_id'],
+ service_type=service['service_type'],
+ uuid=str(uuid.UUID(bytes=bytes(service['uuid']))).upper(),
+ included_services=[create_gatt_service(included_service) for included_service in service['included_services']],
+ characteristics=[create_gatt_characteristic(characteristic) for characteristic in service['characteristics']],
+ value=service.get('value'))
+
+
+def create_gatt_characteristic(characteristic):
+ """Creates GATT characteristic from a dictionary.
+
+ Args:
+ characteristic: Bluetooth GATT characteristic as a dictionary.
+
+ Returns:
+ Bluetooth GATT characteristic object.
+ """
+ return Characteristic(
+ properties=characteristic['properties'],
+ permissions=characteristic['permissions'],
+ uuid=str(uuid.UUID(bytes=bytes(characteristic['uuid']))).upper(),
+ instance_id=characteristic['instance_id'],
+ descriptors=[create_gatt_characteristic_descriptor(descriptor) for descriptor in characteristic['descriptors']],
+ key_size=characteristic['key_size'],
+ write_type=characteristic['write_type'],
+ value=characteristic.get('value'))
+
+
+def create_gatt_characteristic_descriptor(descriptor):
+ """Creates GATT descriptor from a dictionary.
+
+ Args:
+ descriptor: Bluetooth GATT descriptor as a dictionary.
+
+ Returns:
+ Bluetooth GATT descriptor object.
+ """
+ return Descriptor(instance_id=descriptor['instance_id'],
+ permissions=descriptor['permissions'],
+ uuid=str(uuid.UUID(bytes=bytes(descriptor['uuid']))).upper(),
+ value=descriptor.get('value'))
+
+
+def convert_object_to_dict(obj):
+ """Coverts object to dictionary.
+
+ Args:
+ obj: Service/Characteristic/Descriptor object.
+
+ Returns:
+ A dictionary represents the object.
+ """
+ if isinstance(obj, (Descriptor, Characteristic, Service)):
+ return obj.to_dict()
+ elif isinstance(obj, list):
+ return [convert_object_to_dict(item) for item in obj]
+ else:
+ return obj
diff --git a/floss/pandora/floss/cmd_utils.py b/floss/pandora/floss/cmd_utils.py
new file mode 100644
index 0000000..e8f10d6
--- /dev/null
+++ b/floss/pandora/floss/cmd_utils.py
@@ -0,0 +1,228 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+
+import atexit
+import itertools
+import logging
+import os
+import pipes
+import pwd
+import select
+import subprocess
+import threading
+
+TEE_TO_LOGS = object()
+_popen_lock = threading.Lock()
+_logging_service = None
+_command_serial_number = itertools.count(1)
+
+_LOG_BUFSIZE = 4096
+_PIPE_CLOSED = -1
+
+
+class _LoggerProxy(object):
+
+ def __init__(self, logger):
+ self._logger = logger
+
+ def fileno(self):
+ """Returns the fileno of the logger pipe."""
+ return self._logger._pipe[1]
+
+ def __del__(self):
+ self._logger.close()
+
+
+class _PipeLogger(object):
+
+ def __init__(self, level, prefix):
+ self._pipe = list(os.pipe())
+ self._level = level
+ self._prefix = prefix
+
+ def close(self):
+ """Closes the logger."""
+ if self._pipe[1] != _PIPE_CLOSED:
+ os.close(self._pipe[1])
+ self._pipe[1] = _PIPE_CLOSED
+
+
+class _LoggingService(object):
+
+ def __init__(self):
+ # Python's list is thread safe
+ self._loggers = []
+
+ # Change tuple to list so that we can change the value when
+ # closing the pipe.
+ self._pipe = list(os.pipe())
+ self._thread = threading.Thread(target=self._service_run)
+ self._thread.daemon = True
+ self._thread.start()
+
+ def _service_run(self):
+ terminate_loop = False
+ while not terminate_loop:
+ rlist = [l._pipe[0] for l in self._loggers]
+ rlist.append(self._pipe[0])
+ for r in select.select(rlist, [], [])[0]:
+ data = os.read(r, _LOG_BUFSIZE)
+ if r != self._pipe[0]:
+ self._output_logger_message(r, data)
+ elif len(data) == 0:
+ terminate_loop = True
+ # Release resources.
+ os.close(self._pipe[0])
+ for logger in self._loggers:
+ os.close(logger._pipe[0])
+
+ def _output_logger_message(self, r, data):
+ logger = next(l for l in self._loggers if l._pipe[0] == r)
+
+ if len(data) == 0:
+ os.close(logger._pipe[0])
+ self._loggers.remove(logger)
+ return
+
+ for line in data.split('\n'):
+ logging.log(logger._level, '%s%s', logger._prefix, line)
+
+ def create_logger(self, level=logging.DEBUG, prefix=''):
+ """Creates a new logger.
+
+ Args:
+ level: The desired logging level.
+ prefix: The prefix to add to each log entry.
+ """
+ logger = _PipeLogger(level=level, prefix=prefix)
+ self._loggers.append(logger)
+ os.write(self._pipe[1], '\0')
+ return _LoggerProxy(logger)
+
+ def shutdown(self):
+ """Shuts down the logger."""
+ if self._pipe[1] != _PIPE_CLOSED:
+ os.close(self._pipe[1])
+ self._pipe[1] = _PIPE_CLOSED
+ self._thread.join()
+
+
+def create_logger(level=logging.DEBUG, prefix=''):
+ """Creates a new logger.
+
+ Args:
+ level: The desired logging level.
+ prefix: The prefix to add to each log entry.
+ """
+ global _logging_service
+ if _logging_service is None:
+ _logging_service = _LoggingService()
+ atexit.register(_logging_service.shutdown)
+ return _logging_service.create_logger(level=level, prefix=prefix)
+
+
+def wait_and_check_returncode(*popens):
+ """Waits for all the Popens and check the return code is 0.
+
+ Args:
+ popens: The Popens to be checked.
+
+ Raises:
+ RuntimeError if the return code is not 0.
+ """
+ error_message = None
+ for p in popens:
+ if p.wait() != 0:
+ error_message = ('Command failed(%d, %d): %s' % (p.pid, p.returncode, p.command))
+ logging.error(error_message)
+ if error_message:
+ raise RuntimeError(error_message)
+
+
+def execute(args, stdin=None, stdout=TEE_TO_LOGS, stderr=TEE_TO_LOGS, run_as=None):
+ """Executes a child command and wait for it.
+
+ Args:
+ args: The command to be executed.
+ stdin: The executed program's standard input.
+ stdout: The executed program's standard output.
+ stderr: The executed program's standard error.
+ run_as: If not None, run the command as the given user.
+
+ Returns:
+ The output from standard output if 'stdout' is subprocess.PIPE.
+
+ Raises:
+ RuntimeException if the return code of the child command is not 0.
+ """
+ ps = popen(args, stdin=stdin, stdout=stdout, stderr=stderr, run_as=run_as)
+ out = ps.communicate()[0] if stdout == subprocess.PIPE else None
+ wait_and_check_returncode(ps)
+ return out
+
+
+def _run_as(user):
+ """Changes the uid and gid of the running process to be that of the
+ given user and configures its supplementary groups.
+
+ Don't call this function directly, instead wrap it in a lambda and
+ pass that to the preexec_fn argument of subprocess.Popen.
+
+ Example usage:
+ subprocess.Popen(..., preexec_fn=lambda: _run_as('chronos'))
+
+ Args:
+ user: The user to run as.
+ """
+ pw = pwd.getpwnam(user)
+ os.setgid(pw.pw_gid)
+ os.initgroups(user, pw.pw_gid)
+ os.setuid(pw.pw_uid)
+
+
+def popen(args, stdin=None, stdout=TEE_TO_LOGS, stderr=TEE_TO_LOGS, env=None, run_as=None):
+ """Returns a Popen object just as subprocess.Popen does but with the
+ executed command stored in Popen.command.
+
+ Args:
+ args: The command to be executed.
+ stdin: The executed program's standard input.
+ stdout: The executed program's standard output.
+ stderr: The executed program's standard error.
+ env: The executed program's environment.
+ run_as: If not None, run the command as the given user.
+ """
+ command_id = next(_command_serial_number)
+ prefix = '[%04d] ' % command_id
+
+ if stdout is TEE_TO_LOGS:
+ stdout = create_logger(level=logging.DEBUG, prefix=prefix)
+ if stderr is TEE_TO_LOGS:
+ stderr = create_logger(level=logging.ERROR, prefix=prefix)
+
+ command = ' '.join(pipes.quote(x) for x in args)
+ logging.info('%sRunning: %s', prefix, command)
+
+ preexec_fn = None
+ if run_as is not None:
+ preexec_fn = lambda: _run_as(run_as)
+
+ # The lock is required for http://crbug.com/323843.
+ with _popen_lock:
+ ps = subprocess.Popen(args, stdin=stdin, stdout=stdout, stderr=stderr, env=env, preexec_fn=preexec_fn)
+
+ logging.info('%spid is %d', prefix, ps.pid)
+ ps.command_id = command_id
+ ps.command = command
+ return ps
diff --git a/floss/pandora/floss/cras_utils.py b/floss/pandora/floss/cras_utils.py
new file mode 100644
index 0000000..78241d0
--- /dev/null
+++ b/floss/pandora/floss/cras_utils.py
@@ -0,0 +1,852 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+"""This module provides cras audio utilities."""
+
+import dbus
+import logging
+import re
+import subprocess
+
+from floss.pandora.floss import cmd_utils
+from floss.pandora.floss import utils
+
+_CRAS_TEST_CLIENT = '/usr/bin/cras_test_client'
+
+
+class CrasUtilsError(Exception):
+ """Error in CrasUtils."""
+ pass
+
+
+def playback(blocking=True, stdin=None, *args, **kargs):
+ """A helper function to execute the playback_cmd.
+
+ Args:
+ blocking: Blocks this call until playback finishes.
+ stdin: The standard input of playback process.
+ args: Args passed to playback_cmd.
+ kargs: Kargs passed to playback_cmd.
+
+ Returns:
+ The process running the playback command. Note that if the
+ blocking parameter is true, this will return a finished process.
+ """
+ process = cmd_utils.popen(playback_cmd(*args, **kargs), stdin=stdin)
+ if blocking:
+ cmd_utils.wait_and_check_returncode(process)
+ return process
+
+
+def capture(*args, **kargs):
+ """A helper function to execute the capture_cmd.
+
+ Args:
+ args: Args passed to capture_cmd.
+ kargs: Kargs passed to capture_cmd.
+ """
+ cmd_utils.execute(capture_cmd(*args, **kargs))
+
+
+def playback_cmd(playback_file, block_size=None, duration=None, pin_device=None, channels=2, rate=48000):
+ """Gets a command to playback a file with given settings.
+
+ Args:
+ playback_file: The name of the file to play. '-' indicates to playback raw audio from the stdin.
+ pin_device: The device id to playback on.
+ block_size: The number of frames per callback(dictates latency).
+ duration: Seconds to playback.
+ channels: Number of channels.
+ rate: The sampling rate.
+
+ Returns:
+ The command args put in a list of strings.
+ """
+ args = [_CRAS_TEST_CLIENT]
+ args += ['--playback_file', playback_file]
+ if pin_device is not None:
+ args += ['--pin_device', str(pin_device)]
+ if block_size is not None:
+ args += ['--block_size', str(block_size)]
+ if duration is not None:
+ args += ['--duration', str(duration)]
+ args += ['--num_channels', str(channels)]
+ args += ['--rate', str(rate)]
+ return args
+
+
+def capture_cmd(capture_file,
+ block_size=None,
+ duration=10,
+ sample_format='S16_LE',
+ pin_device=None,
+ channels=1,
+ rate=48000):
+ """Gets a command to capture the audio into the file with given settings.
+
+ Args:
+ capture_file: The name of file the audio to be stored in.
+ block_size: The number of frames per callback(dictates latency).
+ duration: Seconds to record. If it is None, duration is not set,
+ and command will keep capturing audio until it is terminated.
+ sample_format: The sample format; possible choices: 'S16_LE', 'S24_LE',
+ and 'S32_LE' default to S16_LE: signed 16 bits/sample, little endian.
+ pin_device: The device id to record from.
+ channels: Number of channels.
+ rate: The sampling rate.
+
+ Returns:
+ The command args put in a list of strings.
+ """
+ args = [_CRAS_TEST_CLIENT]
+ args += ['--capture_file', capture_file]
+ if pin_device is not None:
+ args += ['--pin_device', str(pin_device)]
+ if block_size is not None:
+ args += ['--block_size', str(block_size)]
+ if duration is not None:
+ args += ['--duration', str(duration)]
+ args += ['--num_channels', str(channels)]
+ args += ['--rate', str(rate)]
+ args += ['--format', str(sample_format)]
+ return args
+
+
+def listen_cmd(capture_file, block_size=None, duration=10, channels=1, rate=48000):
+ """Gets a command to listen on hotword and record audio into the file with given settings.
+
+ Args:
+ capture_file: The name of file the audio to be stored in.
+ block_size: The number of frames per callback(dictates latency).
+ duration: Seconds to record. If it is None, duration is not set, and command
+ will keep capturing audio until it is terminated.
+ channels: Number of channels.
+ rate: The sampling rate.
+
+ Returns:
+ The command args put in a list of strings.
+ """
+ args = [_CRAS_TEST_CLIENT]
+ args += ['--listen_for_hotword', capture_file]
+ if block_size is not None:
+ args += ['--block_size', str(block_size)]
+ if duration is not None:
+ args += ['--duration', str(duration)]
+ args += ['--num_channels', str(channels)]
+ args += ['--rate', str(rate)]
+ return args
+
+
+def loopback(*args, **kargs):
+ """A helper function to execute loopback_cmd.
+
+ Args:
+ args: Args passed to loopback_cmd.
+ kargs: Kargs passed to loopback_cmd.
+ """
+ cmd_utils.execute(loopback_cmd(*args, **kargs))
+
+
+def loopback_cmd(output_file, duration=10, channels=2, rate=48000):
+ """Gets a command to record the loopback.
+
+ Args:
+ output_file: The name of the file the loopback to be stored in.
+ channels: The number of channels of the recorded audio.
+ duration: Seconds to record.
+ rate: The sampling rate.
+
+ Returns:
+ The command args put in a list of strings.
+ """
+ args = [_CRAS_TEST_CLIENT]
+ args += ['--loopback_file', output_file]
+ args += ['--duration_seconds', str(duration)]
+ args += ['--num_channels', str(channels)]
+ args += ['--rate', str(rate)]
+ return args
+
+
+def get_cras_nodes_cmd():
+ """Gets a command to query the nodes from Cras.
+
+ Returns:
+ The command to query nodes information from Cras using dbus-send.
+ """
+ return ('dbus-send --system --type=method_call --print-reply '
+ '--dest=org.chromium.cras /org/chromium/cras '
+ 'org.chromium.cras.Control.GetNodes')
+
+
+def _dbus_uint64(x):
+ """Returns a UINT64 python-dbus object.
+
+ *Sometimes* python-dbus fails into the following cases:
+ - Attempt to convert a 64-bit integer into int32 and overflow
+ - Convert `dbus.UInt64(12345678900, variant_level=1)` (we usually get
+ this from some DBus calls) into VARIANT rather than UINT64
+
+ This function is a helper to avoid the above flakiness.
+ """
+ return dbus.types.UInt64(int(x), variant_level=0)
+
+
+def set_system_volume(volume):
+ """Sets the system volume.
+
+ Args:
+ volume: The system output vlume to be set(0 - 100).
+ """
+ get_cras_control_interface().SetOutputVolume(volume)
+
+
+def set_node_volume(node_id, volume):
+ """Sets the volume of the given output node.
+
+ Args:
+ node_id: The id of the output node to be set the volume.
+ volume: The volume to be set(0-100).
+ """
+ get_cras_control_interface().SetOutputNodeVolume(_dbus_uint64(node_id), volume)
+
+
+def get_cras_control_interface(private=False):
+ """Gets Cras DBus control interface.
+
+ Args:
+ private: Set to True to use a new instance for dbus.SystemBus instead of the shared instance.
+
+ Returns:
+ A dBus.Interface object with Cras Control interface.
+ """
+ bus = dbus.SystemBus(private=private)
+ cras_object = bus.get_object('org.chromium.cras', '/org/chromium/cras')
+ return dbus.Interface(cras_object, 'org.chromium.cras.Control')
+
+
+def get_cras_nodes():
+ """Gets nodes information from Cras.
+
+ Returns:
+ A dict containing information of each node.
+ """
+ return get_cras_control_interface().GetNodes()
+
+
+def get_selected_nodes():
+ """Gets selected output nodes and input nodes.
+
+ Returns:
+ A tuple (output_nodes, input_nodes) where each field is a list of selected
+ node IDs returned from Cras DBus API. Note that there may be multiple output/input
+ nodes being selected at the same time.
+ """
+ output_nodes = []
+ input_nodes = []
+ nodes = get_cras_nodes()
+ for node in nodes:
+ if node['Active']:
+ if node['IsInput']:
+ input_nodes.append(node['Id'])
+ else:
+ output_nodes.append(node['Id'])
+ return (output_nodes, input_nodes)
+
+
+def set_selected_output_node_volume(volume):
+ """Sets the selected output node volume.
+
+ Args:
+ volume: The volume to be set (0-100).
+ """
+ selected_output_node_ids, _ = get_selected_nodes()
+ for node_id in selected_output_node_ids:
+ set_node_volume(node_id, volume)
+
+
+def get_active_stream_count():
+ """Gets the number of active streams.
+
+ Returns:
+ The number of active streams.
+ """
+ return int(get_cras_control_interface().GetNumberOfActiveStreams())
+
+
+def set_system_mute(is_mute):
+ """Sets the system mute switch.
+
+ Args:
+ is_mute: Set True to mute the system playback.
+ """
+ get_cras_control_interface().SetOutputMute(is_mute)
+
+
+def set_capture_mute(is_mute):
+ """Sets the capture mute switch.
+
+ Args:
+ is_mute: Set True to mute the capture.
+ """
+ get_cras_control_interface().SetInputMute(is_mute)
+
+
+def node_type_is_plugged(node_type, nodes_info):
+ """Determines if there is any node of node_type plugged.
+
+ This method is used in the AudioLoopbackDongleLabel class, where the call is
+ executed on autotest server. Use get_cras_nodes instead if the call can be executed
+ on Cros device.
+
+ Since Cras only reports the plugged node in GetNodes, we can parse the return value
+ to see if there is any node with the given type. For example, if INTERNAL_MIC is of
+ intereset, the pattern we are looking for is:
+
+ dict entry(
+ string "Type"
+ variant string "INTERNAL_MIC"
+ )
+
+ Args:
+ node_type: A str representing node type defined in CRAS_NODE_TYPES.
+ nodes_info: A str containing output of command get_nodes_cmd.
+
+ Returns:
+ True if there is any node of node_type plugged. False otherwise.
+ """
+ match = re.search(r'string "Type"\s+variant\s+string "%s"' % node_type, nodes_info)
+ return True if match else False
+
+
+# Cras node types reported from Cras DBus control API.
+CRAS_OUTPUT_NODE_TYPES = [
+ 'HEADPHONE', 'INTERNAL_SPEAKER', 'HDMI', 'USB', 'BLUETOOTH', 'LINEOUT', 'UNKNOWN', 'ALSA_LOOPBACK'
+]
+CRAS_INPUT_NODE_TYPES = [
+ 'MIC', 'INTERNAL_MIC', 'USB', 'BLUETOOTH', 'POST_DSP_LOOPBACK', 'POST_MIX_LOOPBACK', 'UNKNOWN', 'KEYBOARD_MIC',
+ 'HOTWORD', 'FRONT_MIC', 'REAR_MIC', 'ECHO_REFERENCE'
+]
+CRAS_NODE_TYPES = CRAS_OUTPUT_NODE_TYPES + CRAS_INPUT_NODE_TYPES
+
+
+def get_filtered_node_types(callback):
+ """Returns the pair of filtered output node types and input node types.
+
+ Args:
+ callback: A callback function which takes a node as input parameter
+ and filter the node based on its return value.
+
+ Returns:
+ A tuple (output_node_types, input_node_types) where each field is
+ a list of node types defined in CRAS_NODE_TYPES, and their 'attribute_name' is True.
+ """
+ output_node_types = []
+ input_node_types = []
+ nodes = get_cras_nodes()
+ for node in nodes:
+ if callback(node):
+ node_type = str(node['Type'])
+ if node_type not in CRAS_NODE_TYPES:
+ logging.warning('node type %s is not in known CRAS_NODE_TYPES', node_type)
+ if node['IsInput']:
+ input_node_types.append(node_type)
+ else:
+ output_node_types.append(node_type)
+ return (output_node_types, input_node_types)
+
+
+def get_selected_node_types():
+ """Returns the pair of active output node types and input node types.
+
+ Returns:
+ A tuple (output_node_types, input_node_types) where each field is a list
+ of selected node types defined in CRAS_NODE_TYPES.
+ """
+
+ def is_selected(node):
+ """Checks if a node is selected.
+
+ A node is selected if its Active attribute is True.
+
+ Returns:
+ True is a node is selected, False otherwise.
+ """
+ return node['Active']
+
+ return get_filtered_node_types(is_selected)
+
+
+def get_selected_output_device_name():
+ """Returns the device name of the active output node.
+
+ Returns:
+ device name string. E.g. mtk-rt5650: :0,0.
+ """
+ nodes = get_cras_nodes()
+ for node in nodes:
+ if node['Active'] and not node['IsInput']:
+ return node['DeviceName']
+ return None
+
+
+def get_selected_output_device_type():
+ """Returns the device type of the active output node.
+
+ Returns:
+ device type string. E.g. INTERNAL_SPEAKER.
+ """
+ nodes = get_cras_nodes()
+ for node in nodes:
+ if node['Active'] and not node['IsInput']:
+ return node['Type']
+ return None
+
+
+def set_single_selected_output_node(node_type):
+ """Sets one selected output node.
+
+ Note that Chrome UI uses SetActiveOutputNode of Cras DBus API to select one output node.
+
+ Args:
+ node_type: A node type.
+
+ Returns:
+ True if the output node type is found and set active.
+ """
+ nodes = get_cras_nodes()
+ for node in nodes:
+ if node['IsInput']:
+ continue
+ if node['Type'] == node_type:
+ set_active_output_node(node['Id'])
+ return True
+ return False
+
+
+def set_selected_output_nodes(types):
+ """Sets selected output node types.
+
+ Note that Chrome UI uses SetActiveOutputNode of Cras DBus API to select one
+ output node. Here we use add/remove active output node to support multiple nodes.
+
+ Args:
+ types: A list of output node types.
+ """
+ nodes = get_cras_nodes()
+ for node in nodes:
+ if node['IsInput']:
+ continue
+ if node['Type'] in types:
+ add_active_output_node(node['Id'])
+ elif node['Active']:
+ remove_active_output_node(node['Id'])
+
+
+def set_active_output_node(node_id):
+ """Sets one active output node.
+
+ Args:
+ node_id: node id.
+ """
+ get_cras_control_interface().SetActiveOutputNode(_dbus_uint64(node_id))
+
+
+def add_active_output_node(node_id):
+ """Adds an active output node.
+
+ Args:
+ node_id: node id.
+ """
+ get_cras_control_interface().AddActiveOutputNode(_dbus_uint64(node_id))
+
+
+def remove_active_output_node(node_id):
+ """Removes an active output node.
+
+ Args:
+ node_id: node id.
+ """
+ get_cras_control_interface().RemoveActiveOutputNode(_dbus_uint64(node_id))
+
+
+def get_node_id_from_node_type(node_type, is_input):
+ """Gets node id from node type.
+
+ Args:
+ node_type: A node type defined in CRAS_NODE_TYPES.
+ is_input: True if the node is input. False otherwise.
+
+ Returns:
+ A string for node id.
+
+ Raises:
+ CrasUtilsError: if unique node id can not be found.
+ """
+ nodes = get_cras_nodes()
+ find_ids = []
+ for node in nodes:
+ if node['Type'] == node_type and node['IsInput'] == is_input:
+ find_ids.append(node['Id'])
+ if len(find_ids) != 1:
+ raise CrasUtilsError('Can not find unique node id from node type %s' % node_type)
+ return find_ids[0]
+
+
+def get_device_id_of(node_id):
+ """Gets the device id of the node id.
+
+ The conversion logic is replicated from the CRAS's type definition at
+ third_party/adhd/cras/src/common/cras_types.h.
+
+ Args:
+ node_id: A string for node id.
+
+ Returns:
+ A string for device id.
+
+ Raises:
+ CrasUtilsError: if device id is invalid.
+ """
+ device_id = str(int(node_id) >> 32)
+ if device_id == "0":
+ raise CrasUtilsError('Got invalid device_id: 0')
+ return device_id
+
+
+def get_device_id_from_node_type(node_type, is_input):
+ """Gets device id from node type.
+
+ Args:
+ node_type: A node type defined in CRAS_NODE_TYPES.
+ is_input: True if the node is input. False otherwise.
+
+ Returns:
+ A string for device id.
+ """
+ node_id = get_node_id_from_node_type(node_type, is_input)
+ return get_device_id_of(node_id)
+
+
+def set_floss_enabled(enabled):
+ """Sets whether CRAS stack expects to use Floss.
+
+ Args:
+ enabled: True for Floss, False for Bluez.
+ """
+ get_cras_control_interface().SetFlossEnabled(enabled)
+
+
+class CrasTestClient(object):
+ """An object to perform cras_test_client functions."""
+
+ BLOCK_SIZE = None
+ PIN_DEVICE = None
+ SAMPLE_FORMAT = 'S16_LE'
+ DURATION = 10
+ CHANNELS = 2
+ RATE = 48000
+
+ def __init__(self):
+ self._proc = None
+ self._capturing_proc = None
+ self._playing_proc = None
+ self._capturing_msg = 'capturing audio file'
+ self._playing_msg = 'playing audio file'
+ self._wbs_cmd = '%s --set_wbs_enabled ' % _CRAS_TEST_CLIENT
+ self._enable_wbs_cmd = ('%s 1' % self._wbs_cmd).split()
+ self._disable_wbs_cmd = ('%s 0' % self._wbs_cmd).split()
+ self._info_cmd = [
+ _CRAS_TEST_CLIENT,
+ ]
+ self._select_input_cmd = '%s --select_input ' % _CRAS_TEST_CLIENT
+
+ def start_subprocess(self, proc, proc_cmd, filename, proc_msg):
+ """Starts a capture or play subprocess
+
+ Args:
+ proc: The process.
+ proc_cmd: The process command and its arguments.
+ filename: The file name to capture or play.
+ proc_msg: The message to display in logging.
+
+ Returns:
+ True if the process is started successfully
+ """
+ if proc is None:
+ try:
+ self._proc = subprocess.Popen(proc_cmd)
+ logging.debug('Start %s %s on the DUT', proc_msg, filename)
+ except Exception as e:
+ logging.error('Failed to popen: %s (%s)', proc_msg, e)
+ return False
+ else:
+ logging.error('cannot run the command twice: %s', proc_msg)
+ return False
+ return True
+
+ def stop_subprocess(self, proc, proc_msg):
+ """Stops a subprocess
+
+ Args:
+ proc: The process to stop.
+ proc_msg: The message to display in logging.
+
+ Returns:
+ True if the process is stopped successfully.
+ """
+ if proc is None:
+ logging.error('cannot run stop %s before starting it.', proc_msg)
+ return False
+
+ proc.terminate()
+ try:
+ utils.poll_for_condition(condition=lambda: proc.poll() is not None,
+ exception=CrasUtilsError,
+ timeout=10,
+ sleep_interval=0.5,
+ desc='Waiting for subprocess to terminate')
+ except Exception:
+ logging.warn('Killing subprocess due to timeout')
+ proc.kill()
+ proc.wait()
+
+ logging.debug('stop %s on the DUT', proc_msg)
+ return True
+
+ def start_capturing_subprocess(self,
+ capture_file,
+ block_size=BLOCK_SIZE,
+ duration=DURATION,
+ pin_device=PIN_DEVICE,
+ sample_format=SAMPLE_FORMAT,
+ channels=CHANNELS,
+ rate=RATE):
+ """Starts capturing in a subprocess.
+
+ Args:
+ capture_file: The name of file the audio to be stored in.
+ block_size: The number of frames per callback(dictates latency).
+ duration: Seconds to record. If it is None, duration is not set, and
+ will keep capturing audio until terminated.
+ sample_format: The sample format.
+ pin_device: The device id to record from.
+ channels: Number of channels.
+ rate: The sampling rate.
+
+ Returns:
+ True if the process is started successfully.
+ """
+ proc_cmd = capture_cmd(capture_file,
+ block_size=block_size,
+ duration=duration,
+ sample_format=sample_format,
+ pin_device=pin_device,
+ channels=channels,
+ rate=rate)
+ result = self.start_subprocess(self._capturing_proc, proc_cmd, capture_file, self._capturing_msg)
+ if result:
+ self._capturing_proc = self._proc
+ return result
+
+ def stop_capturing_subprocess(self):
+ """Stops the capturing subprocess."""
+ result = self.stop_subprocess(self._capturing_proc, self._capturing_msg)
+ if result:
+ self._capturing_proc = None
+ return result
+
+ def start_playing_subprocess(self,
+ audio_file,
+ block_size=BLOCK_SIZE,
+ duration=DURATION,
+ pin_device=PIN_DEVICE,
+ channels=CHANNELS,
+ rate=RATE):
+ """Starts playing the audio file in a subprocess.
+
+ Args:
+ audio_file: The name of audio file to play.
+ block_size: The number of frames per callback(dictates latency).
+ duration: Seconds to play. If it is None, duration is not set, and
+ will keep playing audio until terminated.
+ pin_device: The device id to play to.
+ channels: Number of channels.
+ rate: The sampling rate.
+
+ Returns:
+ True if the process is started successfully.
+ """
+ proc_cmd = playback_cmd(audio_file, block_size, duration, pin_device, channels, rate)
+ result = self.start_subprocess(self._playing_proc, proc_cmd, audio_file, self._playing_msg)
+ if result:
+ self._playing_proc = self._proc
+ return result
+
+ def stop_playing_subprocess(self):
+ """Stops the playing subprocess."""
+ result = self.stop_subprocess(self._playing_proc, self._playing_msg)
+ if result:
+ self._playing_proc = None
+ return result
+
+ def play(self,
+ audio_file,
+ block_size=BLOCK_SIZE,
+ duration=DURATION,
+ pin_device=PIN_DEVICE,
+ channels=CHANNELS,
+ rate=RATE):
+ """Plays the audio file.
+
+ This method will get blocked until it has completed playing back. If you
+ do not want to get blocked, use start_playing_subprocess() above instead.
+
+ Args:
+ audio_file: The name of audio file to play.
+ block_size: The number of frames per callback(dictates latency).
+ duration: Seconds to play. If it is None, duration is not set, and
+ will keep playing audio until terminated.
+ pin_device: The device id to play to.
+ channels: Number of channels.
+ rate: The sampling rate.
+
+ Returns:
+ True if the process is started successfully.
+ """
+ proc_cmd = playback_cmd(audio_file, block_size, duration, pin_device, channels, rate)
+ try:
+ self._proc = subprocess.call(proc_cmd)
+ logging.debug('call "%s" on the DUT', proc_cmd)
+ except Exception as e:
+ logging.error('Failed to call: %s (%s)', proc_cmd, e)
+ return False
+ return True
+
+ def enable_wbs(self, value):
+ """Enables or disable wideband speech (wbs) per the value.
+
+ Args:
+ value: True to enable wbs.
+
+ Returns:
+ True if the operation succeeds.
+ """
+ cmd = self._enable_wbs_cmd if value else self._disable_wbs_cmd
+ logging.debug('call "%s" on the DUT', cmd)
+ if subprocess.call(cmd):
+ logging.error('Failed to call: %s (%s)', cmd)
+ return False
+ return True
+
+ def select_input_device(self, device_name):
+ """Selects the audio input device.
+
+ Args:
+ device_name: The name of the Bluetooth peer device.
+
+ Returns:
+ True if the operation succeeds.
+ """
+ logging.debug('to select input device for device_name: %s', device_name)
+ try:
+ info = subprocess.check_output(self._info_cmd)
+ logging.debug('info: %s', info)
+ except Exception as e:
+ logging.error('Failed to call: %s (%s)', self._info_cmd, e)
+ return False
+
+ flag_input_nodes = False
+ audio_input_node = None
+ for line in info.decode().splitlines():
+ if 'Input Nodes' in line:
+ flag_input_nodes = True
+ elif 'Attached clients' in line:
+ flag_input_nodes = False
+
+ if flag_input_nodes:
+ if device_name in line:
+ audio_input_node = line.split()[1]
+ logging.debug('%s', audio_input_node)
+ break
+
+ if audio_input_node is None:
+ logging.error('Failed to find audio input node: %s', device_name)
+ return False
+
+ select_input_cmd = (self._select_input_cmd + audio_input_node).split()
+ if subprocess.call(select_input_cmd):
+ logging.error('Failed to call: %s (%s)', select_input_cmd)
+ return False
+
+ logging.debug('call "%s" on the DUT', select_input_cmd)
+ return True
+
+ def set_player_playback_status(self, status):
+ """Sets playback status for the registered media player.
+
+ Args:
+ status: Playback status in string.
+ """
+ try:
+ get_cras_control_interface().SetPlayerPlaybackStatus(status)
+ except Exception as e:
+ logging.error('Failed to set player playback status: %s', e)
+ return False
+
+ return True
+
+ def set_player_position(self, position):
+ """Sets media position for the registered media player.
+
+ Args:
+ position: Position in micro seconds.
+ """
+ try:
+ get_cras_control_interface().SetPlayerPosition(position)
+ except Exception as e:
+ logging.error('Failed to set player position: %s', e)
+ return False
+
+ return True
+
+ def set_player_metadata(self, metadata):
+ """Sets title, artist, and album for the registered media player.
+
+ Args:
+ metadata: Dictionary of media metadata.
+ """
+ try:
+ get_cras_control_interface().SetPlayerMetadata(metadata)
+ except Exception as e:
+ logging.error('Failed to set player metadata: %s', e)
+ return False
+
+ return True
+
+ def set_player_length(self, length):
+ """Sets metadata length for the registered media player.
+
+ Media length is a part of metadata information. However, without specify
+ its type to int64. dbus-python will guess the variant type to be int32 by
+ default. Separate it from the metadata function to help prepare the data
+ differently.
+
+ Args:
+ length: DBUS dictionary that contains a variant of int64.
+ """
+ try:
+ get_cras_control_interface().SetPlayerMetadata(length)
+ except Exception as e:
+ logging.error('Failed to set player length: %s', e)
+ return False
+ return True
diff --git a/floss/pandora/floss/floss_enums.py b/floss/pandora/floss/floss_enums.py
index 01e8582..80e54a1 100644
--- a/floss/pandora/floss/floss_enums.py
+++ b/floss/pandora/floss/floss_enums.py
@@ -47,6 +47,31 @@
PHY_CODED = 3
+class GattPermission(enum.IntEnum):
+ """Bluetooth GATT permissions."""
+ PERM_READ = (1 << 0)
+ PERM_READ_ENCRYPTED = (1 << 1)
+ PERM_READ_ENC_MITM = (1 << 2)
+ PERM_WRITE = (1 << 4)
+ PERM_WRITE_ENCRYPTED = (1 << 5)
+ PERM_WRITE_ENC_MITM = (1 << 6)
+ PERM_WRITE_SIGNED = (1 << 7)
+ PERM_WRITE_SIGNED_MITM = (1 << 8)
+ PERM_READ_IF_ENCRYPTED_OR_DISCOVERABLE = (1 << 9)
+
+
+class GattCharacteristicProprieties(enum.IntEnum):
+ """Bluetooth GATT characteristic proprieties."""
+ CHAR_PROP_BIT_BROADCAST = (1 << 0)
+ CHAR_PROP_BIT_READ = (1 << 1)
+ CHAR_PROP_BIT_WRITE_NR = (1 << 2)
+ CHAR_PROP_BIT_WRITE = (1 << 3)
+ CHAR_PROP_BIT_NOTIFY = (1 << 4)
+ CHAR_PROP_BIT_INDICATE = (1 << 5)
+ CHAR_PROP_BIT_AUTH = (1 << 6)
+ CHAR_PROP_BIT_EXT_PROP = (1 << 7)
+
+
class GattStatus(enum.IntEnum):
"""Bluetooth GATT return status."""
SUCCESS = 0x00
diff --git a/floss/pandora/floss/gatt_server.py b/floss/pandora/floss/gatt_server.py
index a3b21cb..f23e042 100644
--- a/floss/pandora/floss/gatt_server.py
+++ b/floss/pandora/floss/gatt_server.py
@@ -1,20 +1,22 @@
# Copyright 2023 Google LLC
#
-# Licensed under the Apache License, Version 2.0 (the "License");
+# 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
#
# https://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,
+# 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.
"""Client Class to access the Floss GATT server interface."""
+import collections
import logging
+from floss.pandora.floss import bluetooth_gatt_service
from floss.pandora.floss import floss_enums
from floss.pandora.floss import observer_base
from floss.pandora.floss import utils
@@ -90,31 +92,31 @@
"""
pass
- def on_characteristic_write_request(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+ def on_characteristic_write_request(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
"""Called when there is a request to write a characteristic.
Args:
addr: Remote device MAC address.
trans_id: Transaction id.
offset: Represents the offset at which the attribute value should be written.
- len: The length of the attribute value that should be written.
- is_prep: A boolean value representing whether it's a "prepare write" command.
- need_rsp: A boolean value representing whether it's a "write no response" command.
+ length: The length of the attribute value that should be written.
+ is_prep: A boolean value representing whether it's a 'prepare write' command.
+ need_rsp: A boolean value representing whether it's a 'write no response' command.
handle: The characteristic handle.
value: The value that should be written to the attribute.
"""
pass
- def on_descriptor_write_request(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+ def on_descriptor_write_request(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
"""Called when there is a request to write a descriptor.
Args:
addr: Remote device MAC address.
trans_id: Transaction id.
offset: The offset value at which the value should be written.
- len: The length of the value that should be written.
- is_prep: A boolean value representing whether it's a "prepare write" command.
- need_rsp: A boolean value representing whether it's a "write no response" command.
+ length: The length of the value that should be written.
+ is_prep: A boolean value representing whether it's a 'prepare write' command.
+ need_rsp: A boolean value representing whether it's a 'write no response' command.
handle: The descriptor handle.
value: The value that should be written to the descriptor.
"""
@@ -196,6 +198,18 @@
pass
+def uuid16_to_uuid128(uuid):
+ """Converts 16-bit UUID to 128-bit UUID.
+
+ Args:
+ uuid: 16-bit UUID value.
+
+ Returns:
+ 128-bit UUID.
+ """
+ return utils.uuid16_to_uuid128(uuid).upper()
+
+
class FlossGattServer(GattServerCallbacks):
"""Handles method calls and callbacks from the GATT server interface."""
@@ -205,6 +219,48 @@
GATT_CB_OBJ_NAME = 'test_gatt_server'
CB_EXPORTED_INTF = 'org.chromium.bluetooth.BluetoothGattServerCallback'
+ # Bluetooth GATT DataBase attribute type.
+ BTGATT_DB_PRIMARY_SERVICE = 0
+ BTGATT_DB_SECONDARY_SERVICE = 1
+ BTGATT_DB_INCLUDED_SERVICE = 2
+ BTGATT_DB_CHARACTERISTIC = 3
+ BTGATT_DB_DESCRIPTOR = 4
+
+ GATT_READ_AUTH_REQUIRED = floss_enums.GattPermission.PERM_READ_ENCRYPTED
+ GATT_READ_MITM_REQUIRED = floss_enums.GattPermission.PERM_READ_ENC_MITM
+ GATT_WRITE_AUTH_REQUIRED = (floss_enums.GattPermission.PERM_WRITE_ENCRYPTED |
+ floss_enums.GattPermission.PERM_WRITE_SIGNED)
+ GATT_WRITE_MITM_REQUIRED = (floss_enums.GattPermission.PERM_WRITE_ENC_MITM |
+ floss_enums.GattPermission.PERM_WRITE_SIGNED_MITM)
+
+ SERVICE_ATTR_UUID = uuid16_to_uuid128('7777')
+ SERVER_CHANGED_CHAR_UUID = uuid16_to_uuid128('2a05')
+ SERVER_SUP_FEAT_UUID = uuid16_to_uuid128('2b3a')
+ CLIENT_SUP_FEAT_UUID = uuid16_to_uuid128('2b29')
+ DATABASE_HASH_UUID = uuid16_to_uuid128('2b2a')
+ LONG_CHAR_UUID = uuid16_to_uuid128('b000')
+ DESCRIPTOR_UUID = uuid16_to_uuid128('b001')
+ KEY_SIZE_CHAR_UUID = uuid16_to_uuid128('b002')
+ UNAUTHORIZED_CHAR_UUID = uuid16_to_uuid128('b003')
+ AUTHENTICATION_CHAR_UUID = uuid16_to_uuid128('b004')
+ INVALID_FOR_LE_UUID = uuid16_to_uuid128('b005')
+ INVALID_FOR_BR_EDR_UUID = uuid16_to_uuid128('b006')
+ NO_READ_CHAR_UUID = uuid16_to_uuid128('b007')
+ NO_WRITE_CHAR_UUID = uuid16_to_uuid128('b008')
+ SHORT_CHAR_UUID = uuid16_to_uuid128('b009')
+ WRITE_NO_RESPONSE_CHAR_UUID = uuid16_to_uuid128('b00a')
+ NOTIFY_CHAR_UUID = uuid16_to_uuid128('b00b')
+ AUTHENTICATE_SHORT_CHAR_UUID = uuid16_to_uuid128('b00c')
+ CCC_DESCRIPTOR_UUID = uuid16_to_uuid128('2902')
+ LONG_CHAR_512_UUID = uuid16_to_uuid128('b00d')
+ MITM_SHORT_CHAR_UUID = uuid16_to_uuid128('b00e')
+
+ LONG_TEST_VALUE_512 = [bytes([7])] * 512
+ LONG_TEST_VALUE = [bytes([7])] * 48
+ SHORT_TEST_VALUE = [bytes([7])] * 2
+ NOTIFICATION_VALUE = [bytes([1]), bytes([0])]
+ INDICATION_VALUE = [bytes([2]), bytes([0])]
+
class ExportedGattServerCallbacks(observer_base.ObserverBase):
"""
<node>
@@ -244,7 +300,7 @@
<arg type="s" name="addr" direction="in" />
<arg type="i" name="trans_id" direction="in" />
<arg type="i" name="offset" direction="in" />
- <arg type="i" name="len" direction="in" />
+ <arg type="i" name="length" direction="in" />
<arg type="b" name="is_prep" direction="in" />
<arg type="b" name="need_rsp" direction="in" />
<arg type="i" name="handle" direction="in" />
@@ -254,7 +310,7 @@
<arg type="s" name="addr" direction="in" />
<arg type="i" name="trans_id" direction="in" />
<arg type="i" name="offset" direction="in" />
- <arg type="i" name="len" direction="in" />
+ <arg type="i" name="length" direction="in" />
<arg type="b" name="is_prep" direction="in" />
<arg type="b" name="need_rsp" direction="in" />
<arg type="i" name="handle" direction="in" />
@@ -363,6 +419,23 @@
for observer in self.observers.values():
observer.on_characteristic_read_request(addr, trans_id, offset, is_long, handle)
+ def OnCharacteristicWriteRequest(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
+ """Handles characteristic write request callback.
+
+ Args:
+ addr: Remote device MAC address.
+ trans_id: Transaction id.
+ offset: Represents the offset from which the attribute value should be read.
+ length: The length of the value that should be written.
+ is_prep: A boolean value representing whether it's a 'prepare write' command.
+ need_rsp: A boolean value representing whether it's a 'write no response' command.
+ handle: The descriptor handle.
+ value: The value that should be written to the descriptor.
+ """
+ for observer in self.observers.values():
+ observer.on_characteristic_write_request(addr, trans_id, offset, length, is_prep, need_rsp, handle,
+ value)
+
def OnDescriptorReadRequest(self, addr, trans_id, offset, is_long, handle):
"""Handles descriptor read request callback.
@@ -377,37 +450,21 @@
for observer in self.observers.values():
observer.on_descriptor_read_request(addr, trans_id, offset, is_long, handle)
- def OnCharacteristicWrite(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
- """Handles characteristic write request callback.
-
- Args:
- addr: Remote device MAC address.
- trans_id: Transaction id.
- offset: Represents the offset at which the attribute value should be written.
- len: The length of the attribute value that should be written.
- is_prep: A boolean value representing whether it's a "prepare write" command.
- need_rsp: A boolean value representing whether it's a "write no response" command.
- handle: The characteristic handle.
- value: The value that should be written to the attribute.
- """
- for observer in self.observers.values():
- observer.on_characteristic_write_request(addr, trans_id, offset, len, is_prep, need_rsp, handle, value)
-
- def OnDescriptorWriteRequest(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+ def OnDescriptorWriteRequest(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
"""Handles descriptor write request callback.
Args:
addr: Remote device MAC address.
trans_id: Transaction id.
offset: The offset value at which the value should be written.
- len: The length of the value that should be written.
- is_prep: A boolean value representing whether it's a "prepare write" command.
- need_rsp: A boolean value representing whether it's a "write no response" command.
+ length: The length of the value that should be written.
+ is_prep: A boolean value representing whether it's a 'prepare write' command.
+ need_rsp: A boolean value representing whether it's a 'write no response' command.
handle: The descriptor handle.
value: The value that should be written to the descriptor.
"""
for observer in self.observers.values():
- observer.on_descriptor_write_request(addr, trans_id, offset, len, is_prep, need_rsp, handle, value)
+ observer.on_descriptor_write_request(addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
def OnExecuteWrite(self, addr, trans_id, exec_write):
"""Handles execute write callback.
@@ -505,17 +562,527 @@
self.objpath = self.GATT_OBJECT_PATTERN.format(hci)
self.cb_dbus_objpath = utils.generate_dbus_cb_objpath(self.GATT_CB_OBJ_NAME, self.hci)
- # Create and publish callbacks
+ # Create and publish callbacks.
self.callbacks = self.ExportedGattServerCallbacks()
self.callbacks.add_observer('gatt_testing_server', self)
self.bus.register_object(self.cb_dbus_objpath, self.callbacks, None)
- self.server_connect_id = None
self.server_id = None
+ self.mtu_value = -1
+ self.write_requests = collections.deque([])
+ self.gatt_services = []
+ # Indicate if PTS attribute values were set or not (set only one time).
+ self.pts_set_values = False
def __del__(self):
"""Destructor."""
del self.callbacks
+ def check_permissions(self, uuid):
+ """Checks request UUID permission.
+
+ Args:
+ uuid: 128-bit UUID value as string.
+
+ Returns:
+ GATT status.
+ """
+ if uuid is None:
+ logging.debug('check_permissions uuid is None or value is None')
+ return floss_enums.GattStatus.NOT_FOUND
+ elif uuid == self.LONG_CHAR_UUID:
+ logging.debug('check_permissions: uuid == long_char, return GATT_SUCCESS')
+ elif uuid == self.DESCRIPTOR_UUID:
+ logging.debug('check_permissions: uuid == descriptor, return GATT_SUCCESS')
+ elif uuid == self.UNAUTHORIZED_CHAR_UUID:
+ logging.debug('check_permissions: uuid == unauthorize_char, return GATT_INSUF_AUTHORIZATION')
+ return floss_enums.GattStatus.INSUF_AUTHORIZATION
+ elif uuid == self.AUTHENTICATION_CHAR_UUID:
+ logging.debug('check_permissions: uuid == authenticate_char, return GATT_SUCCESS')
+ elif uuid == self.INVALID_FOR_LE_UUID:
+ logging.debug('check_permissions: uuid == invalid_for_le, return GATT_SUCCESS')
+ return floss_enums.GattStatus.INTERNAL_ERROR
+ elif uuid == self.INVALID_FOR_BR_EDR_UUID:
+ logging.debug('check_permissions: uuid == invalid_for_bredr, return GATT_SUCCESS')
+ return floss_enums.GattStatus.INTERNAL_ERROR
+ elif uuid == self.NO_READ_CHAR_UUID:
+ logging.debug('check_permissions: uuid == no_read_char, return GATT_READ_NOT_PERMIT')
+ return floss_enums.GattStatus.READ_NOT_PERMIT
+ elif uuid == self.NO_WRITE_CHAR_UUID:
+ logging.debug('check_permissions: uuid == no_write_char, return GATT_WRITE_NOT_PERMIT')
+ return floss_enums.GattStatus.WRITE_NOT_PERMIT
+ elif uuid == self.SHORT_CHAR_UUID:
+ logging.debug('check_permissions: uuid == short_char, return GATT_SUCCESS')
+ elif uuid == self.WRITE_NO_RESPONSE_CHAR_UUID:
+ logging.debug('check_permissions: uuid == write_no_rsp_char, return GATT_SUCCESS')
+ elif uuid == self.AUTHENTICATE_SHORT_CHAR_UUID:
+ logging.debug('check_permissions: uuid == authenticate_short_char, return GATT_SUCCESS')
+ elif uuid == self.CCC_DESCRIPTOR_UUID:
+ logging.debug('check_permissions: uuid == ccc_descriptor, return GATT_SUCCESS')
+ elif uuid == self.LONG_CHAR_512_UUID:
+ logging.debug('check_permissions: uuid == long_char512, return GATT_SUCCESS')
+ elif uuid == self.MITM_SHORT_CHAR_UUID:
+ logging.debug('check_permissions: uuid == mitm_short_char, return GATT_SUCCESS')
+ else:
+ logging.debug('check_permissions: uuid: %s unknown return GATT_NOT_FOUND', uuid)
+ return floss_enums.GattStatus.NOT_FOUND
+ return floss_enums.GattStatus.SUCCESS
+
+ def generic_write(self, offset, length, handle, value):
+ """Writes GATT attribute value.
+
+ Args:
+ offset: Represents the offset at which the attribute value should be written.
+ length: The length of the attribute value that should be written.
+ handle: The attribute handle.
+ value: The value that should be written to the attribute.
+
+ Returns:
+ (Bluetooth Gatt status, attribute value).
+ """
+ attribute = self.get_attribute_from_handle(handle)
+ attr_value = attribute.value if attribute is not None else []
+
+ if len(attr_value) < offset:
+ logging.info('len(char_value) < offset')
+ return floss_enums.GattStatus.INVALID_OFFSET, []
+
+ if offset + length > len(attr_value):
+ logging.info('offset + len > len(char_value)')
+ return floss_enums.GattStatus.INVALID_ATTRLEN, []
+
+ attr_value[offset:(offset + length)] = value
+ self.update_attribute_value(attribute.uuid, attr_value)
+ return floss_enums.GattStatus.SUCCESS, attr_value
+
+ def generic_read(self, offset, handle):
+ """Reads GATT attribute value.
+
+ Args:
+ offset: Represents the offset from which the attribute value should be read.
+ handle: The attribute handle.
+
+ Returns:
+ (Bluetooth Gatt status, attribute value).
+ """
+ attr_value = self.get_attribute_value_from_handle(handle)
+
+ if offset < 0 or offset > len(attr_value):
+ logging.info('generic_read len(value) < offset')
+ return floss_enums.GattStatus.INVALID_OFFSET, []
+
+ return floss_enums.GattStatus.SUCCESS, attr_value[offset:]
+
+ def get_uuid_from_handle(self, handle):
+ """Gets attribute UUID from handle.
+
+ Args:
+ handle: The attribute handle.
+
+ Returns:
+ Attribute UUID as string if found, empty string otherwise.
+ """
+ attribute = self.get_attribute_from_handle(handle)
+ return '' if attribute is None else attribute.uuid
+
+ def get_attribute_value_from_handle(self, handle):
+ """Gets attribute value from handle.
+
+ Args:
+ handle: The attribute handle.
+
+ Returns:
+ Attribute value as list if found, empty list otherwise.
+ """
+ attribute = self.get_attribute_from_handle(handle)
+ return [] if attribute is None else attribute.value
+
+ def get_attribute_from_handle(self, handle):
+ """Gets GATT attribute from handle.
+
+ Args:
+ handle: The attribute handle.
+
+ Returns:
+ GATT attribute if found, None otherwise.
+ """
+ for service in self.gatt_services:
+ if int(service.instance_id) == int(handle):
+ return service
+ for char in service.characteristics:
+ if int(char.instance_id) == int(handle):
+ return char
+ for desc in char.descriptors:
+ if int(desc.instance_id) == int(handle):
+ return desc
+
+ return None
+
+ def update_attribute_value(self, uuid, value):
+ """Update GATT attribute value.
+
+ Args:
+ uuid: GATT attribute uuid as string.
+ value: Attribute value as list.
+ """
+ for service in self.gatt_services:
+ if service.uuid == uuid:
+ service.value = value
+ return
+ for char in service.characteristics:
+ if char.uuid == uuid:
+ char.value = value
+ return
+ for desc in char.descriptors:
+ if desc.uuid == uuid:
+ desc.value = value
+ return
+ logging.error('No attribute found with uuid = %s!', uuid)
+
+ def on_attr_read(self, addr, trans_id, offset, is_long, handle):
+ """Handles the read request for GATT attribute.
+
+ Args:
+ addr: Remote device MAC address.
+ trans_id: Transaction id.
+ offset: Represents the offset from which the attribute value should be read.
+ is_long: A boolean value representing whether the attribute size is longer than what we can put in the ATT
+ PDU.
+ handle: The attribute handle.
+ """
+ uuid = self.get_uuid_from_handle(handle)
+ status = self.check_permissions(uuid)
+ value = []
+ if status == floss_enums.GattStatus.SUCCESS:
+ if not is_long:
+ offset = 0
+ status, value = self.generic_read(offset, handle)
+
+ self.proxy().SendResponse(self.server_id, addr, trans_id, status, offset, value)
+
+ def on_attr_write(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
+ """Handles the read request for GATT attribute.
+
+ Args:
+ addr: Remote device MAC address.
+ trans_id: Transaction id.
+ offset: Represents the offset at which the attribute value should be written.
+ length: The length of the attribute value that should be written.
+ is_prep: A boolean value representing whether it's a 'prepare write' command.
+ need_rsp: A boolean value representing whether it's a 'write no response' command.
+ handle: The attribute handle.
+ value: The value that should be written to the attribute.
+ """
+ uuid = self.get_uuid_from_handle(handle)
+ status = self.check_permissions(uuid)
+
+ if status == floss_enums.GattStatus.SUCCESS:
+ if is_prep:
+ self.write_requests.append((addr, trans_id, offset, length, is_prep, need_rsp, handle, value))
+ else:
+ # write request.
+ status, value = self.generic_write(offset, length, handle, value)
+ else:
+ value = []
+
+ if need_rsp:
+ self.proxy().SendResponse(self.server_id, addr, trans_id, status, offset, value)
+ else:
+ logging.info('No need to send response.')
+
+ def __set_pts_attributes_value(self):
+ """Sets PTS attributes value."""
+ if not self.pts_set_values:
+ self.update_attribute_value(self.SERVICE_ATTR_UUID, self.SHORT_TEST_VALUE)
+ self.update_attribute_value(self.SERVER_CHANGED_CHAR_UUID, self.SHORT_TEST_VALUE)
+ self.update_attribute_value(self.SERVER_SUP_FEAT_UUID, self.SHORT_TEST_VALUE)
+ self.update_attribute_value(self.CLIENT_SUP_FEAT_UUID, self.SHORT_TEST_VALUE)
+ self.update_attribute_value(self.DATABASE_HASH_UUID, self.SHORT_TEST_VALUE)
+ self.update_attribute_value(self.LONG_CHAR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.DESCRIPTOR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.KEY_SIZE_CHAR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.UNAUTHORIZED_CHAR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.AUTHENTICATION_CHAR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.INVALID_FOR_LE_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.INVALID_FOR_BR_EDR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.NO_READ_CHAR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.NO_WRITE_CHAR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.SHORT_CHAR_UUID, self.SHORT_TEST_VALUE)
+ self.update_attribute_value(self.WRITE_NO_RESPONSE_CHAR_UUID, self.LONG_TEST_VALUE)
+ self.update_attribute_value(self.NOTIFY_CHAR_UUID, self.SHORT_TEST_VALUE)
+ self.update_attribute_value(self.AUTHENTICATE_SHORT_CHAR_UUID, self.SHORT_TEST_VALUE)
+ self.update_attribute_value(self.CCC_DESCRIPTOR_UUID, self.INDICATION_VALUE)
+ self.update_attribute_value(self.LONG_CHAR_512_UUID, self.LONG_TEST_VALUE_512)
+ self.update_attribute_value(self.MITM_SHORT_CHAR_UUID, self.SHORT_TEST_VALUE)
+ self.pts_set_values = True
+
+ def __define_services(self):
+ """Defines GATT services for PTS testing."""
+
+ service = bluetooth_gatt_service.Service()
+ characteristic = bluetooth_gatt_service.Characteristic()
+ descriptor = bluetooth_gatt_service.Descriptor()
+ service.included_services = []
+ service.characteristics = []
+ characteristic.descriptors = []
+
+ service.uuid = self.SERVICE_ATTR_UUID
+ service.instance_id = 0
+ service.service_type = self.BTGATT_DB_PRIMARY_SERVICE
+
+ characteristic.uuid = self.SERVER_CHANGED_CHAR_UUID
+ characteristic.instance_id = 1
+ characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_INDICATE
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+
+ descriptor.uuid = self.CCC_DESCRIPTOR_UUID
+ descriptor.instance_id = 2
+ descriptor.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.descriptors.append(descriptor)
+
+ service.characteristics.append(characteristic)
+
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.SERVER_SUP_FEAT_UUID
+ characteristic.instance_id = 3
+ characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.INVALID
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.CLIENT_SUP_FEAT_UUID
+ characteristic.instance_id = 4
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.INVALID
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.DATABASE_HASH_UUID
+ characteristic.instance_id = 5
+ characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.INVALID
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.DATABASE_HASH_UUID
+ characteristic.instance_id = 5
+ characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.INVALID
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ descriptor = bluetooth_gatt_service.Descriptor()
+ characteristic.uuid = self.LONG_CHAR_UUID
+ characteristic.instance_id = 6
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ descriptor.uuid = self.DESCRIPTOR_UUID
+ descriptor.instance_id = 7
+ descriptor.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.descriptors.append(descriptor)
+ service.characteristics.append(characteristic)
+
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.KEY_SIZE_CHAR_UUID
+ characteristic.instance_id = 8
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+ characteristic.permissions = (
+ 10 << 12) | floss_enums.GattPermission.PERM_READ_ENCRYPTED | floss_enums.GattPermission.PERM_WRITE_ENCRYPTED
+ characteristic.key_size = 10
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.UNAUTHORIZED_CHAR_UUID
+ characteristic.instance_id = 9
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.AUTHENTICATION_CHAR_UUID
+ characteristic.instance_id = 10
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_AUTH)
+ characteristic.permissions = self.GATT_READ_AUTH_REQUIRED | self.GATT_WRITE_AUTH_REQUIRED
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.INVALID_FOR_LE_UUID
+ characteristic.instance_id = 11
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.INVALID_FOR_BR_EDR_UUID
+ characteristic.instance_id = 12
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.NO_READ_CHAR_UUID
+ characteristic.instance_id = 13
+ # Only write property.
+ characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE
+ # Only write permission.
+ characteristic.permissions = floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.NO_WRITE_CHAR_UUID
+ characteristic.instance_id = 14
+ # Only read property.
+ characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ
+ # Only read permission.
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.INVALID
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.SHORT_CHAR_UUID
+ characteristic.instance_id = 15
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.WRITE_NO_RESPONSE_CHAR_UUID
+ characteristic.instance_id = 16
+ # Write without response property.
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE_NR)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE_NO_RSP
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ descriptor = bluetooth_gatt_service.Descriptor()
+ characteristic.uuid = self.NOTIFY_CHAR_UUID
+ characteristic.instance_id = 17
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_NOTIFY |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_INDICATE)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ descriptor.uuid = self.CCC_DESCRIPTOR_UUID
+ descriptor.instance_id = 18
+ descriptor.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+
+ characteristic.descriptors.append(descriptor)
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.AUTHENTICATE_SHORT_CHAR_UUID
+ characteristic.instance_id = 19
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_AUTH)
+ characteristic.permissions = self.GATT_READ_AUTH_REQUIRED | self.GATT_WRITE_AUTH_REQUIRED
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.LONG_CHAR_512_UUID
+ characteristic.instance_id = 20
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+ characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ characteristic = bluetooth_gatt_service.Characteristic()
+ characteristic.uuid = self.MITM_SHORT_CHAR_UUID
+ characteristic.instance_id = 21
+ characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE |
+ floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_AUTH)
+ characteristic.permissions = self.GATT_READ_MITM_REQUIRED | self.GATT_WRITE_MITM_REQUIRED
+ characteristic.key_size = 0
+ characteristic.write_type = floss_enums.GattWriteType.WRITE
+ characteristic.descriptors = []
+ service.characteristics.append(characteristic)
+ # ----------------------------------------------------
+ service_dict = bluetooth_gatt_service.convert_object_to_dict(service)
+ self.proxy().AddService(self.server_id, self.make_dbus_service(service_dict))
+
@utils.glib_callback()
def on_server_registered(self, status, server_id):
"""Handles server registration callback.
@@ -530,6 +1097,7 @@
logging.error('Failed to register server with id: %s, status = %s', server_id, status)
return
self.server_id = server_id
+ self.__define_services()
@utils.glib_callback()
def on_server_connection_state(self, server_id, connected, addr):
@@ -551,8 +1119,15 @@
status: floss_enums.GattStatus.
service: BluetoothGattService.
"""
+ if status != floss_enums.GattStatus.SUCCESS:
+ return
+ self.gatt_services.append(bluetooth_gatt_service.create_gatt_service(service))
+
logging.debug('on_service_added: status: %s, service: %s', status, service)
+ # This function will run once when the server is initiated.
+ self.__set_pts_attributes_value()
+
@utils.glib_callback()
def on_service_removed(self, status, handle):
"""Handles service removed callback.
@@ -579,6 +1154,8 @@
'on_characteristic_read_request: device address: %s, trans_id: %s, offset: %s, is_long: %s, handle: %s',
addr, trans_id, offset, is_long, handle)
+ self.on_attr_read(addr, trans_id, offset, is_long, handle)
+
@utils.glib_callback()
def on_descriptor_read_request(self, addr, trans_id, offset, is_long, handle):
"""Handles the read request of the descriptor callback.
@@ -594,42 +1171,45 @@
logging.debug(
'on_descriptor_read_request: device address: %s, trans_id: %s, offset: %s, is_long: %s, handle: %s', addr,
trans_id, offset, is_long, handle)
+ self.on_attr_read(addr, trans_id, offset, is_long, handle)
@utils.glib_callback()
- def on_characteristic_write_request(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+ def on_characteristic_write_request(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
"""Handles the write request of the characteristic callback.
Args:
addr: Remote device MAC address.
trans_id: Transaction id.
offset: Represents the offset at which the attribute value should be written.
- len: The length of the attribute value that should be written.
- is_prep: A boolean value representing whether it's a "prepare write" command.
- need_rsp: A boolean value representing whether it's a "write no response" command.
+ length: The length of the attribute value that should be written.
+ is_prep: A boolean value representing whether it's a 'prepare write' command.
+ need_rsp: A boolean value representing whether it's a 'write no response' command.
handle: The characteristic handle.
value: The value that should be written to the attribute.
"""
logging.debug(
'on_characteristic_write_request: device address: %s, trans_id: %s, offset: %s, length: %s, is_prep: %s, '
- 'need_rsp: %s, handle: %s, values: %s', addr, trans_id, offset, len, is_prep, need_rsp, handle, value)
+ 'need_rsp: %s, handle: %s, values: %s', addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
+ self.on_attr_write(addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
@utils.glib_callback()
- def on_descriptor_write_request(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+ def on_descriptor_write_request(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
"""Handles the write request of the descriptor callback.
Args:
addr: Remote device MAC address.
trans_id: Transaction id.
offset: The offset value at which the value should be written.
- len: The length of the value that should be written.
- is_prep: A boolean value representing whether it's a "prepare write" command.
- need_rsp: A boolean value representing whether it's a "write no response" command.
+ length: The length of the value that should be written.
+ is_prep: A boolean value representing whether it's a 'prepare write' command.
+ need_rsp: A boolean value representing whether it's a 'write no response' command.
handle: The descriptor handle.
value: The value that should be written to the descriptor.
"""
logging.debug(
'on_descriptor_write_request: device address: %s, trans_id: %s, offset: %s, length: %s, is_prep: %s, '
- 'need_rsp: %s, handle: %s, values: %s', addr, trans_id, offset, len, is_prep, need_rsp, handle, value)
+ 'need_rsp: %s, handle: %s, values: %s', addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
+ self.on_attr_write(addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
@utils.glib_callback()
def on_execute_write(self, addr, trans_id, exec_write):
@@ -642,6 +1222,19 @@
"""
logging.debug('on_execute_write: device address: %s, trans_id: %s, exec_write: %s', addr, trans_id, exec_write)
+ if not exec_write:
+ self.write_requests.clear()
+ status = floss_enums.GattStatus.SUCCESS
+ else:
+ write_requests, self.write_requests = self.write_requests, collections.deque([])
+ for request in write_requests:
+ _, _, offset2, length, _, _, handle2, value2 = request
+ status, _ = self.generic_write(offset2, length, handle2, value2)
+ if status != floss_enums.GattStatus.SUCCESS:
+ break
+
+ self.proxy().SendResponse(self.server_id, addr, trans_id, status, 0, [])
+
@utils.glib_callback()
def on_notification_sent(self, addr, status):
"""Handles notification sent callback.
@@ -661,6 +1254,7 @@
mtu: Maximum transmission unit.
"""
logging.debug('on_mtu_changed: device address: %s, mtu : %s', addr, mtu)
+ self.mtu_value = mtu
@utils.glib_callback()
def on_phy_update(self, addr, tx_phy, rx_phy, status):
@@ -898,15 +1492,16 @@
"""Makes struct for descriptor D-Bus.
Args:
- uuid : Descriptor UUID as array of bytes.
+ uuid : Descriptor UUID as string.
instance_id: Descriptor identifier.
permissions: Descriptor permissions.
Returns:
Dictionary of descriptor.
"""
+ desc_uuid = utils.get_uuid_as_list(uuid)
return {
- 'uuid': GLib.Variant('ay', uuid),
+ 'uuid': GLib.Variant('ay', desc_uuid),
'instance_id': GLib.Variant('i', instance_id),
'permissions': GLib.Variant('i', permissions)
}
@@ -915,7 +1510,7 @@
"""Makes struct for characteristic D-Bus.
Args:
- uuid : Characteristic UUID as array of bytes.
+ uuid : Characteristic UUID as string.
instance_id: Characteristic handle id.
properties: Characteristic properties.
permissions: Characteristic permissions.
@@ -929,8 +1524,9 @@
desc = []
for d in descriptors:
desc.append(self.make_dbus_descriptor(d['uuid'], d['instance_id'], d['permissions']))
+ char_uuid = utils.get_uuid_as_list(uuid)
return {
- 'uuid': GLib.Variant('ay', uuid),
+ 'uuid': GLib.Variant('ay', char_uuid),
'instance_id': GLib.Variant('i', instance_id),
'properties': GLib.Variant('i', properties),
'permissions': GLib.Variant('i', permissions),
@@ -957,9 +1553,9 @@
included_services = []
for s in service['included_services']:
included_services.append(self.make_dbus_service(s))
-
+ service_uuid = utils.get_uuid_as_list(service['uuid'])
return {
- 'uuid': GLib.Variant('ay', service['uuid']),
+ 'uuid': GLib.Variant('ay', service_uuid),
'instance_id': GLib.Variant('i', service['instance_id']),
'service_type': GLib.Variant('i', service['service_type']),
'characteristics': GLib.Variant('aa{sv}', characteristics),
diff --git a/floss/pandora/floss/media_client.py b/floss/pandora/floss/media_client.py
index 6c42378..83dbe1e 100644
--- a/floss/pandora/floss/media_client.py
+++ b/floss/pandora/floss/media_client.py
@@ -182,6 +182,7 @@
self.bus = bus
self.hci = hci
self.objpath = self.MEDIA_OBJECT_PATTERN.format(hci)
+ self.devices = []
# We don't register callbacks by default.
self.callbacks = None
@@ -198,6 +199,9 @@
device: The struct of BluetoothAudioDevice.
"""
logging.debug('on_bluetooth_audio_device_added: device: %s', device)
+ if device['address'] in self.devices:
+ logging.debug("Device already added")
+ self.devices.append(device['address'])
@utils.glib_callback()
def on_bluetooth_audio_device_removed(self, addr):
@@ -207,6 +211,8 @@
addr: Address of device to be removed.
"""
logging.debug('on_bluetooth_audio_device_removed: address: %s', addr)
+ if addr in self.devices:
+ self.devices.remove(addr)
@utils.glib_callback()
def on_absolute_volume_supported_changed(self, supported):
@@ -523,3 +529,23 @@
"""
self.proxy().SetPlayerMetadata(metadata)
return True
+
+ def register_callback_observer(self, name, observer):
+ """Adds an observer for all callbacks.
+
+ Args:
+ name: Name of the observer.
+ observer: Observer that implements all callback classes.
+ """
+ if isinstance(observer, BluetoothMediaCallbacks):
+ self.callbacks.add_observer(name, observer)
+
+ def unregister_callback_observer(self, name, observer):
+ """Removes an observer for all callbacks.
+
+ Args:
+ name: Name of the observer.
+ observer: Observer that implements all callback classes.
+ """
+ if isinstance(observer, BluetoothMediaCallbacks):
+ self.callbacks.remove_observer(name, observer)
diff --git a/floss/pandora/floss/sox_utils.py b/floss/pandora/floss/sox_utils.py
new file mode 100644
index 0000000..45d2b5c
--- /dev/null
+++ b/floss/pandora/floss/sox_utils.py
@@ -0,0 +1,89 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+
+SOX_PATH = 'sox'
+
+
+def _raw_format_args(channels, bits, rate):
+ """Gets raw format args used in sox.
+
+ Args:
+ channels: Number of channels.
+ bits: Bit length for a sample.
+ rate: Sampling rate.
+
+ Returns:
+ A list of args.
+ """
+ args = ['-t', 'raw', '-e', 'signed']
+ args += _format_args(channels, bits, rate)
+ return args
+
+
+def _format_args(channels, bits, rate):
+ """Gets format args used in sox.
+
+ Args:
+ channels: Number of channels.
+ bits: Bit length for a sample.
+ rate: Sampling rate.
+
+ Returns:
+ A list of args.
+ """
+ return ['-c', str(channels), '-b', str(bits), '-r', str(rate)]
+
+
+def generate_sine_tone_cmd(filename,
+ channels=2,
+ bits=16,
+ rate=48000,
+ duration=None,
+ frequencies=440,
+ gain=None,
+ vol=None,
+ raw=True):
+ """Gets a command to generate sine tones at specified frequencies.
+
+ Args:
+ filename: The name of the file to store the sine wave in.
+ channels: The number of channels.
+ bits: The number of bits of each sample.
+ rate: The sampling rate.
+ duration: The length of the generated sine tone (in seconds).
+ frequencies: The frequencies of the sine wave. Pass a number or a list to specify frequency for each channel.
+ gain: The gain (in db).
+ vol: A float for volume scale used in sox command.
+ E.g. 1.0 is the same. 0.5 to scale volume by
+ half. -1.0 to invert the data.
+ raw: True to use raw data format. False to use what filename specifies.
+ """
+ args = [SOX_PATH, '-n']
+ if raw:
+ args += _raw_format_args(channels, bits, rate)
+ else:
+ args += _format_args(channels, bits, rate)
+ args.append(filename)
+ args.append('synth')
+ if duration is not None:
+ args.append(str(duration))
+ if not isinstance(frequencies, list):
+ frequencies = [frequencies]
+ for freq in frequencies:
+ args += ['sine', str(freq)]
+ if gain is not None:
+ args += ['gain', str(gain)]
+ if vol is not None:
+ args += ['vol', str(vol)]
+ return args
diff --git a/floss/pandora/floss/utils.py b/floss/pandora/floss/utils.py
index be825ed..bcd5feb 100644
--- a/floss/pandora/floss/utils.py
+++ b/floss/pandora/floss/utils.py
@@ -17,7 +17,9 @@
import logging
import threading
import time
+import uuid
from typing import List, Optional
+import traceback
from floss.pandora.floss import floss_enums
from gi.repository import GLib
@@ -76,6 +78,64 @@
time.sleep(sleep_interval)
+def dbus_safe(default_return_value, return_error=False):
+ """Catches all DBus exceptions and return a default value instead.
+
+ Wrap a function with a try block that catches DBus exceptions and returns the error with the specified return
+ status. The exception is logged to aid in debugging.
+
+ If |return_error| is set, the call will return a tuple with (default_return_value, str(error)).
+
+ Args:
+ default_return_value: What value to return in case of errors.
+ return_error: Whether to return the error string as well.
+
+ Returns:
+ Either the return value from the method call if successful or the |default_return_value| or
+ a tuple(default_return_value, str(error)).
+ """
+
+ def decorator(wrapped_function):
+ """Calls a function and catch DBus errors.
+
+ Args:
+ wrapped_function: Function to call in dbus safe context.
+
+ Returns:
+ Function return value or default_return_value on failure.
+ """
+
+ @functools.wraps(wrapped_function)
+ def wrapper(*args, **kwargs):
+ """Passes args and kwargs to a dbus safe function.
+
+ Args:
+ args: Formal python arguments.
+ kwargs: Keyword python arguments.
+
+ Returns:
+ Function return value or default_return_value on failure.
+ """
+ logging.debug('%s()', wrapped_function.__name__)
+ try:
+ return wrapped_function(*args, **kwargs)
+ except GLib.Error as e:
+ logging.debug('Exception while performing operation %s: %s', wrapped_function.__name__, e)
+
+ if return_error:
+ return (default_return_value, str(e))
+ else:
+ return default_return_value
+ except Exception as e:
+ logging.debug('Exception in %s: %s', wrapped_function.__name__, e)
+ logging.debug(traceback.format_exc())
+ raise
+
+ return wrapper
+
+ return decorator
+
+
def generate_dbus_cb_objpath(name, hci=None):
"""Generates a DBus callbacks object path with a suffix that won't conflict.
@@ -389,6 +449,18 @@
return f'{uuid32}-0000-1000-8000-00805f9b34fb'
+def get_uuid_as_list(str_uuid):
+ """Converts string uuid to a list of bytes.
+
+ Args:
+ str_uuid: String UUID.
+
+ Returns:
+ UUID string as list of bytes.
+ """
+ return list(uuid.UUID(str_uuid).bytes)
+
+
def advertise_data_from(request_data: host_pb2.DataTypes):
"""Mapping DataTypes to a dict.
diff --git a/floss/pandora/server/a2dp.py b/floss/pandora/server/a2dp.py
new file mode 100644
index 0000000..8ba66dc
--- /dev/null
+++ b/floss/pandora/server/a2dp.py
@@ -0,0 +1,254 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+"""A2DP grpc interface."""
+
+import asyncio
+import json
+import logging
+from typing import AsyncGenerator
+from typing import AsyncIterator
+
+from floss.pandora.floss import audio_utils
+from floss.pandora.floss import cras_utils
+from floss.pandora.floss import media_client
+from floss.pandora.floss import utils
+from floss.pandora.server import bluetooth as bluetooth_module
+from google.protobuf import wrappers_pb2, empty_pb2
+import grpc
+from pandora import a2dp_grpc_aio
+from pandora import a2dp_pb2
+
+
+class A2DPService(a2dp_grpc_aio.A2DPServicer):
+ """Service to trigger Bluetooth A2DP procedures.
+
+ This class implements the Pandora bluetooth test interfaces,
+ where the meta class definition is automatically generated by the protobuf.
+ The interface definition can be found in:
+ https://cs.android.com/android/platform/superproject/+/main:external/pandora/bt-test-interfaces/pandora/a2dp.proto
+ """
+
+ def __init__(self, bluetooth: bluetooth_module.Bluetooth):
+ self.bluetooth = bluetooth
+ self._cras_test_client = cras_utils.CrasTestClient()
+ cras_utils.set_floss_enabled(True)
+
+ async def OpenSource(self, request: a2dp_pb2.OpenSourceRequest,
+ context: grpc.ServicerContext) -> a2dp_pb2.OpenSourceResponse:
+
+ class ConnectionObserver(media_client.BluetoothMediaCallbacks):
+ """Observer to observe the A2DP profile connection state."""
+
+ def __init__(self, task):
+ self.task = task
+
+ @utils.glib_callback()
+ def on_bluetooth_audio_device_added(self, remote_device):
+ if remote_device['address'] != self.task['address']:
+ return
+
+ future = self.task['open_source']
+ future.get_loop().call_soon_threadsafe(future.set_result, True)
+
+ connection = utils.connection_from(request.connection)
+ address = connection.address
+ connected_devices = self.bluetooth.get_connected_audio_devices()
+
+ if not self.bluetooth.is_connected(address) or address not in connected_devices:
+ try:
+ open_source = asyncio.get_running_loop().create_future()
+ observer = ConnectionObserver({'open_source': open_source, 'address': address})
+ name = utils.create_observer_name(observer)
+ self.bluetooth.media_client.register_callback_observer(name, observer)
+ self.bluetooth.connect_device(address)
+ success = await asyncio.wait_for(open_source, timeout=10)
+
+ if not success:
+ await context.abort(grpc.StatusCode.UNKNOWN, f'Failed to connect to the address {address}.')
+ except asyncio.TimeoutError as e:
+ logging.error(f'OpenSource: timeout for waiting A2DP connection. {e}')
+ finally:
+ self.bluetooth.media_client.unregister_callback_observer(name, observer)
+
+ cookie = utils.address_to(address)
+ return a2dp_pb2.OpenSourceResponse(source=a2dp_pb2.Source(cookie=cookie))
+
+ async def OpenSink(self, request: a2dp_pb2.OpenSinkRequest,
+ context: grpc.ServicerContext) -> a2dp_pb2.OpenSinkResponse:
+
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
+
+ async def WaitSource(self, request: a2dp_pb2.WaitSourceRequest,
+ context: grpc.ServicerContext) -> a2dp_pb2.WaitSourceResponse:
+
+ class ConnectionObserver(media_client.BluetoothMediaCallbacks):
+ """Observer to observe the A2DP profile connection state."""
+
+ def __init__(self, task):
+ self.task = task
+
+ @utils.glib_callback()
+ def on_bluetooth_audio_device_added(self, remote_device):
+ if remote_device['address'] != self.task['address']:
+ return
+
+ future = self.task['wait_source']
+ future.get_loop().call_soon_threadsafe(future.set_result, address)
+
+ connection = utils.connection_from(request.connection)
+ address = connection.address
+ if not address:
+ await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Request address field must be set.')
+
+ connected_devices = self.bluetooth.get_connected_audio_devices()
+ if not self.bluetooth.is_connected(address) or address not in connected_devices:
+ try:
+ wait_source = asyncio.get_running_loop().create_future()
+ observer = ConnectionObserver({'wait_source': wait_source, 'address': address})
+ name = utils.create_observer_name(observer)
+ self.bluetooth.media_client.register_callback_observer(name, observer)
+ await asyncio.wait_for(wait_source, timeout=10)
+ except asyncio.TimeoutError as e:
+ logging.error(f'WaitSource: timeout for waiting A2DP connection. {e}')
+ finally:
+ self.bluetooth.media_client.unregister_callback_observer(name, observer)
+
+ cookie = utils.address_to(address)
+ return a2dp_pb2.WaitSourceResponse(source=a2dp_pb2.Source(cookie=cookie))
+
+ async def WaitSink(self, request: a2dp_pb2.WaitSinkRequest,
+ context: grpc.ServicerContext) -> a2dp_pb2.WaitSinkResponse:
+
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
+
+ async def IsSuspended(self, request: a2dp_pb2.IsSuspendedRequest,
+ context: grpc.ServicerContext) -> wrappers_pb2.BoolValue:
+
+ address = utils.address_from(request.target.cookie)
+ connected_audio_devices = self.bluetooth.get_connected_audio_devices()
+ if address not in connected_audio_devices:
+ await context.abort(grpc.StatusCode.FAILED_PRECONDITION,
+ 'A2dp device is not connected, cannot get suspend state')
+
+ is_suspended = cras_utils.get_active_stream_count() == 0
+ return wrappers_pb2.BoolValue(value=is_suspended)
+
+ async def Start(self, request: a2dp_pb2.StartRequest, context: grpc.ServicerContext) -> a2dp_pb2.StartResponse:
+
+ target = request.WhichOneof('target')
+ address = utils.address_from(request.target.cookie)
+ connected_audio_devices = self.bluetooth.get_connected_audio_devices()
+ if address not in connected_audio_devices:
+ await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'A2dp device is not connected, cannot start')
+
+ audio_data = json.dumps(audio_utils.A2DP_TEST_DATA)
+ audio_data = json.loads(audio_data)
+ audio_utils.generate_playback_file(audio_data)
+
+ if not audio_utils.select_audio_output_node():
+ await context.abort(grpc.StatusCode.UNKNOWN, 'Failed to select audio output node')
+
+ if target == 'source':
+ self._cras_test_client.start_playing_subprocess(audio_data['file'],
+ channels=audio_data['channels'],
+ rate=audio_data['rate'],
+ duration=audio_data['duration'])
+ else:
+ await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f'Invalid target type: {target}.')
+
+ return a2dp_pb2.StartResponse(started=empty_pb2.Empty())
+
+ async def Suspend(self, request: a2dp_pb2.SuspendRequest,
+ context: grpc.ServicerContext) -> a2dp_pb2.SuspendResponse:
+
+ target = request.WhichOneof('target')
+ address = utils.address_from(request.target.cookie)
+ connected_audio_devices = self.bluetooth.get_connected_audio_devices()
+ if address not in connected_audio_devices:
+ await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'A2dp device is not connected, cannot suspend')
+
+ if cras_utils.get_active_stream_count() == 0:
+ await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'A2dp Device is already suspended, cannot suspend')
+
+ if target == 'source':
+ self._cras_test_client.stop_playing_subprocess()
+ else:
+ await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f'Invalid target type: {target}.')
+
+ return a2dp_pb2.SuspendResponse(suspended=empty_pb2.Empty())
+
+ async def Close(self, request: a2dp_pb2.CloseRequest, context: grpc.ServicerContext) -> a2dp_pb2.CloseResponse:
+
+ class ConnectionObserver(media_client.BluetoothMediaCallbacks):
+ """Observer to observe the A2DP profile connection state."""
+
+ def __init__(self, task):
+ self.task = task
+
+ @utils.glib_callback()
+ def on_bluetooth_audio_device_removed(self, address):
+ if address != self.task['address']:
+ return
+
+ future = self.task['close_stream']
+ future.get_loop().call_soon_threadsafe(future.set_result, address)
+
+ address = utils.address_from(request.target.cookie)
+ connected_audio_devices = self.bluetooth.get_connected_audio_devices()
+ if address not in connected_audio_devices:
+ await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'A2dp device is not connected, cannot close')
+
+ try:
+ close_stream = asyncio.get_running_loop().create_future()
+ observer = ConnectionObserver({'close_stream': close_stream, 'address': address})
+ name = utils.create_observer_name(observer)
+ self.bluetooth.media_client.register_callback_observer(name, observer)
+ self.bluetooth.disconnect_media(address)
+ await close_stream
+ finally:
+ self.bluetooth.media_client.unregister_callback_observer(name, observer)
+ return a2dp_pb2.CloseResponse()
+
+ async def GetAudioEncoding(self, request: a2dp_pb2.GetAudioEncodingRequest,
+ context: grpc.ServicerContext) -> a2dp_pb2.GetAudioEncodingResponse:
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
+
+ async def PlaybackAudio(self, request: AsyncIterator[a2dp_pb2.PlaybackAudioRequest],
+ context: grpc.ServicerContext) -> a2dp_pb2.PlaybackAudioResponse:
+
+ audio_signals = request
+ logging.info('PlaybackAudio: Wait for audio signal...')
+
+ audio_signal = await utils.anext(audio_signals)
+ audio_data = audio_signal.data
+
+ if cras_utils.get_active_stream_count() == 0:
+ await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'Audio track is not started')
+
+ audio_utils.generate_playback_file_from_binary_data(audio_data)
+ audio_file = audio_utils.A2DP_PLAYBACK_DATA['file']
+ self._cras_test_client.play(audio_file)
+ return a2dp_pb2.PlaybackAudioResponse()
+
+ async def CaptureAudio(self, request: a2dp_pb2.CaptureAudioRequest,
+ context: grpc.ServicerContext) -> AsyncGenerator[a2dp_pb2.CaptureAudioResponse, None]:
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
diff --git a/floss/pandora/server/bluetooth.py b/floss/pandora/server/bluetooth.py
index 25d44d4..fd2d3d3 100644
--- a/floss/pandora/server/bluetooth.py
+++ b/floss/pandora/server/bluetooth.py
@@ -19,13 +19,14 @@
from floss.pandora.floss import adapter_client
from floss.pandora.floss import advertising_client
+from floss.pandora.floss import floss_enums
+from floss.pandora.floss import gatt_client
+from floss.pandora.floss import gatt_server
from floss.pandora.floss import manager_client
+from floss.pandora.floss import media_client
from floss.pandora.floss import qa_client
from floss.pandora.floss import scanner_client
from floss.pandora.floss import socket_manager
-from floss.pandora.floss import gatt_client
-from floss.pandora.floss import gatt_server
-from floss.pandora.floss import floss_enums
from floss.pandora.floss import utils
from gi.repository import GLib
import pydbus
@@ -66,6 +67,7 @@
self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, self.DEFAULT_ADAPTER)
self.scanner_client = scanner_client.FlossScannerClient(self.bus, self.DEFAULT_ADAPTER)
self.qa_client = qa_client.FlossQAClient(self.bus, self.DEFAULT_ADAPTER)
+ self.media_client = media_client.FlossMediaClient(self.bus, self.DEFAULT_ADAPTER)
self.gatt_client = gatt_client.FlossGattClient(self.bus, self.DEFAULT_ADAPTER)
self.gatt_server = gatt_server.FlossGattServer(self.bus, self.DEFAULT_ADAPTER)
self.socket_manager = socket_manager.FlossSocketManagerClient(self.bus, self.DEFAULT_ADAPTER)
@@ -133,6 +135,9 @@
if not self.qa_client.register_qa_callback():
logging.error('qa_client: Failed to register callbacks')
return False
+ if not self.media_client.register_callback():
+ logging.error('media_client: Failed to register callbacks')
+ return False
if not self.gatt_client.register_client(self.FAKE_GATT_APP_ID, False):
logging.error('gatt_client: Failed to register callbacks')
return False
@@ -153,6 +158,7 @@
self.advertising_client.has_proxy(),
self.scanner_client.has_proxy(),
self.qa_client.has_proxy(),
+ self.media_client.has_proxy(),
self.gatt_client.has_proxy(),
self.gatt_server.has_proxy(),
self.socket_manager.has_proxy()
@@ -189,6 +195,7 @@
self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, default_adapter)
self.scanner_client = scanner_client.FlossScannerClient(self.bus, default_adapter)
self.qa_client = qa_client.FlossQAClient(self.bus, default_adapter)
+ self.media_client = media_client.FlossMediaClient(self.bus, default_adapter)
self.gatt_client = gatt_client.FlossGattClient(self.bus, default_adapter)
self.gatt_server = gatt_server.FlossGattServer(self.bus, default_adapter)
self.socket_manager = socket_manager.FlossSocketManagerClient(self.bus, default_adapter)
@@ -397,3 +404,9 @@
def close_socket(self, socket_id):
return self.socket_manager.close(socket_id)
+
+ def get_connected_audio_devices(self):
+ return self.media_client.devices
+
+ def disconnect_media(self, address):
+ return self.media_client.disconnect(address)
diff --git a/floss/pandora/server/gatt.py b/floss/pandora/server/gatt.py
index 559ef9a..9cdf78e 100644
--- a/floss/pandora/server/gatt.py
+++ b/floss/pandora/server/gatt.py
@@ -111,7 +111,7 @@
self.bluetooth.configure_mtu(address, request.mtu)
status = await configure_mtu
if status != floss_enums.GattStatus.SUCCESS:
- raise RuntimeError('Failed to configure MTU.')
+ await context.abort(grpc.StatusCode.INTERNAL, 'Failed to configure MTU.')
finally:
self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
return gatt_pb2.ExchangeMTUResponse()
@@ -255,7 +255,7 @@
services, status = await search_services
if status != floss_enums.GattStatus.SUCCESS:
- raise RuntimeError('Failed to find services.')
+ await context.abort(grpc.StatusCode.INTERNAL, 'Failed to find services.')
response = gatt_pb2.DiscoverServicesResponse()
for serv in services:
response.services.append(self.create_gatt_service(serv))
@@ -295,8 +295,7 @@
status = self.bluetooth.fetch_remote(address)
if not status:
- raise RuntimeError(f'Failed to fetch remote device {address} '
- f'uuids.')
+ await context.abort(grpc.StatusCode.INTERNAL, f'Failed to fetch remote device {address} uuids.')
await device_uuids_changed
uuids = self.bluetooth.get_remote_uuids(address)
response = gatt_pb2.DiscoverServicesSdpResponse()
@@ -335,7 +334,7 @@
self.bluetooth.refresh_device(address)
status = await refresh
if status != floss_enums.GattStatus.SUCCESS:
- raise RuntimeError('Failed to clear cache.')
+ await context.abort(grpc.StatusCode.INTERNAL, 'Failed to clear cache.')
finally:
self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
return gatt_pb2.ClearCacheResponse()
@@ -370,8 +369,6 @@
self.bluetooth.gatt_client.register_callback_observer(name, observer)
self.bluetooth.read_characteristic(address, request.handle, self.AUTHENTICATION_NONE)
value, status = await characteristic_from_handle
- if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Found no characteristic with supported handle.')
finally:
self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
@@ -407,8 +404,6 @@
def on_characteristic_read(self, addr, status, handle, value):
if addr != self.task['address']:
return
- if handle < self.task['start_handle'] or handle > self.task['end_handle']:
- return
if floss_enums.GattStatus(status) != floss_enums.GattStatus.SUCCESS:
logging.error('Failed to read characteristic from handle. Status: %s', status)
future = self.task['characteristic_from_uuid']
@@ -427,8 +422,7 @@
self.bluetooth.discover_services(address)
services, status = await search_services
if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Found no services.')
-
+ await context.abort(grpc.StatusCode.INTERNAL, 'Found no services.')
characteristic_from_uuid = asyncio.get_running_loop().create_future()
observer = ReadCharacteristicsFromUuidObserver({
'characteristic_from_uuid': characteristic_from_uuid,
@@ -449,27 +443,26 @@
characteristic['instance_id'],
self.AUTHENTICATION_NONE)
status, handle, value = await characteristic_from_uuid
- if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError(f'Found no characteristic from uuid={request.uuid}.')
characteristics.append((status, handle, value))
+ if not characteristics:
+ self.bluetooth.read_using_characteristic_uuid(address, request.uuid, request.start_handle,
+ request.end_handle, self.AUTHENTICATION_NONE)
+ status, handle, value = await characteristic_from_uuid
+ result = gatt_pb2.ReadCharacteristicsFromUuidResponse(characteristics_read=[
+ gatt_pb2.ReadCharacteristicResponse(value=gatt_pb2.AttValue(value=bytes(value), handle=handle),
+ status=status)
+ ])
+ else:
+ result = gatt_pb2.ReadCharacteristicsFromUuidResponse(characteristics_read=[
+ gatt_pb2.ReadCharacteristicResponse(
+ value=gatt_pb2.AttValue(value=bytes(value), handle=handle),
+ status=status,
+ ) for status, handle, value in characteristics
+ ])
finally:
for name, observer in observers:
self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
-
- if not characteristics:
- return gatt_pb2.ReadCharacteristicsFromUuidResponse(characteristics_read=[
- gatt_pb2.ReadCharacteristicResponse(
- value=gatt_pb2.AttValue(value=bytes(), handle=request.start_handle),
- status=gatt_pb2.UNKNOWN_ERROR,
- )
- ])
-
- return gatt_pb2.ReadCharacteristicsFromUuidResponse(characteristics_read=[
- gatt_pb2.ReadCharacteristicResponse(
- value=gatt_pb2.AttValue(value=bytes(value), handle=handle),
- status=status,
- ) for status, handle, value in characteristics
- ])
+ return result
async def ReadCharacteristicDescriptorFromHandle(
self, request: gatt_pb2.ReadCharacteristicDescriptorRequest,
@@ -502,8 +495,6 @@
self.bluetooth.gatt_client.register_callback_observer(name, observer)
self.bluetooth.read_descriptor(address, request.handle, self.AUTHENTICATION_NONE)
value, status = await descriptor
- if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Found no descriptor with supported handle.')
finally:
self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
@@ -530,7 +521,7 @@
def convert_req_to_dictionary(request):
service_dict = {
'service_type': self.SERVICE_TYPE_PRIMARY,
- 'uuid': list(UUID(request.uuid).bytes),
+ 'uuid': request.uuid,
'instance_id': self.DEFAULT_INSTANCE_ID,
'included_services': [],
'characteristics': [],
@@ -539,7 +530,7 @@
# Iterate through the characteristics in the request.
for char in request.characteristics:
char_dict = {
- 'uuid': list(UUID(char.uuid).bytes),
+ 'uuid': char.uuid,
'instance_id': self.DEFAULT_INSTANCE_ID,
'properties': char.properties,
'permissions': char.permissions,
@@ -551,7 +542,7 @@
# Iterate through the descriptors in the characteristic.
for desc in char.descriptors:
desc_dict = {
- 'uuid': list(UUID(desc.uuid).bytes),
+ 'uuid': desc.uuid,
'instance_id': self.DEFAULT_INSTANCE_ID,
'permissions': desc.permissions,
}
@@ -569,7 +560,7 @@
self.bluetooth.add_service(serv_dic)
status, service = await register_service
if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Failed to register service.')
+ await context.abort(grpc.StatusCode.INTERNAL, 'Failed to register service.')
finally:
self.bluetooth.gatt_server.unregister_callback_observer(name, observer)
@@ -634,7 +625,7 @@
self.bluetooth.read_descriptor(address, request.handle, self.AUTHENTICATION_NONE)
value, status = await descriptor_futures['read_descriptor']
if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Found no descriptor with supported handle.')
+ await context.abort(grpc.StatusCode.INTERNAL, 'Found no descriptor with supported handle.')
search_services = asyncio.get_running_loop().create_future()
observer = DiscoveryObserver({'search_services': search_services, 'address': address})
@@ -644,7 +635,7 @@
self.bluetooth.discover_services(address)
services, status = await search_services
if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Found no device services.')
+ await context.abort(grpc.StatusCode.INTERNAL, 'Found no device services.')
characteristic_handle = None
for serv in services:
@@ -665,8 +656,6 @@
self.bluetooth.write_descriptor(address, request.handle, self.AUTHENTICATION_NONE,
self.ENABLE_NOTIFICATION_VALUE)
status = await descriptor_futures['write_descriptor']
- if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Can not write descriptor.')
finally:
for name, observer in observers:
self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
@@ -722,7 +711,7 @@
self.bluetooth.read_descriptor(address, request.handle, self.AUTHENTICATION_NONE)
value, status = await read_descriptor
if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Found no descriptor with supported handle.')
+ await context.abort(grpc.StatusCode.INTERNAL, 'Found no descriptor with supported handle.')
search_services = asyncio.get_running_loop().create_future()
observer = DiscoveryObserver({'search_services': search_services, 'address': address})
@@ -732,7 +721,7 @@
self.bluetooth.discover_services(address)
services, status = await search_services
if status != floss_enums.GattStatus.SUCCESS:
- raise ValueError('Found no device services.')
+ await context.abort(grpc.StatusCode.INTERNAL, 'Found no device services.')
characteristic_handle = None
for serv in services:
diff --git a/floss/pandora/server/server.py b/floss/pandora/server/server.py
index e0c0445..3c58f30 100644
--- a/floss/pandora/server/server.py
+++ b/floss/pandora/server/server.py
@@ -16,6 +16,7 @@
import asyncio
import logging
+from floss.pandora.server import a2dp
from floss.pandora.server import bluetooth as bluetooth_module
from floss.pandora.server import gatt
from floss.pandora.server import hid
@@ -24,6 +25,7 @@
from floss.pandora.server import rfcomm
from floss.pandora.server import security
import grpc
+from pandora import a2dp_grpc_aio
from pandora import host_grpc_aio
from pandora import security_grpc_aio
from pandora_experimental import gatt_grpc_aio
@@ -41,7 +43,7 @@
while True:
bluetooth = bluetooth_module.Bluetooth()
bluetooth.reset()
- logging.info("bluetooth initialized")
+ logging.info('bluetooth initialized')
server = grpc.aio.server()
security_service = security.SecurityService(bluetooth)
@@ -65,10 +67,13 @@
rfcomm_service = rfcomm.RFCOMMService(bluetooth)
rfcomm_grpc_aio.add_RFCOMMServicer_to_server(rfcomm_service, server)
+ a2dp_service = a2dp.A2DPService(bluetooth)
+ a2dp_grpc_aio.add_A2DPServicer_to_server(a2dp_service, server)
+
server.add_insecure_port(f'[::]:{port}')
await server.start()
- logging.info("server started")
+ logging.info('server started')
await server.wait_for_termination()
bluetooth.cleanup()
diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java
index 9f0f623..e7a3a94 100644
--- a/framework/java/android/bluetooth/BluetoothPbapClient.java
+++ b/framework/java/android/bluetooth/BluetoothPbapClient.java
@@ -218,7 +218,7 @@
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return true;
+ return false;
}
/**
diff --git a/framework/tests/bumble/AndroidTest.xml b/framework/tests/bumble/AndroidTest.xml
index 5f56a28..089d771 100644
--- a/framework/tests/bumble/AndroidTest.xml
+++ b/framework/tests/bumble/AndroidTest.xml
@@ -32,10 +32,7 @@
<option name="throw-if-cmd-fail" value="true" />
<option name="run-command" value="setprop persist.bluetooth.btsnooplogmode full" />
<option name="run-command" value="device_config set_sync_disabled_for_tests persistent" />
- <option name="run-command"
- value="device_config put bluetooth INIT_logging_debug_enabled_for_all true" />
- <option name="run-command"
- value="device_config put bluetooth INIT_default_log_level_str LOG_VERBOSE" />
+ <option name="run-command" value="setprop log.tag.bluetooth VERBOSE" />
<option name="run-command" value="settings put global ble_scan_always_enabled 0" />
<option name="run-command" value="cmd bluetooth_manager disable" />
<option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_OFF" />
diff --git a/system/blueberry/tests/gd_sl4a/lib/gd_sl4a_base_test.py b/system/blueberry/tests/gd_sl4a/lib/gd_sl4a_base_test.py
index d673adc..a3aed2a 100644
--- a/system/blueberry/tests/gd_sl4a/lib/gd_sl4a_base_test.py
+++ b/system/blueberry/tests/gd_sl4a/lib/gd_sl4a_base_test.py
@@ -74,8 +74,8 @@
def setup_test(self):
self.cert.rootservice.StartStack(
- facade_rootservice.StartStackRequest(
- module_under_test=facade_rootservice.BluetoothModule.Value(self.cert_module),))
+ facade_rootservice.StartStackRequest(module_under_test=facade_rootservice.BluetoothModule.Value(
+ self.cert_module),))
self.cert.wait_channel_ready()
self.timer_list = []
@@ -87,7 +87,7 @@
self.dut.sl4a.bluetoothDisableBLE()
disable_bluetooth(self.dut.sl4a, self.dut.ed)
# Enable full verbose logging for Bluetooth
- self.dut.adb.shell("device_config put bluetooth INIT_logging_debug_enabled_for_all true")
+ self.dut.adb.shell("setprop log.tag.bluetooth VERBOSE")
# Then enable Bluetooth
enable_bluetooth(self.dut.sl4a, self.dut.ed)
self.dut.sl4a.bluetoothDisableBLE()
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py b/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
index 0184288..d2bd77f 100644
--- a/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
+++ b/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
@@ -78,14 +78,14 @@
self.dut.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
getprop_result = self.dut.adb.getprop("persist.bluetooth.btsnooplogmode")
if getprop_result is None or ("full" not in getprop_result.lower()):
- self.dut.log.warning("Failed to enable Bluetooth HCI Snoop Logging on DUT, mode is {}"
- .format(getprop_result))
+ self.dut.log.warning(
+ "Failed to enable Bluetooth HCI Snoop Logging on DUT, mode is {}".format(getprop_result))
self.cert.adb.root()
self.cert.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
getprop_result = self.cert.adb.getprop("persist.bluetooth.btsnooplogmode")
if getprop_result is None or ("full" not in getprop_result.lower()):
- self.cert.log.warning("Failed to enable Bluetooth HCI Snoop Logging on CERT, mode is {}"
- .format(getprop_result))
+ self.cert.log.warning(
+ "Failed to enable Bluetooth HCI Snoop Logging on CERT, mode is {}".format(getprop_result))
self.ble = BleLib(dut=self.dut)
@@ -103,7 +103,7 @@
device.sl4a.bluetoothDisableBLE()
disable_bluetooth(device.sl4a, device.ed)
# Enable full verbose logging for Bluetooth
- device.adb.shell("device_config put bluetooth INIT_logging_debug_enabled_for_all true")
+ device.adb.shell("setprop log.tag.bluetooth VERBOSE")
# Then enable Bluetooth
enable_bluetooth(device.sl4a, device.ed)
device.sl4a.bluetoothDisableBLE()
diff --git a/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py b/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py
index 8dc9e74..9828269 100644
--- a/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py
+++ b/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py
@@ -98,7 +98,7 @@
self.cert.sl4a.bluetoothDisableBLE()
disable_bluetooth(self.cert.sl4a, self.cert.ed)
self.cert.adb.shell("setprop bluetooth.core.gap.le.privacy.enabled false")
- self.cert.adb.shell("device_config put bluetooth INIT_logging_debug_enabled_for_all true")
+ self.cert.adb.shell("setprop log.tag.bluetooth VERBOSE")
enable_bluetooth(self.cert.sl4a, self.cert.ed)
self.cert.sl4a.bluetoothDisableBLE()
self._test_le_reconnect_after_irk_rotation(False)
diff --git a/system/bta/ag/bta_ag_at.cc b/system/bta/ag/bta_ag_at.cc
index df7acbd..6621e22 100644
--- a/system/bta/ag/bta_ag_at.cc
+++ b/system/bta/ag/bta_ag_at.cc
@@ -25,9 +25,11 @@
#include "bta/ag/bta_ag_at.h"
+#include <android_bluetooth_flags.h>
#include <bluetooth/log.h>
#include <cstdint>
+#include <cstdlib>
#include "bta/ag/bta_ag_int.h"
#include "bta/include/utl.h"
@@ -138,7 +140,31 @@
/* if it's a set integer check max, min range */
if (arg_type == BTA_AG_AT_SET &&
p_cb->p_at_tbl[idx].fmt == BTA_AG_AT_INT) {
- int_arg = utl_str2int(p_arg);
+#if TARGET_FLOSS
+ if (true)
+#else
+ if (IS_FLAG_ENABLED(bta_ag_cmd_brsf_allow_uint32))
+#endif
+ {
+ if (p_cb->p_at_tbl[idx].command_id == BTA_AG_LOCAL_EVT_BRSF) {
+ // Per HFP v1.9 BRSF could be 32-bit integer and we should ignore
+ // all reserved bits rather than responding ERROR.
+ long long int_arg_ll = std::atoll(p_arg);
+ if (int_arg_ll >= (1ll << 32) || int_arg_ll < 0) int_arg_ll = -1;
+
+ // Ignore reserved bits. 0xfff because there are 12 defined bits.
+ if (int_arg_ll > 0 && (int_arg_ll & (~0xfffll))) {
+ log::warn("BRSF: reserved bit is set: 0x{:x}", int_arg_ll);
+ int_arg_ll &= 0xfffll;
+ }
+
+ int_arg = static_cast<int16_t>(int_arg_ll);
+ } else {
+ int_arg = utl_str2int(p_arg);
+ }
+ } else {
+ int_arg = utl_str2int(p_arg);
+ }
if (int_arg < (int16_t)p_cb->p_at_tbl[idx].min ||
int_arg > (int16_t)p_cb->p_at_tbl[idx].max) {
/* arg out of range; error */
diff --git a/system/bta/ag/bta_ag_cmd.cc b/system/bta/ag/bta_ag_cmd.cc
index bf63c95..9b8cd47 100644
--- a/system/bta/ag/bta_ag_cmd.cc
+++ b/system/bta/ag/bta_ag_cmd.cc
@@ -63,17 +63,6 @@
#define COLON_IDX_4_VGSVGM 4
-/* Local events which will not trigger a higher layer callback */
-enum {
- BTA_AG_LOCAL_EVT_FIRST = 0x100,
- BTA_AG_LOCAL_EVT_CCWA,
- BTA_AG_LOCAL_EVT_CLIP,
- BTA_AG_LOCAL_EVT_CMER,
- BTA_AG_LOCAL_EVT_BRSF,
- BTA_AG_LOCAL_EVT_CMEE,
- BTA_AG_LOCAL_EVT_BCC,
-};
-
/* AT command interpreter table for HSP */
static const tBTA_AG_AT_CMD bta_ag_hsp_cmd[] = {
{"+CKPD", BTA_AG_AT_CKPD_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 200, 200},
diff --git a/system/bta/ag/bta_ag_int.h b/system/bta/ag/bta_ag_int.h
index 5429fce..5da5163 100644
--- a/system/bta/ag/bta_ag_int.h
+++ b/system/bta/ag/bta_ag_int.h
@@ -94,6 +94,17 @@
BTA_AG_MAX_EVT,
};
+/* Local events which will not trigger a higher layer callback */
+enum {
+ BTA_AG_LOCAL_EVT_FIRST = 0x100,
+ BTA_AG_LOCAL_EVT_CCWA,
+ BTA_AG_LOCAL_EVT_CLIP,
+ BTA_AG_LOCAL_EVT_CMER,
+ BTA_AG_LOCAL_EVT_BRSF,
+ BTA_AG_LOCAL_EVT_CMEE,
+ BTA_AG_LOCAL_EVT_BCC,
+};
+
/* Actions to perform after a SCO event */
enum {
BTA_AG_POST_SCO_NONE, /* no action */
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index 6fcee05..a06494d 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -697,7 +697,7 @@
}
void CleanUp() {
- LOG_DEBUG();
+ log::debug("");
BTA_GATTC_AppDeregister(gatt_if_);
for (auto& device : devices_) {
@@ -2047,7 +2047,7 @@
return;
}
- LOG_VERBOSE();
+ log::verbose("");
const std::list<gatt::Service>* all_services =
BTA_GATTC_GetServices(device->conn_id);
diff --git a/system/bta/include/bta_ras_api.h b/system/bta/include/bta_ras_api.h
index b8c54ea..fe62996 100644
--- a/system/bta/include/bta_ras_api.h
+++ b/system/bta/include/bta_ras_api.h
@@ -24,19 +24,12 @@
namespace bluetooth {
namespace ras {
-enum ProcedureDoneStatus : uint8_t {
- ALL_RESULTS_COMPLETE = 0x0,
- PARTIAL_RESULTS = 0x1,
- ABORTED = 0xf,
-};
-
class RasServer {
public:
virtual ~RasServer() = default;
virtual void Initialize() = 0;
virtual void PushProcedureData(RawAddress address, uint16_t procedure_count,
- ProcedureDoneStatus procedure_done_status,
- std::vector<uint8_t> data) = 0;
+ bool is_last, std::vector<uint8_t> data) = 0;
};
RasServer* GetRasServer();
diff --git a/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
index 4b8ad5a..935e2e3 100644
--- a/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
@@ -93,7 +93,7 @@
return false;
}
- LOG_INFO();
+ log::info("");
le_audio_source_hal_state = HAL_STOPPED;
return true;
}
@@ -104,7 +104,7 @@
return;
}
- LOG_INFO();
+ log::info("");
if (halSourceInterface_) {
halSourceInterface_->Cleanup();
@@ -219,7 +219,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSourceInterface_->StopSession();
le_audio_source_hal_state = HAL_STOPPED;
@@ -256,7 +256,7 @@
return;
}
- LOG_INFO();
+ log::info("");
if (IS_FLAG_ENABLED(leaudio_start_stream_race_fix)) {
halSourceInterface_->ConfirmStreamingRequestV2();
} else {
@@ -271,7 +271,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSourceInterface_->SuspendedForReconfiguration();
}
@@ -282,7 +282,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSourceInterface_->ReconfigurationComplete();
}
@@ -293,7 +293,7 @@
return;
}
- LOG_INFO();
+ log::info("");
if (IS_FLAG_ENABLED(leaudio_start_stream_race_fix)) {
halSourceInterface_->CancelStreamingRequestV2();
} else {
@@ -308,7 +308,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSourceInterface_->SetRemoteDelay(remote_delay_ms);
}
@@ -320,7 +320,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSourceInterface_->UpdateAudioConfigToHal(config);
}
} // namespace
@@ -334,7 +334,7 @@
return nullptr;
}
- LOG_INFO();
+ log::info("");
return std::move(impl);
}
diff --git a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
index d9398da..2842900 100644
--- a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
@@ -134,7 +134,7 @@
return false;
}
- LOG_INFO();
+ log::info("");
le_audio_sink_hal_state_ = HAL_STOPPED;
return this->InitAudioSinkThread();
}
@@ -145,7 +145,7 @@
return;
}
- LOG_INFO();
+ log::info("");
worker_thread_->ShutDown();
if (halSinkInterface_) {
@@ -365,7 +365,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSinkInterface_->StopSession();
le_audio_sink_hal_state_ = HAL_STOPPED;
@@ -392,7 +392,7 @@
return;
}
- LOG_INFO();
+ log::info("");
if (IS_FLAG_ENABLED(leaudio_start_stream_race_fix)) {
halSinkInterface_->ConfirmStreamingRequestV2();
} else {
@@ -418,7 +418,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSinkInterface_->SuspendedForReconfiguration();
}
@@ -429,7 +429,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSinkInterface_->ReconfigurationComplete();
}
@@ -440,7 +440,7 @@
return;
}
- LOG_INFO();
+ log::info("");
if (IS_FLAG_ENABLED(leaudio_start_stream_race_fix)) {
halSinkInterface_->CancelStreamingRequestV2();
} else {
@@ -455,7 +455,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSinkInterface_->SetRemoteDelay(remote_delay_ms);
}
@@ -467,7 +467,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSinkInterface_->UpdateAudioConfigToHal(config);
}
@@ -478,7 +478,7 @@
return;
}
- LOG_INFO();
+ log::info("");
halSinkInterface_->UpdateBroadcastAudioConfigToHal(config);
}
} // namespace
@@ -492,7 +492,7 @@
return nullptr;
}
- LOG_INFO();
+ log::info("");
return std::move(impl);
}
@@ -505,7 +505,7 @@
return nullptr;
}
- LOG_INFO();
+ log::info("");
return std::move(impl);
}
diff --git a/system/bta/le_audio/broadcaster/broadcaster.cc b/system/bta/le_audio/broadcaster/broadcaster.cc
index 593095f..60e105f 100644
--- a/system/bta/le_audio/broadcaster/broadcaster.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster.cc
@@ -98,7 +98,7 @@
current_phy_(PHY_LE_2M),
audio_data_path_state_(AudioDataPathState::INACTIVE),
le_audio_source_hal_client_(nullptr) {
- LOG_INFO();
+ log::info("");
/* Register State machine callbacks */
BroadcastStateMachine::Initialize(&state_machine_callbacks_,
@@ -430,8 +430,7 @@
/* Choose the dominating audio context when multiple contexts are mixed */
LeAudioContextType ChooseConfigurationContextType(
AudioContexts audio_contexts) {
- LOG_DEBUG("Got contexts=%s",
- bluetooth::common::ToString(audio_contexts).c_str());
+ log::debug("Got contexts={}", bluetooth::common::ToString(audio_contexts));
/* Prioritize the most common use cases. */
if (audio_contexts.any()) {
@@ -443,16 +442,15 @@
};
for (auto ct : context_priority_list) {
if (audio_contexts.test(ct)) {
- LOG_DEBUG("Selecting configuration context type: %s",
- ToString(ct).c_str());
+ log::debug("Selecting configuration context type: {}", ToString(ct));
return ct;
}
}
}
auto fallback_config = LeAudioContextType::MEDIA;
- LOG_DEBUG("Selecting configuration context type: %s",
- ToString(fallback_config).c_str());
+ log::debug("Selecting configuration context type: {}",
+ ToString(fallback_config));
return fallback_config;
}
@@ -595,7 +593,7 @@
if (public_features & bluetooth::le_audio::kLeAudioQualityHigh &&
config->GetSamplingFrequencyHzMax() < 48000) {
- LOG_WARN(
+ log::warn(
"Preferred quality isn't supported. Fallback to standard audio "
"quality");
public_features &= (0xFFFF & ~bluetooth::le_audio::kLeAudioQualityHigh);
@@ -1158,7 +1156,7 @@
if (!broadcast_config_.has_value() ||
(broadcast_config_->subgroups.size() == 0)) {
- LOG_ERROR("Codec was not configured properly");
+ log::error("Codec was not configured properly");
return;
}
@@ -1195,14 +1193,14 @@
}
virtual void OnAudioSuspend(void) override {
- LOG_INFO();
+ log::info("");
/* TODO: Should we suspend all broadcasts - remove BIGs? */
if (instance)
instance->audio_data_path_state_ = AudioDataPathState::SUSPENDED;
}
virtual void OnAudioResume(void) override {
- LOG_INFO();
+ log::info("");
if (!instance) return;
/* TODO: Should we resume all broadcasts - recreate BIGs? */
@@ -1268,7 +1266,7 @@
bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks,
base::Callback<bool()> audio_hal_verifier) {
std::scoped_lock<std::mutex> lock(instance_mutex);
- LOG_INFO();
+ log::info("");
if (instance) {
log::error("Already initialized");
return;
@@ -1299,13 +1297,13 @@
bool LeAudioBroadcaster::IsLeAudioBroadcasterRunning() { return instance; }
LeAudioBroadcaster* LeAudioBroadcaster::Get(void) {
- LOG_INFO();
+ log::info("");
CHECK(instance);
return instance;
}
void LeAudioBroadcaster::Stop(void) {
- LOG_INFO();
+ log::info("");
if (instance) {
instance->Stop();
@@ -1314,7 +1312,7 @@
void LeAudioBroadcaster::Cleanup(void) {
std::scoped_lock<std::mutex> lock(instance_mutex);
- LOG_INFO();
+ log::info("");
if (instance == nullptr) return;
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index ace5303..5d4c6b2 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -5409,7 +5409,8 @@
}
void HandlePendingDeviceDisconnection(LeAudioDeviceGroup* group) {
- LOG_DEBUG();
+ log::debug("");
+
auto leAudioDevice = group->GetFirstDevice();
while (leAudioDevice) {
if (leAudioDevice->closing_stream_for_disconnection_) {
@@ -5817,8 +5818,7 @@
void ClientAudioInterfaceRelease() {
auto group = aseGroups_.FindById(active_group_id_);
if (!group) {
- LOG(ERROR) << __func__
- << ", Invalid group: " << static_cast<int>(active_group_id_);
+ log::error("Invalid group: {}", static_cast<int>(active_group_id_));
} else {
handleAsymmetricPhyForUnicast(group);
log::info("ClientAudioInterfaceRelease - cleanup");
@@ -5862,13 +5862,13 @@
}
if (group->dsa_.mode != DsaMode::ISO_SW) {
- LOG_WARN("ISO packets received over HCI in DSA mode: %d",
- group->dsa_.mode);
+ log::warn("ISO packets received over HCI in DSA mode: {}",
+ group->dsa_.mode);
return false;
}
if (iso_data_callback == nullptr) {
- LOG_WARN("Dsa data consumer not registered");
+ log::warn("Dsa data consumer not registered");
return false;
}
@@ -6289,7 +6289,7 @@
return false;
}
- LOG_INFO("ISO data consumer changed");
+ log::info("ISO data consumer changed");
iso_data_callback = callback;
return true;
}
diff --git a/system/bta/le_audio/client_parser.cc b/system/bta/le_audio/client_parser.cc
index 857f760..ea1ac99 100644
--- a/system/bta/le_audio/client_parser.cc
+++ b/system/bta/le_audio/client_parser.cc
@@ -320,7 +320,7 @@
}
return cur_len + conf.codec_config.size();
}
- LOG_ERROR("Error parsing codec configuration LTV data.");
+ log::error("Error parsing codec configuration LTV data.");
}
conf_ents_str << "\t"
diff --git a/system/bta/le_audio/codec_manager.cc b/system/bta/le_audio/codec_manager.cc
index 76353e7..145ea9f 100644
--- a/system/bta/le_audio/codec_manager.cc
+++ b/system/bta/le_audio/codec_manager.cc
@@ -206,7 +206,8 @@
const AudioSetConfigurations* GetSupportedCodecConfigurations(
types::LeAudioContextType ctx_type) {
if (GetCodecLocation() == le_audio::types::CodecLocation::ADSP) {
- LOG_VERBOSE("Get offload config for the context type: %d", (int)ctx_type);
+ log::verbose("Get offload config for the context type: {}",
+ (int)ctx_type);
// TODO: Need to have a mechanism to switch to software session if offload
// doesn't support.
@@ -215,7 +216,7 @@
: nullptr;
}
- LOG_VERBOSE("Get software config for the context type: %d", (int)ctx_type);
+ log::verbose("Get software config for the context type: {}", (int)ctx_type);
return AudioSetConfigurationProvider::Get()->GetConfigurations(ctx_type);
}
@@ -342,7 +343,7 @@
}
if (broadcast_target_config == -1) {
- LOG_ERROR(
+ log::error(
"There is no valid broadcast offload config with preferred_quality");
return nullptr;
}
@@ -388,8 +389,8 @@
auto offload_config = GetBroadcastOffloadConfig(BIG_audio_quality);
if (offload_config == nullptr) {
- LOG_ERROR("No Offload configuration supported for quality index: %d.",
- BIG_audio_quality);
+ log::error("No Offload configuration supported for quality index: {}.",
+ BIG_audio_quality);
return nullptr;
}
@@ -438,7 +439,7 @@
}
if (subgroup_quality.size() > 1) {
- LOG_ERROR("More than one subgroup is not supported!");
+ log::error("More than one subgroup is not supported!");
}
return std::make_unique<broadcaster::BroadcastConfiguration>(
@@ -465,7 +466,7 @@
if (broadcast_target_config == -1 ||
broadcast_target_config >= (int)supported_broadcast_config.size()) {
- LOG_ERROR("There is no valid broadcast offload config");
+ log::error("There is no valid broadcast offload config");
return;
}
@@ -685,8 +686,8 @@
// Check for number of ASEs mismatch
if (adsp_set_ase_confs.size() != software_set_ase_confs.size()) {
- LOG_ERROR(
- "%s: ADSP config size mismatches the software: %zu != %zu",
+ log::error(
+ "{}: ADSP config size mismatches the software: {} != {}",
direction == types::kLeAudioDirectionSink ? "Sink" : "Source",
adsp_set_ase_confs.size(), software_set_ase_confs.size());
continue;
diff --git a/system/bta/le_audio/device_groups.cc b/system/bta/le_audio/device_groups.cc
index 2dd37b1..64f29e4 100644
--- a/system/bta/le_audio/device_groups.cc
+++ b/system/bta/le_audio/device_groups.cc
@@ -486,7 +486,7 @@
uint8_t LeAudioDeviceGroup::GetPacking(void) const {
if (!stream_conf.conf) {
- LOG_ERROR("No stream configuration has been set.");
+ log::error("No stream configuration has been set.");
return bluetooth::hci::kIsoCigPackingSequential;
}
return stream_conf.conf->packing;
@@ -943,19 +943,21 @@
/* Choose the group configuration strategy based on PAC records */
strategy_ = GetGroupSinkStrategyFromPacs(expected_group_size);
- LOG_INFO("Group strategy set to: %s", [](types::LeAudioConfigurationStrategy
- strategy) {
- switch (strategy) {
- case types::LeAudioConfigurationStrategy::MONO_ONE_CIS_PER_DEVICE:
- return "MONO_ONE_CIS_PER_DEVICE";
- case types::LeAudioConfigurationStrategy::STEREO_TWO_CISES_PER_DEVICE:
- return "STEREO_TWO_CISES_PER_DEVICE";
- case types::LeAudioConfigurationStrategy::STEREO_ONE_CIS_PER_DEVICE:
- return "STEREO_ONE_CIS_PER_DEVICE";
- default:
- return "RFU";
- }
- }(*strategy_));
+ log::info(
+ "Group strategy set to: {}",
+ [](types::LeAudioConfigurationStrategy strategy) {
+ switch (strategy) {
+ case types::LeAudioConfigurationStrategy::MONO_ONE_CIS_PER_DEVICE:
+ return "MONO_ONE_CIS_PER_DEVICE";
+ case types::LeAudioConfigurationStrategy::
+ STEREO_TWO_CISES_PER_DEVICE:
+ return "STEREO_TWO_CISES_PER_DEVICE";
+ case types::LeAudioConfigurationStrategy::STEREO_ONE_CIS_PER_DEVICE:
+ return "STEREO_ONE_CIS_PER_DEVICE";
+ default:
+ return "RFU";
+ }
+ }(*strategy_));
}
return *strategy_;
}
@@ -1309,9 +1311,8 @@
*/
for (auto direction :
{types::kLeAudioDirectionSink, types::kLeAudioDirectionSource}) {
- LOG_DEBUG("Looking for configuration: %s - %s",
- audio_set_conf->name.c_str(),
- (direction == types::kLeAudioDirectionSink ? "Sink" : "Source"));
+ log::debug("Looking for configuration: {} - {}", audio_set_conf->name,
+ (direction == types::kLeAudioDirectionSink ? "Sink" : "Source"));
auto const& ase_confs = audio_set_conf->confs.get(direction);
ASSERT_LOG(
@@ -1324,11 +1325,11 @@
auto const ase_cnt = ase_confs.size();
if (ase_cnt == 0) {
- LOG_ERROR("ASE count is 0");
+ log::error("ASE count is 0");
continue;
}
if (device_cnt == 0) {
- LOG_ERROR("Device count is 0");
+ log::error("Device count is 0");
continue;
}
@@ -1356,7 +1357,7 @@
device != nullptr && required_device_cnt > 0;
device = GetNextDevice(device)) {
if (device->ases_.empty()) {
- LOG_ERROR("Device has no ASEs.");
+ log::error("Device has no ASEs.");
continue;
}
@@ -1366,12 +1367,12 @@
for (auto const& ent : ase_confs) {
if (!device->GetCodecConfigurationSupportedPac(direction, ent.codec)) {
- LOG_DEBUG("Insufficient PAC");
+ log::debug("Insufficient PAC");
continue;
}
if (!CheckIfStrategySupported(strategy, ent, direction, *device)) {
- LOG_DEBUG("Strategy not supported");
+ log::debug("Strategy not supported");
continue;
}
for (auto& ase : device->ases_) {
@@ -1385,8 +1386,8 @@
}
if (needed_ase_per_dev > 0) {
- LOG_DEBUG("Not enough ASEs on the device (needs %d more).",
- needed_ase_per_dev);
+ log::debug("Not enough ASEs on the device (needs {} more).",
+ needed_ase_per_dev);
return false;
}
@@ -1395,8 +1396,8 @@
if (required_device_cnt > 0) {
/* Don't left any active devices if requirements are not met */
- LOG_DEBUG(
- "Could not configure all the devices for direction: %s",
+ log::debug(
+ "Could not configure all the devices for direction: {}",
(direction == types::kLeAudioDirectionSink ? "Sink" : "Source"));
return false;
}
@@ -1463,11 +1464,11 @@
{types::kLeAudioDirectionSink, types::kLeAudioDirectionSource}) {
auto direction_str =
(direction == types::kLeAudioDirectionSink ? "Sink" : "Source");
- LOG_DEBUG("%s: Looking for requirements: %s", direction_str,
- audio_set_conf->name.c_str());
+ log::debug("{}: Looking for requirements: {}", direction_str,
+ audio_set_conf->name);
if (audio_set_conf->confs.get(direction).empty()) {
- LOG_WARN("No %s configuration available.", direction_str);
+ log::warn("No {} configuration available.", direction_str);
continue;
}
@@ -1870,20 +1871,20 @@
const auto& confs = audio_set_conf->confs.get(direction);
if (confs.size() == 0) continue;
- LOG_INFO("Looking for requirements: %s - %s", audio_set_conf->name.c_str(),
- (direction == 1 ? "snk" : "src"));
+ log::info("Looking for requirements: {} - {}", audio_set_conf->name,
+ (direction == 1 ? "snk" : "src"));
for (const auto& ent : confs) {
if (!leAudioDevice->GetCodecConfigurationSupportedPac(direction,
ent.codec)) {
- LOG_INFO("Configuration is NOT supported by device %s",
- ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
+ log::info("Configuration is NOT supported by device {}",
+ ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
return false;
}
}
}
- LOG_INFO("Configuration is supported by device %s",
- ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
+ log::info("Configuration is supported by device {}",
+ ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
return true;
}
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index c47e5f9..40b22b9 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -721,8 +721,8 @@
for (const auto pac : pac_recs) {
if (!utils::IsCodecUsingLtvFormat(pac.codec_id)) {
- LOG_WARN("Unknown codec PAC record for codec: %s",
- bluetooth::common::ToString(pac.codec_id).c_str());
+ log::warn("Unknown codec PAC record for codec: {}",
+ bluetooth::common::ToString(pac.codec_id));
continue;
}
ASSERT_LOG(!pac.codec_spec_caps.IsEmpty(),
@@ -833,10 +833,11 @@
// Take the preferences if possible
if (preferred_phy && (phy_bitmask & preferred_phy)) {
phy_bitmask &= preferred_phy;
- LOG_DEBUG("Using ASE preferred phy 0x%02x", static_cast<int>(phy_bitmask));
+ log::debug("Using ASE preferred phy 0x{:02x}",
+ static_cast<int>(phy_bitmask));
} else {
- LOG_WARN(
- "ASE preferred 0x%02x has nothing common with phy_bitfield 0x%02x ",
+ log::warn(
+ "ASE preferred 0x{:02x} has nothing common with phy_bitfield 0x{:02x}",
static_cast<int>(preferred_phy), static_cast<int>(phy_bitmask));
}
return phy_bitmask;
diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc
index 2930e48..74352ed 100644
--- a/system/bta/le_audio/devices_test.cc
+++ b/system/bta/le_audio/devices_test.cc
@@ -755,7 +755,7 @@
continue;
}
if (device_cnt == 0) {
- LOG_ERROR("Device count is 0");
+ log::error("Device count is 0");
continue;
}
@@ -764,18 +764,18 @@
* to active
*/
if (device_cnt != data_size) {
- LOG_DEBUG("Device count mismatch device!=data (%d!=%d)",
- static_cast<int>(device_cnt),
- static_cast<int>(data_size));
+ log::debug("Device count mismatch device!=data ({}!={})",
+ static_cast<int>(device_cnt),
+ static_cast<int>(data_size));
interesting_configuration = false;
}
/* Make sure the strategy is the expected one */
if (direction == kLeAudioDirectionSink &&
group_->GetGroupSinkStrategy() != strategy) {
- LOG_DEBUG("Sink strategy mismatch group!=cfg.entry (%d!=%d)",
- static_cast<int>(group_->GetGroupSinkStrategy()),
- static_cast<int>(strategy));
+ log::debug("Sink strategy mismatch group!=cfg.entry ({}!={})",
+ static_cast<int>(group_->GetGroupSinkStrategy()),
+ static_cast<int>(strategy));
interesting_configuration = false;
}
diff --git a/system/bta/le_audio/le_audio_health_status_test.cc b/system/bta/le_audio/le_audio_health_status_test.cc
index 19a7492..0a6ecc0 100644
--- a/system/bta/le_audio/le_audio_health_status_test.cc
+++ b/system/bta/le_audio/le_audio_health_status_test.cc
@@ -38,11 +38,6 @@
using bluetooth::le_audio::LeAudioHealthGroupStatType;
using bluetooth::le_audio::LeAudioHealthStatus;
-static const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
LeAudioHealthBasedAction recommendation_in_callback =
LeAudioHealthBasedAction::NONE;
RawAddress address_in_callback = RawAddress::kEmpty;
@@ -60,7 +55,6 @@
void SetUp() override {
reset_mock_function_count_map();
group_ = new LeAudioDeviceGroup(group_id_);
- bluetooth::common::InitFlags::Load(test_flags);
le_audio_health_status_instance_ = LeAudioHealthStatus::Get();
le_audio_health_status_instance_->RegisterCallback(
base::BindRepeating(healthCallback));
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 66fdf2a..ff2a7d4 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -240,8 +240,8 @@
codec_config_setting.params);
}
- LOG_ERROR("Codec %s, seems to be not supported here.",
- bluetooth::common::ToString(codec_id).c_str());
+ log::error("Codec {}, seems to be not supported here.",
+ bluetooth::common::ToString(codec_id));
return false;
}
@@ -535,7 +535,7 @@
LeAudioLtvMap ltv_map;
success = ltv_map.Parse(p_value, len);
if (!success) {
- LOG(ERROR) << __func__ << "Error parsing LTV map";
+ log::error("Error parsing LTV map");
}
return ltv_map;
}
diff --git a/system/bta/le_audio/le_audio_utils.cc b/system/bta/le_audio/le_audio_utils.cc
index 1e4ed1e..3b328bc 100644
--- a/system/bta/le_audio/le_audio_utils.cc
+++ b/system/bta/le_audio/le_audio_utils.cc
@@ -343,7 +343,7 @@
const struct types::acs_ac_record& record,
std::vector<bluetooth::le_audio::btle_audio_codec_config_t>& vec) {
if (!utils::IsCodecUsingLtvFormat(record.codec_id)) {
- LOG_WARN(
+ log::warn(
"Unknown codec capability format. Unable to report known codec "
"parameters.");
return;
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index f302170..6fa4136 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -1972,10 +1972,10 @@
out_cfg.max_transport_latency = rsp.max_transport_latency;
out_cfg.retrans_nb = rsp.preferred_retrans_nb;
out_cfg.phy = leAudioDevice->GetPreferredPhyBitmask(rsp.preferred_phy);
- LOG_INFO(
- "Using server preferred QoS settings. Max Transport Latency: %d, "
- "Retransmission Number: %d, Phy: %d",
- +out_cfg.max_transport_latency, out_cfg.retrans_nb, +out_cfg.phy);
+ log::info(
+ "Using server preferred QoS settings. Max Transport Latency: {}, "
+ "Retransmission Number: {}, Phy: {}",
+ out_cfg.max_transport_latency, out_cfg.retrans_nb, out_cfg.phy);
}
};
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index b39f1fd..3c73042 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -60,10 +60,6 @@
extern struct fake_osi_alarm_set_on_mloop fake_osi_alarm_set_on_mloop_;
void osi_property_set_bool(const char* key, bool value);
-static const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
constexpr uint8_t media_ccid = 0xC0;
constexpr auto media_context = LeAudioContextType::MEDIA;
@@ -266,7 +262,6 @@
bool stop_inject_configured_ase_after_first_ase_configured_;
virtual void SetUp() override {
- bluetooth::common::InitFlags::Load(test_flags);
reset_mock_function_count_map();
bluetooth::manager::SetMockBtmInterface(&btm_interface);
gatt::SetMockBtaGattInterface(&gatt_interface);
@@ -4404,7 +4399,7 @@
fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop"));
- LOG_INFO("OnStateTransitionTimeout");
+ log::info("OnStateTransitionTimeout");
/* Simulate On State timeout */
group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
@@ -4458,7 +4453,7 @@
fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop"));
- LOG_INFO("OnStateTransitionTimeout");
+ log::info("OnStateTransitionTimeout");
/* Simulate On State timeout */
group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
@@ -4512,7 +4507,7 @@
fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop"));
- LOG_INFO("OnStateTransitionTimeout");
+ log::info("OnStateTransitionTimeout");
/* Simulate On State timeout */
group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
diff --git a/system/bta/le_audio/storage_helper_test.cc b/system/bta/le_audio/storage_helper_test.cc
index b068a6a..b7084b2 100644
--- a/system/bta/le_audio/storage_helper_test.cc
+++ b/system/bta/le_audio/storage_helper_test.cc
@@ -22,11 +22,6 @@
using bluetooth::le_audio::LeAudioDevice;
-const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
namespace bluetooth::le_audio {
RawAddress GetTestAddress(uint8_t index) {
CHECK_LT(index, UINT8_MAX);
@@ -34,12 +29,7 @@
return result;
}
-class StorageHelperTest : public ::testing::Test {
- protected:
- void SetUp() override { bluetooth::common::InitFlags::Load(test_flags); }
-
- void TearDown() override {}
-};
+class StorageHelperTest : public ::testing::Test {};
TEST(StorageHelperTest, DeserializeSinkPacs) {
// clang-format off
diff --git a/system/bta/ras/ras_server.cc b/system/bta/ras/ras_server.cc
index a30e956..6d93b1a 100644
--- a/system/bta/ras/ras_server.cc
+++ b/system/bta/ras/ras_server.cc
@@ -30,8 +30,6 @@
using namespace ::ras;
using namespace ::ras::uuid;
-using bluetooth::ras::ProcedureDoneStatus;
-
namespace {
class RasServerImpl;
@@ -76,11 +74,10 @@
}
void PushProcedureData(RawAddress address, uint16_t procedure_counter,
- ProcedureDoneStatus procedure_done_status,
- std::vector<uint8_t> data) {
- log::info("{}, counter:{}, procedure_done_status:{}, with size {}",
- ADDRESS_TO_LOGGABLE_STR(address), procedure_counter,
- (uint16_t)procedure_done_status, data.size());
+ bool is_last, std::vector<uint8_t> data) {
+ log::info("{}, counter:{}, is_last:{}, with size {}",
+ ADDRESS_TO_LOGGABLE_STR(address), procedure_counter, is_last,
+ data.size());
tBLE_BD_ADDR ble_bd_addr;
ResolveAddress(ble_bd_addr, address);
@@ -96,8 +93,7 @@
data_buffer.segments_.push_back(data);
// Send data ready
- if (procedure_done_status == ProcedureDoneStatus::ALL_RESULTS_COMPLETE ||
- procedure_done_status == ProcedureDoneStatus::ABORTED) {
+ if (is_last) {
uint16_t ccc_value =
tracker.ccc_values_[kRasRangingDataReadyCharacteristic];
if (ccc_value == GATT_CLT_CONFIG_NONE) {
diff --git a/system/bta/test/bta_api_test.cc b/system/bta/test/bta_api_test.cc
index 37f2e88..65c91d1 100644
--- a/system/bta/test/bta_api_test.cc
+++ b/system/bta/test/bta_api_test.cc
@@ -29,21 +29,9 @@
using namespace std::chrono_literals;
-namespace {
-
-const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
-} // namespace
-
class BtaApiTest : public testing::Test {
protected:
- void SetUp() override {
- reset_mock_function_count_map();
- bluetooth::common::InitFlags::Load(test_flags);
- }
+ void SetUp() override { reset_mock_function_count_map(); }
void TearDown() override {}
};
diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc
index ddbaabc..40dc9a1 100644
--- a/system/bta/vc/vc.cc
+++ b/system/bta/vc/vc.cc
@@ -930,12 +930,12 @@
}
void Mute(std::variant<RawAddress, int> addr_or_group_id) override {
- LOG_DEBUG();
+ log::debug("");
MuteUnmute(addr_or_group_id, true /* mute */);
}
void UnMute(std::variant<RawAddress, int> addr_or_group_id) override {
- LOG_DEBUG();
+ log::debug("");
MuteUnmute(addr_or_group_id, false /* mute */);
}
diff --git a/system/btif/src/btif_hh.cc b/system/btif/src/btif_hh.cc
index 457d818..08d5c64 100644
--- a/system/btif/src/btif_hh.cc
+++ b/system/btif/src/btif_hh.cc
@@ -493,15 +493,26 @@
conn.link_spec.ToRedactedStringForLogging(), conn.status,
conn.handle);
- if (IS_FLAG_ENABLED(allow_switching_hid_and_hogp) &&
- conn.link_spec.transport != BT_TRANSPORT_AUTO) {
+ if (IS_FLAG_ENABLED(allow_switching_hid_and_hogp)) {
+ // Initialize with disconnected/accepting state based on reconnection policy
+ bthh_connection_state_t dev_status =
+ hh_get_state_on_disconnect(conn.link_spec);
+
+ // Use current state if the device instance already exists
btif_hh_device_t* p_dev = btif_hh_find_dev_by_link_spec(conn.link_spec);
- if ((p_dev != NULL) && (p_dev->dev_status != BTHH_CONN_STATE_ACCEPTING &&
- p_dev->dev_status != BTHH_CONN_STATE_CONNECTING)) {
+ if (p_dev != nullptr) {
+ dev_status = p_dev->dev_status;
+ }
+
+ if (dev_status != BTHH_CONN_STATE_ACCEPTING &&
+ dev_status != BTHH_CONN_STATE_CONNECTING) {
log::warn("Reject Incoming HID Connection, device: {}, state: {}",
- p_dev->link_spec.ToRedactedStringForLogging(),
- p_dev->dev_status);
- p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;
+ conn.link_spec.ToRedactedStringForLogging(), dev_status);
+
+ if (p_dev != nullptr) {
+ p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;
+ }
+
hh_connect_complete(conn.handle, conn.link_spec,
BTIF_HH_DEV_DISCONNECTED);
return;
@@ -618,6 +629,9 @@
}
if (hh_add_device(link_spec, attr_mask, reconnect_allowed)) {
+ if (IS_FLAG_ENABLED(allow_switching_hid_and_hogp) && reconnect_allowed) {
+ BTHH_STATE_UPDATE(link_spec, BTHH_CONN_STATE_ACCEPTING);
+ }
BTA_HhAddDev(link_spec, attr_mask, sub_class, app_id, dscp_info);
}
}
@@ -1619,7 +1633,8 @@
btif_hh_added_device_t* added_dev = btif_hh_find_added_dev(link_spec);
if (added_dev != nullptr) {
added_dev->reconnect_allowed = reconnect_allowed;
- btif_storage_set_hid_connection_policy(link_spec, reconnect_allowed);
+ btif_storage_set_hid_connection_policy(added_dev->link_spec,
+ reconnect_allowed);
}
}
diff --git a/system/btif/src/btif_rc.cc b/system/btif/src/btif_rc.cc
index 79d4a69..26b1d2e 100644
--- a/system/btif/src/btif_rc.cc
+++ b/system/btif/src/btif_rc.cc
@@ -860,10 +860,9 @@
// check if already some RC is connected
if (p_dev->rc_connected) {
- LOG_ERROR(
- "%s: Got RC OPEN in connected state, Connected RC: %d \
- and Current RC: %d",
- __func__, p_dev->rc_handle, p_rc_open->rc_handle);
+ log::error(
+ "Got RC OPEN in connected state, Connected RC: {} and Current RC: {}",
+ p_dev->rc_handle, p_rc_open->rc_handle);
if (p_dev->rc_handle != p_rc_open->rc_handle &&
p_dev->rc_addr != p_rc_open->peer_addr) {
log::verbose("Got RC connected for some other handle");
@@ -1536,10 +1535,10 @@
}
if (!bSent) {
- LOG_VERBOSE(
- "%s: Notification not sent, as there are no RC connections or the \
- CT has not subscribed for event_id: %s",
- __func__, dump_rc_notification_event_id(event_id));
+ log::verbose(
+ "Notification not sent, as there are no RC connections or the CT has "
+ "not subscribed for event_id: {}",
+ dump_rc_notification_event_id(event_id));
}
} else {
/* All other commands go here */
@@ -1727,10 +1726,10 @@
uint8_t num_attr;
num_attr = pavrc_cmd->get_items.attr_count;
- LOG_VERBOSE(
- "%s: AVRC_PDU_GET_FOLDER_ITEMS num_attr: %d, start_item [%d] \
- end_item [%d]",
- __func__, num_attr, pavrc_cmd->get_items.start_item,
+ log::verbose(
+ "AVRC_PDU_GET_FOLDER_ITEMS num_attr: {}, start_item [{}] end_item "
+ "[{}]",
+ num_attr, pavrc_cmd->get_items.start_item,
pavrc_cmd->get_items.end_item);
/* num_attr requested:
diff --git a/system/device/test/device_iot_config_test.cc b/system/device/test/device_iot_config_test.cc
index c440401..cbdfff1 100644
--- a/system/device/test/device_iot_config_test.cc
+++ b/system/device/test/device_iot_config_test.cc
@@ -36,13 +36,11 @@
using namespace testing;
const char* test_flags_feature_enabled[] = {
- "INIT_logging_debug_enabled_for_all=true",
"INIT_device_iot_config_logging=true",
nullptr,
};
const char* test_flags_feature_disabled[] = {
- "INIT_logging_debug_enabled_for_all=true",
"INIT_device_iot_config_logging=false",
nullptr,
};
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index dd4136e..695a3e5 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -552,10 +552,12 @@
cfi: true,
},
static_libs: [
+ "libbase",
"libbluetooth_log",
"libbluetooth_rust_interop",
"libbt_shim_bridge",
"libbt_shim_ffi",
+ "liblog",
],
target: {
android: {
diff --git a/system/gd/fuzz/helpers.h b/system/gd/fuzz/helpers.h
index c9cbd18..77e9a38 100644
--- a/system/gd/fuzz/helpers.h
+++ b/system/gd/fuzz/helpers.h
@@ -21,7 +21,7 @@
#include <cstdint>
#include <vector>
-#include "os/handler.h"
+#include "common/contextual_callback.h"
namespace bluetooth {
namespace fuzz {
@@ -43,7 +43,9 @@
if (!packet.IsValid()) {
return;
}
- callback.InvokeIfNotEmpty(packet);
+ if (!callback.IsEmpty()) {
+ callback.Invoke(packet);
+ }
}
template <typename TView>
@@ -52,7 +54,9 @@
if (!packet.IsValid()) {
return;
}
- callback.InvokeIfNotEmpty(packet);
+ if (!callback.IsEmpty()) {
+ callback.Invoke(packet);
+ }
}
} // namespace fuzz
diff --git a/system/gd/hal/snoop_logger_socket_test.cc b/system/gd/hal/snoop_logger_socket_test.cc
index da379ab..6659585 100644
--- a/system/gd/hal/snoop_logger_socket_test.cc
+++ b/system/gd/hal/snoop_logger_socket_test.cc
@@ -28,11 +28,6 @@
#include "hal/syscall_wrapper_impl.h"
#include "hal/syscall_wrapper_mock.h"
-static const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
namespace testing {
using bluetooth::hal::SnoopLoggerCommon;
@@ -44,9 +39,6 @@
class SnoopLoggerSocketModuleTest : public Test {
protected:
- void SetUp() override {
- bluetooth::common::InitFlags::Load(test_flags);
- }
SnoopLoggerSocketModuleTest() : sls(&mock) {}
void InitializeCommunicationsSuccess(SnoopLoggerSocket& sls, SyscallWrapperMock& mock) {
diff --git a/system/gd/hal/snoop_logger_socket_thread_test.cc b/system/gd/hal/snoop_logger_socket_thread_test.cc
index e8d1c68..37be6a8 100644
--- a/system/gd/hal/snoop_logger_socket_thread_test.cc
+++ b/system/gd/hal/snoop_logger_socket_thread_test.cc
@@ -28,11 +28,6 @@
#include "hal/syscall_wrapper_impl.h"
#include "os/utils.h"
-static const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
namespace testing {
using bluetooth::hal::SnoopLoggerCommon;
@@ -42,14 +37,7 @@
static constexpr int INVALID_FD = -1;
-class SnoopLoggerSocketThreadModuleTest : public Test {
- protected:
- void SetUp() override {
- bluetooth::common::InitFlags::Load(test_flags);
- }
-
- void TearDown() override {}
-};
+class SnoopLoggerSocketThreadModuleTest : public Test {};
TEST_F(SnoopLoggerSocketThreadModuleTest, socket_start_no_stop_test) {
{
diff --git a/system/gd/hal/snoop_logger_test.cc b/system/gd/hal/snoop_logger_test.cc
index ba8f538..d4ca3a2 100644
--- a/system/gd/hal/snoop_logger_test.cc
+++ b/system/gd/hal/snoop_logger_test.cc
@@ -115,11 +115,6 @@
using bluetooth::hal::SnoopLogger;
using namespace std::chrono_literals;
-const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
// Expose protected constructor for test
class TestSnoopLoggerModule : public SnoopLogger {
public:
@@ -195,8 +190,6 @@
ASSERT_FALSE(std::filesystem::exists(temp_snooz_log_last_));
test_registry = new TestModuleRegistry();
-
- bluetooth::common::InitFlags::Load(test_flags);
}
void TearDown() override {
diff --git a/system/gd/hci/acl_manager.cc b/system/gd/hci/acl_manager.cc
index 35e1232..853d4f7 100644
--- a/system/gd/hci/acl_manager.cc
+++ b/system/gd/hci/acl_manager.cc
@@ -414,6 +414,10 @@
return pimpl_->le_impl_->HACK_get_handle(address);
}
+Address AclManager::HACK_GetLeAddress(uint16_t connection_handle) {
+ return pimpl_->le_impl_->HACK_get_address(connection_handle);
+}
+
void AclManager::HACK_SetAclTxPriority(uint8_t handle, bool high_priority) {
CallOn(pimpl_->round_robin_scheduler_, &RoundRobinScheduler::SetLinkPriority, handle, high_priority);
}
diff --git a/system/gd/hci/acl_manager.h b/system/gd/hci/acl_manager.h
index 6814f85..db945a0 100644
--- a/system/gd/hci/acl_manager.h
+++ b/system/gd/hci/acl_manager.h
@@ -171,6 +171,7 @@
private:
virtual uint16_t HACK_GetHandle(const Address address);
virtual uint16_t HACK_GetLeHandle(const Address address);
+ virtual Address HACK_GetLeAddress(uint16_t connection_handle);
virtual void HACK_SetAclTxPriority(uint8_t handle, bool high_priority);
diff --git a/system/gd/hci/acl_manager/acl_scheduler.cc b/system/gd/hci/acl_manager/acl_scheduler.cc
index cbf45f7..e19c4fe 100644
--- a/system/gd/hci/acl_manager/acl_scheduler.cc
+++ b/system/gd/hci/acl_manager/acl_scheduler.cc
@@ -61,7 +61,7 @@
if (entry != nullptr && entry->address == address) {
// If so, clear the current entry and advance the queue
outgoing_entry_.reset();
- handle_outgoing_connection.InvokeIfNotEmpty();
+ handle_outgoing_connection.Invoke();
try_dequeue_next_operation();
return;
}
@@ -70,9 +70,9 @@
// Otherwise check if it's an incoming request and advance the queue if so
if (incoming_connecting_address_set_.find(address) != incoming_connecting_address_set_.end()) {
incoming_connecting_address_set_.erase(address);
- handle_incoming_connection.InvokeIfNotEmpty();
+ handle_incoming_connection.Invoke();
} else {
- handle_unknown_connection.InvokeIfNotEmpty(set_of_incoming_connecting_addresses());
+ handle_unknown_connection.Invoke(set_of_incoming_connecting_addresses());
}
try_dequeue_next_operation();
}
diff --git a/system/gd/hci/acl_manager/classic_impl_test.cc b/system/gd/hci/acl_manager/classic_impl_test.cc
index f92521b..7dcef4b 100644
--- a/system/gd/hci/acl_manager/classic_impl_test.cc
+++ b/system/gd/hci/acl_manager/classic_impl_test.cc
@@ -129,14 +129,14 @@
common::ContextualOnceCallback<void()> handle_incoming_connection,
common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) override {
if (handle_outgoing_connection_) {
- handle_outgoing_connection.InvokeIfNotEmpty();
+ handle_outgoing_connection.Invoke();
return;
}
if (handle_incoming_connection_) {
- handle_incoming_connection.InvokeIfNotEmpty();
+ handle_incoming_connection.Invoke();
} else {
- handle_unknown_connection.InvokeIfNotEmpty("set_of_incoming_connecting_addresses()");
+ handle_unknown_connection.Invoke("set_of_incoming_connecting_addresses()");
}
}
};
diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h
index f3db96d..cbed7b8 100644
--- a/system/gd/hci/acl_manager/le_impl.h
+++ b/system/gd/hci/acl_manager/le_impl.h
@@ -646,6 +646,10 @@
return connections.HACK_get_handle(address);
}
+ Address HACK_get_address(uint16_t connection_handle) {
+ return connections.getAddressWithType(connection_handle).GetAddress();
+ }
+
void OnAdvertisingSetTerminated(
uint16_t conn_handle,
uint8_t adv_set_id,
diff --git a/system/gd/hci/distance_measurement_manager.cc b/system/gd/hci/distance_measurement_manager.cc
index c7aea0e..86cab99 100644
--- a/system/gd/hci/distance_measurement_manager.cc
+++ b/system/gd/hci/distance_measurement_manager.cc
@@ -456,6 +456,7 @@
// Create a cs tracker with role reflector
// TODO: Check ROLE via CS config. (b/304295768)
cs_trackers_[connection_handle].role = CsRole::REFLECTOR;
+ cs_trackers_[connection_handle].address = acl_manager_->HACK_GetLeAddress(connection_handle);
} else {
send_le_cs_security_enable(connection_handle);
}
@@ -712,11 +713,11 @@
append_vector(procedure_data->ras_subevents, subevent_raw);
// erase buffer
procedure_data->ras_subevent_data_.clear();
- send_on_demand_data(procedure_data);
+ send_on_demand_data(cs_trackers_[connection_handle].address, procedure_data);
}
}
- void send_on_demand_data(CsProcedureData* procedure_data) {
+ void send_on_demand_data(Address address, CsProcedureData* procedure_data) {
if (procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS) {
procedure_data->segmentation_header_.last_segment_ = 1;
}
@@ -727,7 +728,11 @@
procedure_data->ras_subevents);
auto raw_data = builder_to_bytes(std::move(builder));
log::debug("counter: {}, size:{}", procedure_data->counter, (uint16_t)raw_data.size());
- // TODO, push data to RAS server
+ distance_measurement_callbacks_->OnRasFragmentReady(
+ address,
+ procedure_data->counter,
+ procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS,
+ raw_data);
procedure_data->ras_subevents.clear();
procedure_data->segmentation_header_.first_segment_ = 0;
diff --git a/system/gd/hci/distance_measurement_manager.h b/system/gd/hci/distance_measurement_manager.h
index 2c1d645..17e6cdb 100644
--- a/system/gd/hci/distance_measurement_manager.h
+++ b/system/gd/hci/distance_measurement_manager.h
@@ -64,6 +64,8 @@
int altitude_angle,
int error_altitude_angle,
DistanceMeasurementMethod method) = 0;
+ virtual void OnRasFragmentReady(
+ Address address, uint16_t procedure_counter, bool is_last, std::vector<uint8_t> raw_data) = 0;
};
class DistanceMeasurementManager : public bluetooth::Module {
diff --git a/system/gd/hci/fuzz/fuzz_hci_layer.cc b/system/gd/hci/fuzz/fuzz_hci_layer.cc
index 38fd22a..d2a3dda 100644
--- a/system/gd/hci/fuzz/fuzz_hci_layer.cc
+++ b/system/gd/hci/fuzz/fuzz_hci_layer.cc
@@ -181,8 +181,11 @@
}
void FuzzHciLayer::injectAclDisconnect(FuzzedDataProvider& fdp) {
- acl_on_disconnect_.InvokeIfNotEmpty(fdp.ConsumeIntegral<uint16_t>(),
- static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+ if (!acl_on_disconnect_.IsEmpty()) {
+ acl_on_disconnect_.Invoke(
+ fdp.ConsumeIntegral<uint16_t>(),
+ static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+ }
}
void FuzzHciLayer::injectLeAclEvent(std::vector<uint8_t> data) {
@@ -190,8 +193,11 @@
}
void FuzzHciLayer::injectLeAclDisconnect(FuzzedDataProvider& fdp) {
- le_acl_on_disconnect_.InvokeIfNotEmpty(fdp.ConsumeIntegral<uint16_t>(),
- static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+ if (!le_acl_on_disconnect_.IsEmpty()) {
+ le_acl_on_disconnect_.Invoke(
+ fdp.ConsumeIntegral<uint16_t>(),
+ static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+ }
}
void FuzzHciLayer::injectLeAdvertisingEvent(std::vector<uint8_t> data) {
diff --git a/system/gd/hci/hci_layer.cc b/system/gd/hci/hci_layer.cc
index 709a651..7ed84fd 100644
--- a/system/gd/hci/hci_layer.cc
+++ b/system/gd/hci/hci_layer.cc
@@ -65,7 +65,6 @@
}
static void abort_after_time_out(OpCode op_code) {
- bluetooth::os::LogMetricHciTimeoutEvent(static_cast<uint32_t>(op_code));
ASSERT_LOG(false, "Done waiting for debug information after HCI timeout (%s)", OpCodeText(op_code).c_str());
}
@@ -272,7 +271,8 @@
void on_hci_timeout(OpCode op_code) {
common::StopWatch::DumpStopWatchLog();
log::error("Timed out waiting for 0x{:02x} ({})", op_code, OpCodeText(op_code));
- // TODO: LogMetricHciTimeoutEvent(static_cast<uint32_t>(op_code));
+
+ bluetooth::os::LogMetricHciTimeoutEvent(static_cast<uint32_t>(op_code));
log::error("Flushing {} waiting commands", command_queue_.size());
// Clear any waiting commands (there is an abort coming anyway)
diff --git a/system/gd/hci/le_periodic_sync_manager.h b/system/gd/hci/le_periodic_sync_manager.h
index 8ee7bf6..ec97386 100644
--- a/system/gd/hci/le_periodic_sync_manager.h
+++ b/system/gd/hci/le_periodic_sync_manager.h
@@ -38,7 +38,7 @@
namespace bluetooth {
namespace hci {
-constexpr std::chrono::duration kPeriodicSyncTimeout = std::chrono::seconds(30);
+constexpr std::chrono::duration kPeriodicSyncTimeout = std::chrono::seconds(5);
constexpr int kMaxSyncTransactions = 16;
enum PeriodicSyncState : int {
diff --git a/system/gd/hci/le_periodic_sync_manager_test.cc b/system/gd/hci/le_periodic_sync_manager_test.cc
index 4910950..ed2c019 100644
--- a/system/gd/hci/le_periodic_sync_manager_test.cc
+++ b/system/gd/hci/le_periodic_sync_manager_test.cc
@@ -133,11 +133,6 @@
mutable std::mutex mutex_;
};
-const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
class PeriodicSyncManagerTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -146,7 +141,6 @@
test_le_scanning_interface_ = new TestLeScanningInterface();
periodic_sync_manager_ = new PeriodicSyncManager(&mock_callbacks_);
periodic_sync_manager_->Init(test_le_scanning_interface_, handler_);
- bluetooth::common::InitFlags::Load(test_flags);
}
void TearDown() override {
diff --git a/system/gd/os/linux_generic/queue.tpp b/system/gd/os/linux_generic/queue.tpp
deleted file mode 100644
index 24113b1..0000000
--- a/system/gd/os/linux_generic/queue.tpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-template <typename T>
-Queue<T>::Queue(size_t capacity) : enqueue_(capacity), dequeue_(0){};
-
-template <typename T>
-Queue<T>::~Queue() {
- ASSERT_LOG(enqueue_.handler_ == nullptr, "Enqueue is not unregistered");
- ASSERT_LOG(dequeue_.handler_ == nullptr, "Dequeue is not unregistered");
-};
-
-template <typename T>
-void Queue<T>::RegisterEnqueue(Handler* handler, EnqueueCallback callback) {
- std::lock_guard<std::mutex> lock(mutex_);
- ASSERT(enqueue_.handler_ == nullptr);
- ASSERT(enqueue_.reactable_ == nullptr);
- enqueue_.handler_ = handler;
- enqueue_.reactable_ = enqueue_.handler_->thread_->GetReactor()->Register(
- enqueue_.reactive_semaphore_.GetFd(),
- base::Bind(&Queue<T>::EnqueueCallbackInternal, base::Unretained(this), std::move(callback)),
- base::Closure());
-}
-
-template <typename T>
-void Queue<T>::UnregisterEnqueue() {
- Reactor* reactor = nullptr;
- Reactor::Reactable* to_unregister = nullptr;
- bool wait_for_unregister = false;
- {
- std::lock_guard<std::mutex> lock(mutex_);
- ASSERT(enqueue_.reactable_ != nullptr);
- reactor = enqueue_.handler_->thread_->GetReactor();
- wait_for_unregister = (!enqueue_.handler_->thread_->IsSameThread());
- to_unregister = enqueue_.reactable_;
- enqueue_.reactable_ = nullptr;
- enqueue_.handler_ = nullptr;
- }
- reactor->Unregister(to_unregister);
- if (wait_for_unregister) {
- reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
- }
-}
-
-template <typename T>
-void Queue<T>::RegisterDequeue(Handler* handler, DequeueCallback callback) {
- std::lock_guard<std::mutex> lock(mutex_);
- ASSERT(dequeue_.handler_ == nullptr);
- ASSERT(dequeue_.reactable_ == nullptr);
- dequeue_.handler_ = handler;
- dequeue_.reactable_ = dequeue_.handler_->thread_->GetReactor()->Register(
- dequeue_.reactive_semaphore_.GetFd(), callback, base::Closure());
-}
-
-template <typename T>
-void Queue<T>::UnregisterDequeue() {
- Reactor* reactor = nullptr;
- Reactor::Reactable* to_unregister = nullptr;
- bool wait_for_unregister = false;
- {
- std::lock_guard<std::mutex> lock(mutex_);
- ASSERT(dequeue_.reactable_ != nullptr);
- reactor = dequeue_.handler_->thread_->GetReactor();
- wait_for_unregister = (!dequeue_.handler_->thread_->IsSameThread());
- to_unregister = dequeue_.reactable_;
- dequeue_.reactable_ = nullptr;
- dequeue_.handler_ = nullptr;
- }
- reactor->Unregister(to_unregister);
- if (wait_for_unregister) {
- reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
- }
-}
-
-template <typename T>
-std::unique_ptr<T> Queue<T>::TryDequeue() {
- std::lock_guard<std::mutex> lock(mutex_);
-
- if (queue_.empty()) {
- return nullptr;
- }
-
- dequeue_.reactive_semaphore_.Decrease();
-
- std::unique_ptr<T> data = std::move(queue_.front());
- queue_.pop();
-
- enqueue_.reactive_semaphore_.Increase();
-
- return data;
-}
-
-template <typename T>
-void Queue<T>::EnqueueCallbackInternal(EnqueueCallback callback) {
- std::unique_ptr<T> data = callback.Run();
- ASSERT(data != nullptr);
- std::lock_guard<std::mutex> lock(mutex_);
- enqueue_.reactive_semaphore_.Decrease();
- queue_.push(std::move(data));
- dequeue_.reactive_semaphore_.Increase();
-}
diff --git a/system/gd/os/linux_generic/reactor_unittest.cc b/system/gd/os/linux_generic/reactor_unittest.cc
index 993dbfe..7893f55 100644
--- a/system/gd/os/linux_generic/reactor_unittest.cc
+++ b/system/gd/os/linux_generic/reactor_unittest.cc
@@ -93,7 +93,7 @@
}
void OnReadReady() {
- LOG_INFO();
+ log::info("");
uint64_t value = 0;
auto read_result = eventfd_read(fd_, &value);
log::info("value = {}", (int)value);
@@ -330,14 +330,14 @@
auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
EXPECT_EQ(write_result, 0);
EXPECT_EQ(future.get(), kReadReadyValue);
- LOG_INFO();
+ log::info("");
delete g_promise;
g_promise = new std::promise<int>;
future = g_promise->get_future();
write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kUnregisterSampleReactable);
EXPECT_EQ(write_result, 0);
EXPECT_EQ(future.get(), kReadReadyValue);
- LOG_INFO();
+ log::info("");
reactor_->Stop();
reactor_thread.join();
diff --git a/system/gd/os/log.h b/system/gd/os/log.h
index 437cba5..be42c68 100644
--- a/system/gd/os/log.h
+++ b/system/gd/os/log.h
@@ -33,15 +33,6 @@
#if defined(FUZZ_TARGET)
-#define LOG_VERBOSE_INT(...)
-#define LOG_DEBUG_INT(...)
-#define LOG_INFO_INT(...)
-#define LOG_WARN_INT(...)
-
-#define LOG_ERROR_INT(...) do { \
- fprintf(stderr, __VA_ARGS__); \
-} while (false)
-
// for fuzz targets, we just
// need to abort in this statement
// to catch the bug
@@ -57,25 +48,6 @@
#include <log/log.h>
#include <log/log_event_list.h>
-#define LOG_VERBOSE_INT(fmt, args...) \
- do { \
- if (!__android_log_is_loggable(ANDROID_LOG_VERBOSE, LOG_TAG, ANDROID_LOG_INFO) && \
- !__android_log_is_loggable(ANDROID_LOG_VERBOSE, "bluetooth", ANDROID_LOG_INFO)) { \
- ALOGV(fmt, ##args); \
- } \
- } while (false)
-
-#define LOG_DEBUG_INT(fmt, args...) \
- do { \
- if (!__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO) && \
- !__android_log_is_loggable(ANDROID_LOG_DEBUG, "bluetooth", ANDROID_LOG_INFO)) { \
- ALOGD(fmt, ##args); \
- } \
- } while (false)
-
-#define LOG_INFO_INT(fmt, args...) ALOGI(fmt, ##args)
-#define LOG_WARN_INT(fmt, args...) ALOGW(fmt, ##args)
-#define LOG_ERROR_INT(fmt, args...) ALOGE(fmt, ##args)
#define LOG_ALWAYS_FATAL_INT(fmt, args...) do { \
ALOGE(fmt, ##args); \
abort(); \
@@ -88,28 +60,19 @@
#define LOGWRAPPER(fmt, args...) VERBOSE_INFO(bluetooth, "bluetooth: " fmt, \
##args)
-#define LOG_VEBOSE_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_DEBUG_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_INFO_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_WARN_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_ERROR_INT(...) LOGWRAPPER(__VA_ARGS__)
#define LOG_ALWAYS_FATAL_INT(fmt, args...) \
do { \
fprintf(stderr, fmt "\n", ##args); \
abort(); \
} while (false)
+
#elif defined(TARGET_FLOSS) /* end of defined (ANDROID_EMULATOR) */
+
#include "os/syslog.h"
// Prefix the log with tag, file, line and function
#define LOGWRAPPER(tag, fmt, args...) write_syslog(tag, LOG_TAG, "%s: " fmt, LOG_TAG, ##args)
-#define LOG_VERBOSE_INT(...) LOGWRAPPER(LOG_TAG_VERBOSE, __VA_ARGS__)
-#define LOG_DEBUG_INT(...) LOGWRAPPER(LOG_TAG_DEBUG, __VA_ARGS__)
-#define LOG_INFO_INT(...) LOGWRAPPER(LOG_TAG_INFO, __VA_ARGS__)
-#define LOG_WARN_INT(...) LOGWRAPPER(LOG_TAG_WARN, __VA_ARGS__)
-#define LOG_ERROR_INT(...) LOGWRAPPER(LOG_TAG_ERROR, __VA_ARGS__)
-
#define LOG_ALWAYS_FATAL_INT(...) \
do { \
LOGWRAPPER(LOG_TAG_FATAL, __VA_ARGS__); \
@@ -148,12 +111,6 @@
##args); \
} while (false)
-#define LOG_VERBOSE_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_DEBUG_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_INFO_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_WARN_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_ERROR_INT(...) LOGWRAPPER(__VA_ARGS__)
-
#ifndef LOG_ALWAYS_FATAL
#define LOG_ALWAYS_FATAL_INT(...) \
do { \
@@ -169,6 +126,7 @@
#define _LOG_SRC_FMT_STR "%s:%d - %s: "
#define _PREPEND_SRC_LOC_IN_LOG(fmt, args...) \
_LOG_SRC_FMT_STR fmt, __FILE__, __LINE__, __func__, ##args
+
// ---------------------------------------------------------
// All MACROs defined above are internal and should *not* be
// used directly (use LOG_XXX defined below instead).
@@ -176,21 +134,6 @@
// location of the log emitting statement, so far they are only used by
// LogMsg, where the source locations is passed in.
-#define LOG_VERBOSE(fmt, args...) \
- LOG_VERBOSE_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
-#define LOG_DEBUG(fmt, args...) \
- LOG_DEBUG_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
-#define LOG_INFO(fmt, args...) \
- LOG_INFO_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
-#define LOG_WARN(fmt, args...) \
- LOG_WARN_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
-#define LOG_ERROR(fmt, args...) \
- LOG_ERROR_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
#ifndef LOG_ALWAYS_FATAL
#define LOG_ALWAYS_FATAL(fmt, args...) \
LOG_ALWAYS_FATAL_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
diff --git a/system/gd/os/queue.h b/system/gd/os/queue.h
index 57b04d5..820b35c 100644
--- a/system/gd/os/queue.h
+++ b/system/gd/os/queue.h
@@ -157,7 +157,104 @@
common::OnceClosure callback_on_empty_;
};
-#include "os/linux_generic/queue.tpp"
+template <typename T>
+Queue<T>::Queue(size_t capacity) : enqueue_(capacity), dequeue_(0){};
+
+template <typename T>
+Queue<T>::~Queue() {
+ ASSERT_LOG(enqueue_.handler_ == nullptr, "Enqueue is not unregistered");
+ ASSERT_LOG(dequeue_.handler_ == nullptr, "Dequeue is not unregistered");
+};
+
+template <typename T>
+void Queue<T>::RegisterEnqueue(Handler* handler, EnqueueCallback callback) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ ASSERT(enqueue_.handler_ == nullptr);
+ ASSERT(enqueue_.reactable_ == nullptr);
+ enqueue_.handler_ = handler;
+ enqueue_.reactable_ = enqueue_.handler_->thread_->GetReactor()->Register(
+ enqueue_.reactive_semaphore_.GetFd(),
+ base::Bind(&Queue<T>::EnqueueCallbackInternal, base::Unretained(this), std::move(callback)),
+ base::Closure());
+}
+
+template <typename T>
+void Queue<T>::UnregisterEnqueue() {
+ Reactor* reactor = nullptr;
+ Reactor::Reactable* to_unregister = nullptr;
+ bool wait_for_unregister = false;
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ ASSERT(enqueue_.reactable_ != nullptr);
+ reactor = enqueue_.handler_->thread_->GetReactor();
+ wait_for_unregister = (!enqueue_.handler_->thread_->IsSameThread());
+ to_unregister = enqueue_.reactable_;
+ enqueue_.reactable_ = nullptr;
+ enqueue_.handler_ = nullptr;
+ }
+ reactor->Unregister(to_unregister);
+ if (wait_for_unregister) {
+ reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
+ }
+}
+
+template <typename T>
+void Queue<T>::RegisterDequeue(Handler* handler, DequeueCallback callback) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ ASSERT(dequeue_.handler_ == nullptr);
+ ASSERT(dequeue_.reactable_ == nullptr);
+ dequeue_.handler_ = handler;
+ dequeue_.reactable_ = dequeue_.handler_->thread_->GetReactor()->Register(
+ dequeue_.reactive_semaphore_.GetFd(), callback, base::Closure());
+}
+
+template <typename T>
+void Queue<T>::UnregisterDequeue() {
+ Reactor* reactor = nullptr;
+ Reactor::Reactable* to_unregister = nullptr;
+ bool wait_for_unregister = false;
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ ASSERT(dequeue_.reactable_ != nullptr);
+ reactor = dequeue_.handler_->thread_->GetReactor();
+ wait_for_unregister = (!dequeue_.handler_->thread_->IsSameThread());
+ to_unregister = dequeue_.reactable_;
+ dequeue_.reactable_ = nullptr;
+ dequeue_.handler_ = nullptr;
+ }
+ reactor->Unregister(to_unregister);
+ if (wait_for_unregister) {
+ reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
+ }
+}
+
+template <typename T>
+std::unique_ptr<T> Queue<T>::TryDequeue() {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ if (queue_.empty()) {
+ return nullptr;
+ }
+
+ dequeue_.reactive_semaphore_.Decrease();
+
+ std::unique_ptr<T> data = std::move(queue_.front());
+ queue_.pop();
+
+ enqueue_.reactive_semaphore_.Increase();
+
+ return data;
+}
+
+template <typename T>
+void Queue<T>::EnqueueCallbackInternal(EnqueueCallback callback) {
+ std::unique_ptr<T> data = callback.Run();
+ ASSERT(data != nullptr);
+ std::lock_guard<std::mutex> lock(mutex_);
+ enqueue_.reactive_semaphore_.Decrease();
+ queue_.push(std::move(data));
+ dequeue_.reactive_semaphore_.Increase();
+}
} // namespace os
} // namespace bluetooth
diff --git a/system/gd/packet/parser/gen_cpp.cc b/system/gd/packet/parser/gen_cpp.cc
index 0d5014e..420a516 100644
--- a/system/gd/packet/parser/gen_cpp.cc
+++ b/system/gd/packet/parser/gen_cpp.cc
@@ -96,7 +96,6 @@
#include "packet/packet_view.h"
#include "packet/checksum_type_checker.h"
#include "packet/custom_type_checker.h"
-#include "os/log.h"
)";
if (generate_fuzzing || generate_tests) {
@@ -105,6 +104,7 @@
#if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
#include "packet/raw_builder.h"
+#include <bluetooth/log.h>
#endif
)";
}
diff --git a/system/gd/packet/parser/packet_def.cc b/system/gd/packet/parser/packet_def.cc
index eaf7c85..d91ab2d 100644
--- a/system/gd/packet/parser/packet_def.cc
+++ b/system/gd/packet/parser/packet_def.cc
@@ -484,8 +484,9 @@
s << "public: ";
s << "void CompareBytes(std::vector<uint8_t> captured_packet) {";
s << name_ << "View view = " << name_ << "View::FromBytes(captured_packet);";
- s << "if (!view.IsValid()) { LOG_INFO(\"Invalid Packet Bytes (size = %zu)\", view.size());";
- s << "for (size_t i = 0; i < view.size(); i++) { LOG_INFO(\"%5zd:%02X\", i, *(view.begin() + i)); }}";
+ s << "if (!view.IsValid()) { log::info(\"Invalid Packet Bytes (size = {})\", view.size());";
+ s << "for (size_t i = 0; i < view.size(); i++) { log::info(\"{:5}:{:02x}\", i, *(view.begin() + "
+ "i)); }}";
s << "ASSERT_TRUE(view.IsValid());";
s << "auto packet = " << name_ << "Builder::FromView(view);";
s << "std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();";
diff --git a/system/gd/rust/linux/stack/src/bluetooth_media.rs b/system/gd/rust/linux/stack/src/bluetooth_media.rs
index 112c267..631ede6 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_media.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_media.rs
@@ -1299,8 +1299,9 @@
// or pre-exists, and that an app which disconnects from WebHID may not have trigger
// the UHID_OUTPUT_NONE, we need to remove all pending HID calls on telephony use
// release to keep lower HF layer in sync and not prevent A2DP streaming
- self.hangup_call_impl();
- self.phone_state_change("".into());
+ if self.hangup_call_impl() {
+ self.phone_state_change("".into());
+ }
}
self.telephony_callbacks.lock().unwrap().for_all_callbacks(|callback| {
callback.on_telephony_use(address.to_string(), state);
diff --git a/system/main/shim/acl.cc b/system/main/shim/acl.cc
index 1980b6c..838ce45 100644
--- a/system/main/shim/acl.cc
+++ b/system/main/shim/acl.cc
@@ -900,7 +900,7 @@
if (IsClassicAcl(handle)) {
handle_to_classic_connection_map_[handle]->Flush();
} else {
- LOG_ERROR("handle %d is not a classic connection", handle);
+ log::error("handle {} is not a classic connection", handle);
}
}
@@ -1109,7 +1109,7 @@
uint16_t max_ce_len) {
auto connection = handle_to_le_connection_map_.find(handle);
if (connection == handle_to_le_connection_map_.end()) {
- LOG_WARN("Unknown le connection handle:0x%04x", handle);
+ log::warn("Unknown le connection handle:0x{:04x}", handle);
return;
}
connection->second->UpdateConnectionParameters(conn_int_min, conn_int_max,
@@ -1798,36 +1798,6 @@
conn_timeout, min_ce_len, max_ce_len);
}
-bool shim::legacy::Acl::HoldMode(uint16_t hci_handle, uint16_t max_interval,
- uint16_t min_interval) {
- handler_->CallOn(pimpl_.get(), &Acl::impl::HoldMode, hci_handle, max_interval,
- min_interval);
- return false; // TODO void
-}
-
-bool shim::legacy::Acl::SniffMode(uint16_t hci_handle, uint16_t max_interval,
- uint16_t min_interval, uint16_t attempt,
- uint16_t timeout) {
- handler_->CallOn(pimpl_.get(), &Acl::impl::SniffMode, hci_handle,
- max_interval, min_interval, attempt, timeout);
- return false;
-}
-
-bool shim::legacy::Acl::ExitSniffMode(uint16_t hci_handle) {
- handler_->CallOn(pimpl_.get(), &Acl::impl::ExitSniffMode, hci_handle);
- return false;
-}
-
-bool shim::legacy::Acl::SniffSubrating(uint16_t hci_handle,
- uint16_t maximum_latency,
- uint16_t minimum_remote_timeout,
- uint16_t minimum_local_timeout) {
- handler_->CallOn(pimpl_.get(), &Acl::impl::SniffSubrating, hci_handle,
- maximum_latency, minimum_remote_timeout,
- minimum_local_timeout);
- return false;
-}
-
void shim::legacy::Acl::LeSetDefaultSubrate(uint16_t subrate_min,
uint16_t subrate_max,
uint16_t max_latency,
diff --git a/system/main/shim/acl.h b/system/main/shim/acl.h
index ec659bd..d00b34e 100644
--- a/system/main/shim/acl.h
+++ b/system/main/shim/acl.h
@@ -26,7 +26,6 @@
#include "hci/class_of_device.h"
#include "main/shim/acl_legacy_interface.h"
#include "main/shim/link_connection_interface.h"
-#include "main/shim/link_policy_interface.h"
#include "os/handler.h"
#include "packet/raw_builder.h"
#include "types/raw_address.h"
@@ -37,8 +36,7 @@
class Acl : public hci::acl_manager::ConnectionCallbacks,
public hci::acl_manager::LeConnectionCallbacks,
- public LinkConnectionInterface,
- public LinkPolicyInterface {
+ public LinkConnectionInterface {
public:
Acl(os::Handler* handler, const acl_interface_t& acl_interface,
uint8_t max_acceptlist_size, uint8_t max_address_resolution_size);
@@ -95,16 +93,6 @@
const hci::AddressWithType& address_with_type);
void ClearAddressResolution();
- // LinkPolicyInterface
- bool HoldMode(uint16_t hci_handle, uint16_t max_interval,
- uint16_t min_interval) override;
- bool SniffMode(uint16_t hci_handle, uint16_t max_interval,
- uint16_t min_interval, uint16_t attempt,
- uint16_t timeout) override;
- bool ExitSniffMode(uint16_t hci_handle) override;
- bool SniffSubrating(uint16_t hci_handle, uint16_t maximum_latency,
- uint16_t minimum_remote_timeout,
- uint16_t minimum_local_timeout) override;
void LeSetDefaultSubrate(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/distance_measurement_manager.cc b/system/main/shim/distance_measurement_manager.cc
index 31e835e..a5ad180 100644
--- a/system/main/shim/distance_measurement_manager.cc
+++ b/system/main/shim/distance_measurement_manager.cc
@@ -16,6 +16,7 @@
#include "distance_measurement_manager.h"
+#include "bta/include/bta_ras_api.h"
#include "btif/include/btif_common.h"
#include "hci/distance_measurement_manager.h"
#include "main/shim/entry.h"
@@ -104,6 +105,13 @@
error_altitude_angle, static_cast<uint8_t>(method)));
}
+ void OnRasFragmentReady(bluetooth::hci::Address address,
+ uint16_t procedure_counter, bool is_last,
+ std::vector<uint8_t> raw_data) {
+ bluetooth::ras::GetRasServer()->PushProcedureData(
+ bluetooth::ToRawAddress(address), procedure_counter, is_last, raw_data);
+ }
+
private:
::DistanceMeasurementCallbacks* distance_measurement_callbacks_;
};
diff --git a/system/main/shim/link_policy_interface.h b/system/main/shim/link_policy_interface.h
deleted file mode 100644
index 41423a1..0000000
--- a/system/main/shim/link_policy_interface.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#pragma once
-
-#include <cstdint>
-
-namespace bluetooth {
-namespace shim {
-
-class LinkPolicyInterface {
- public:
- virtual bool HoldMode(uint16_t handle, uint16_t max_interval,
- uint16_t min_interval) = 0;
- virtual bool SniffMode(uint16_t handle, uint16_t max_interval,
- uint16_t min_interval, uint16_t attempt,
- uint16_t timeout) = 0;
- virtual bool ExitSniffMode(uint16_t handle) = 0;
- virtual bool SniffSubrating(uint16_t handle, uint16_t maximum_latency,
- uint16_t minimum_remote_timeout,
- uint16_t minimum_local_timeout) = 0;
- virtual ~LinkPolicyInterface() = default;
-};
-
-} // namespace shim
-} // namespace bluetooth
diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc
index 608b823..7430ecf 100644
--- a/system/main/shim/stack.cc
+++ b/system/main/shim/stack.cc
@@ -41,6 +41,7 @@
#include "hci/vendor_specific_event_manager.h"
#include "main/shim/acl.h"
#include "main/shim/acl_legacy_interface.h"
+#include "main/shim/btm.h"
#include "main/shim/distance_measurement_manager.h"
#include "main/shim/entry.h"
#include "main/shim/hci_layer.h"
@@ -60,6 +61,7 @@
struct Stack::impl {
legacy::Acl* acl_ = nullptr;
+ Btm* btm_ = nullptr;
};
Stack::Stack() { pimpl_ = std::make_shared<Stack::impl>(); }
@@ -151,8 +153,8 @@
ASSERT_LOG(is_running_, "%s Gd stack not running", __func__);
is_running_ = false;
- delete btm_;
- btm_ = nullptr;
+ delete pimpl_->btm_;
+ pimpl_->btm_ = nullptr;
stack_handler_->Clear();
@@ -192,17 +194,10 @@
return pimpl_->acl_;
}
-LinkPolicyInterface* Stack::LinkPolicy() {
- std::lock_guard<std::recursive_mutex> lock(mutex_);
- ASSERT(is_running_);
- ASSERT_LOG(pimpl_->acl_ != nullptr, "Acl shim layer has not been created");
- return pimpl_->acl_;
-}
-
Btm* Stack::GetBtm() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
ASSERT(is_running_);
- return btm_;
+ return pimpl_->btm_;
}
os::Handler* Stack::GetHandler() {
diff --git a/system/main/shim/stack.h b/system/main/shim/stack.h
index 25cba54..66b7462 100644
--- a/system/main/shim/stack.h
+++ b/system/main/shim/stack.h
@@ -19,8 +19,6 @@
#include <functional>
#include <mutex>
-#include "main/shim/btm.h"
-#include "main/shim/link_policy_interface.h"
#include "module.h"
#include "os/handler.h"
#include "os/thread.h"
@@ -29,6 +27,9 @@
// The shim layer implementation on the Gd stack side.
namespace bluetooth {
namespace shim {
+
+class Btm;
+
namespace legacy {
class Acl;
}; // namespace legacy
@@ -55,7 +56,6 @@
const StackManager* GetStackManager() const;
legacy::Acl* GetAcl();
- LinkPolicyInterface* LinkPolicy();
Btm* GetBtm();
os::Handler* GetHandler();
@@ -87,7 +87,6 @@
bool is_running_ = false;
os::Thread* stack_thread_ = nullptr;
os::Handler* stack_handler_ = nullptr;
- Btm* btm_ = nullptr;
size_t num_modules_{0};
void Start(ModuleList* modules);
};
diff --git a/system/main/test/main_shim_test.cc b/system/main/test/main_shim_test.cc
index d79f59a..c1175b8 100644
--- a/system/main/test/main_shim_test.cc
+++ b/system/main/test/main_shim_test.cc
@@ -646,13 +646,7 @@
run_all_jni_thread_task();
}
-const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
TEST_F(MainShimTest, DISABLED_LeShimAclConnection_local_disconnect) {
- bluetooth::common::InitFlags::Load(test_flags);
auto acl = MakeAcl();
EXPECT_CALL(*test::mock_acl_manager_, CreateLeConnection(_, _)).Times(1);
diff --git a/system/stack/avrc/avrc_pars_tg.cc b/system/stack/avrc/avrc_pars_tg.cc
index 4a053c6..578fcc1 100644
--- a/system/stack/avrc/avrc_pars_tg.cc
+++ b/system/stack/avrc/avrc_pars_tg.cc
@@ -412,7 +412,7 @@
#define RETURN_STATUS_IF_FALSE(_status_, _b_, _msg_, ...) \
if (!(_b_)) { \
- LOG_VERBOSE(_msg_, ##__VA_ARGS__); \
+ log::verbose(_msg_, ##__VA_ARGS__); \
return _status_; \
}
diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc
index c48147c..98ea0b5 100644
--- a/system/stack/btm/btm_ble_gap.cc
+++ b/system/stack/btm/btm_ble_gap.cc
@@ -2406,13 +2406,13 @@
}
if (has_advertising_flags && (local_flag & BTM_BLE_BREDR_NOT_SPT) == 0) {
if (p_cur->ble_addr_type != BLE_ADDR_RANDOM) {
- LOG_VERBOSE("NOT_BR_EDR support bit not set, treat device as DUMO");
+ log::verbose("NOT_BR_EDR support bit not set, treat device as DUMO");
p_cur->device_type |= BT_DEVICE_TYPE_DUMO;
} else {
- LOG_VERBOSE("Random address, treat device as LE only");
+ log::verbose("Random address, treat device as LE only");
}
} else {
- LOG_VERBOSE("NOT_BR/EDR support bit set, treat device as LE only");
+ log::verbose("NOT_BR/EDR support bit set, treat device as LE only");
}
}
}
diff --git a/system/stack/test/gatt_connection_manager_test.cc b/system/stack/test/gatt_connection_manager_test.cc
index 039e829..afa178d 100644
--- a/system/stack/test/gatt_connection_manager_test.cc
+++ b/system/stack/test/gatt_connection_manager_test.cc
@@ -21,11 +21,6 @@
using connection_manager::tAPP_ID;
-const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
namespace {
// convenience mock, for verifying acceptlist operations on lower layer are
// actually scheduled
@@ -92,7 +87,6 @@
namespace connection_manager {
class BleConnectionManager : public testing::Test {
void SetUp() override {
- bluetooth::common::InitFlags::Load(test_flags);
localAcceptlistMock = std::make_unique<AcceptlistMock>();
}
diff --git a/system/stack/test/stack_acl_test.cc b/system/stack/test/stack_acl_test.cc
index 2d5d1be..59f8c8d 100644
--- a/system/stack/test/stack_acl_test.cc
+++ b/system/stack/test/stack_acl_test.cc
@@ -34,11 +34,6 @@
tBTM_CB btm_cb;
namespace {
-const char* test_flags[] = {
- "INIT_logging_debug_enabled_for_all=true",
- nullptr,
-};
-
const RawAddress kRawAddress = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
} // namespace
@@ -54,7 +49,6 @@
protected:
void SetUp() override {
reset_mock_function_count_map();
- bluetooth::common::InitFlags::Load(test_flags);
bluetooth::hci::testing::mock_controller_ = &controller_;
}
void TearDown() override {
diff --git a/system/test/mock/mock_bta_ras_server.cc b/system/test/mock/mock_bta_ras_server.cc
index 4d06c4f..ad6f7fb 100644
--- a/system/test/mock/mock_bta_ras_server.cc
+++ b/system/test/mock/mock_bta_ras_server.cc
@@ -16,13 +16,10 @@
#include "bta/include/bta_ras_api.h"
-using bluetooth::ras::ProcedureDoneStatus;
-
class MockRasServer : public bluetooth::ras::RasServer {
void Initialize() override {}
void PushProcedureData(RawAddress /* address */,
- uint16_t /* procedure_count */,
- ProcedureDoneStatus /* procedure_done_status */,
+ uint16_t /* procedure_count */, bool /* is_last */,
std::vector<uint8_t> /* data */) override{};
};
diff --git a/system/test/mock/mock_main_shim_acl.cc b/system/test/mock/mock_main_shim_acl.cc
index e8d5677..54817b1 100644
--- a/system/test/mock/mock_main_shim_acl.cc
+++ b/system/test/mock/mock_main_shim_acl.cc
@@ -163,35 +163,6 @@
inc_func_call_count(__func__);
}
-bool shim::legacy::Acl::HoldMode(uint16_t /* hci_handle */,
- uint16_t /* max_interval */,
- uint16_t /* min_interval */) {
- inc_func_call_count(__func__);
- return false;
-}
-
-bool shim::legacy::Acl::SniffMode(uint16_t /* hci_handle */,
- uint16_t /* max_interval */,
- uint16_t /* min_interval */,
- uint16_t /* attempt */,
- uint16_t /* timeout */) {
- inc_func_call_count(__func__);
- return false;
-}
-
-bool shim::legacy::Acl::ExitSniffMode(uint16_t /* hci_handle */) {
- inc_func_call_count(__func__);
- return false;
-}
-
-bool shim::legacy::Acl::SniffSubrating(uint16_t /* hci_handle */,
- uint16_t /* maximum_latency */,
- uint16_t /* minimum_remote_timeout */,
- uint16_t /* minimum_local_timeout */) {
- inc_func_call_count(__func__);
- return false;
-}
-
void shim::legacy::Acl::LeSetDefaultSubrate(uint16_t /* subrate_min */,
uint16_t /* subrate_max */,
uint16_t /* max_latency */,
diff --git a/tools/rootcanal/hal/bluetooth_hci.cc b/tools/rootcanal/hal/bluetooth_hci.cc
index 34cda65..7cea46b 100644
--- a/tools/rootcanal/hal/bluetooth_hci.cc
+++ b/tools/rootcanal/hal/bluetooth_hci.cc
@@ -19,6 +19,7 @@
#include "bluetooth_hci.h"
#include <cutils/properties.h>
+#include <log/log.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
@@ -59,7 +60,7 @@
void serviceDied(
uint64_t /* cookie */,
const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
- LOG_ERROR("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
+ ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
has_died_ = true;
mHci->close();
}
@@ -87,15 +88,15 @@
Return<void> BluetoothHci::initialize_impl(
const sp<V1_0::IBluetoothHciCallbacks>& cb,
const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) {
- LOG_INFO("%s", __func__);
+ ALOGI("%s", __func__);
if (cb == nullptr) {
- LOG_ERROR("cb == nullptr! -> Unable to call initializationComplete(ERR)");
+ ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
return Void();
}
death_recipient_->setHasDied(false);
auto link_ret = cb->linkToDeath(death_recipient_, 0);
- ASSERT_LOG(link_ret.isOk(), "Error calling linkToDeath.");
+ ALOG_ASSERT(link_ret.isOk(), "Error calling linkToDeath.");
test_channel_transport_.RegisterCommandHandler(
[this](const std::string& name, const std::vector<std::string>& args) {
@@ -121,9 +122,9 @@
hidl_vec<uint8_t> hci_event(packet->begin(), packet->end());
auto ret = cb->hciEventReceived(hci_event);
if (!ret.isOk()) {
- LOG_ERROR("Error sending event callback");
+ ALOGE("Error sending event callback");
if (!death_recipient_->getHasDied()) {
- LOG_ERROR("Closing");
+ ALOGE("Closing");
close();
}
}
@@ -134,9 +135,9 @@
hidl_vec<uint8_t> acl_packet(packet->begin(), packet->end());
auto ret = cb->aclDataReceived(acl_packet);
if (!ret.isOk()) {
- LOG_ERROR("Error sending acl callback");
+ ALOGE("Error sending acl callback");
if (!death_recipient_->getHasDied()) {
- LOG_ERROR("Closing");
+ ALOGE("Closing");
close();
}
}
@@ -147,9 +148,9 @@
hidl_vec<uint8_t> sco_packet(packet->begin(), packet->end());
auto ret = cb->scoDataReceived(sco_packet);
if (!ret.isOk()) {
- LOG_ERROR("Error sending sco callback");
+ ALOGE("Error sending sco callback");
if (!death_recipient_->getHasDied()) {
- LOG_ERROR("Closing");
+ ALOGE("Closing");
close();
}
}
@@ -161,9 +162,9 @@
hidl_vec<uint8_t> iso_packet(packet->begin(), packet->end());
auto ret = cb_1_1->isoDataReceived(iso_packet);
if (!ret.isOk()) {
- LOG_ERROR("Error sending iso callback");
+ ALOGE("Error sending iso callback");
if (!death_recipient_->getHasDied()) {
- LOG_ERROR("Closing");
+ ALOGE("Closing");
close();
}
}
@@ -182,7 +183,7 @@
// Send responses to logcat if the test channel is not configured.
test_channel_.RegisterSendResponse([](const std::string& response) {
- LOG_INFO("No test channel yet: %s", response.c_str());
+ ALOGI("No test channel yet: %s", response.c_str());
});
if (BtTestConsoleEnabled()) {
@@ -211,7 +212,7 @@
});
} else {
// This should be configurable in the future.
- LOG_INFO("Adding Beacons so the scan list is not empty.");
+ ALOGI("Adding Beacons so the scan list is not empty.");
test_channel_.AddDevice({"beacon", "be:ac:10:00:00:01", "1000"});
test_model_.AddDeviceToPhy(controller_index + 1, low_energy_phy_index);
test_channel_.AddDevice({"beacon", "be:ac:10:00:00:02", "1000"});
@@ -224,28 +225,28 @@
test_channel_.List({});
}
- unlink_cb_ = [this, cb](sp<BluetoothDeathRecipient>& death_recipient) {
+ unlink_cb_ = [cb](sp<BluetoothDeathRecipient>& death_recipient) {
if (death_recipient->getHasDied())
- LOG_INFO("Skipping unlink call, service died.");
+ ALOGI("Skipping unlink call, service died.");
else {
auto ret = cb->unlinkToDeath(death_recipient);
if (!ret.isOk()) {
- ASSERT_LOG(death_recipient_->getHasDied(),
- "Error calling unlink, but no death notification.");
+ ALOG_ASSERT(death_recipient_->getHasDied(),
+ "Error calling unlink, but no death notification.");
}
}
};
auto init_ret = cb->initializationComplete(V1_0::Status::SUCCESS);
if (!init_ret.isOk()) {
- ASSERT_LOG(death_recipient_->getHasDied(),
- "Error sending init callback, but no death notification.");
+ ALOG_ASSERT(death_recipient_->getHasDied(),
+ "Error sending init callback, but no death notification.");
}
return Void();
}
Return<void> BluetoothHci::close() {
- LOG_INFO("%s", __func__);
+ ALOGI("%s", __func__);
test_model_.Reset();
return Void();
}
@@ -296,11 +297,11 @@
void BluetoothHci::SetUpHciServer(ConnectCallback connection_callback) {
test_channel_.RegisterSendResponse([](const std::string& response) {
- LOG_INFO("No HCI Response channel: %s", response.c_str());
+ ALOGI("No HCI Response channel: %s", response.c_str());
});
if (!remote_hci_transport_.SetUp(hci_socket_server_, connection_callback)) {
- LOG_ERROR("Remote HCI channel SetUp failed.");
+ ALOGE("Remote HCI channel SetUp failed.");
return;
}
}
@@ -309,7 +310,7 @@
remote_link_layer_transport_.SetUp(link_socket_server_, connection_callback);
test_channel_.RegisterSendResponse([](const std::string& response) {
- LOG_INFO("No LinkLayer Response channel: %s", response.c_str());
+ ALOGI("No LinkLayer Response channel: %s", response.c_str());
});
}
@@ -324,7 +325,7 @@
bool transport_configured = test_channel_transport_.SetUp(
test_socket_server_, [this](std::shared_ptr<AsyncDataChannel> conn_fd,
AsyncDataChannelServer*) {
- LOG_INFO("Test channel connection accepted.");
+ ALOGI("Test channel connection accepted.");
test_channel_.RegisterSendResponse(
[this, conn_fd](const std::string& response) {
test_channel_transport_.SendResponse(conn_fd, response);
@@ -336,15 +337,15 @@
return false;
});
test_channel_.RegisterSendResponse([](const std::string& response) {
- LOG_INFO("No test channel: %s", response.c_str());
+ ALOGI("No test channel: %s", response.c_str());
});
if (!transport_configured) {
- LOG_ERROR("Test channel SetUp failed.");
+ ALOGE("Test channel SetUp failed.");
return;
}
- LOG_INFO("Test channel SetUp() successful");
+ ALOGI("Test channel SetUp() successful");
}
/* Fallback to shared library if there is no service. */
diff --git a/tools/rootcanal/hal/bluetooth_hci.h b/tools/rootcanal/hal/bluetooth_hci.h
index 1bceda8..3745421 100644
--- a/tools/rootcanal/hal/bluetooth_hci.h
+++ b/tools/rootcanal/hal/bluetooth_hci.h
@@ -27,7 +27,6 @@
#include "model/setup/test_model.h"
#include "net/posix/posix_async_socket_connector.h"
#include "net/posix/posix_async_socket_server.h"
-#include "os/log.h"
namespace android {
namespace hardware {
diff --git a/tools/rootcanal/hal/service.cc b/tools/rootcanal/hal/service.cc
index 7856dcf..cef8fe7 100644
--- a/tools/rootcanal/hal/service.cc
+++ b/tools/rootcanal/hal/service.cc
@@ -16,11 +16,10 @@
#define LOG_TAG "android.hardware.bluetooth@1.1-service.sim"
-#include "os/log.h"
-
#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
#include "bluetooth_hci.h"
@@ -37,5 +36,5 @@
if (status == android::OK)
joinRpcThreadpool();
else
- LOG_ERROR("Could not register as a service!");
+ ALOGE("Could not register as a service!");
}
diff --git a/tools/rootcanal/include/os/log.h b/tools/rootcanal/include/os/log.h
deleted file mode 100644
index a1e46dd..0000000
--- a/tools/rootcanal/include/os/log.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 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.
- */
-
-// This header is currently needed for hci_packets.h
-// FIXME: Change hci_packets.h to not depend on os/log.h
-// and remove this.
-#include "include/log.h"
-
-#define LOG_INFO(...) \
- rootcanal::log::Log(rootcanal::log::Verbosity::kInfo, __FILE__, __LINE__, \
- "{}", fmt::sprintf(__VA_ARGS__))
-#define LOG_WARN(...) \
- rootcanal::log::Log(rootcanal::log::Verbosity::kWarning, __FILE__, __LINE__, \
- "{}", fmt::sprintf(__VA_ARGS__))
-#define LOG_ERROR(...) \
- rootcanal::log::Log(rootcanal::log::Verbosity::kError, __FILE__, __LINE__, \
- "{}", fmt::sprintf(__VA_ARGS__))
-#define LOG_ALWAYS_FATAL(...) \
- rootcanal::log::Log(rootcanal::log::Verbosity::kFatal, __FILE__, __LINE__, \
- "{}", fmt::sprintf(__VA_ARGS__))