From 24d95c89b4fa53513debfcf223b4369294e507ea Mon Sep 17 00:00:00 2001 From: hieudz Date: Tue, 6 Sep 2022 07:37:46 +0000 Subject: [PATCH 001/667] Add BluetoothOppShareInfoTest Test: atest android.bluetooth.opp.BluetoothOppShareInfoTest Bug: 244534014 Change-Id: Iad804e7e083f42aa6ab116a0ee98795785351ae7 (cherry picked from commit e046109fef27df8171789cb1eef0eb8051b38f3c) Merged-In: Iad804e7e083f42aa6ab116a0ee98795785351ae7 --- .../opp/BluetoothOppShareInfoTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppShareInfoTest.java diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppShareInfoTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppShareInfoTest.java new file mode 100644 index 00000000000..ca549ecd8f4 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppShareInfoTest.java @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package com.android.bluetooth.opp; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BluetoothOppShareInfoTest { + private BluetoothOppShareInfo mBluetoothOppShareInfo; + + private Uri uri = Uri.parse("file://Idontknow//Justmadeitup"); + private String hintString = "this is a object that take 4 bytes"; + private String filename = "random.jpg"; + private String mimetype = "image/jpeg"; + private int direction = BluetoothShare.DIRECTION_INBOUND; + private String destination = "01:23:45:67:89:AB"; + private int visibility = BluetoothShare.VISIBILITY_VISIBLE; + private int confirm = BluetoothShare.USER_CONFIRMATION_CONFIRMED; + private int status = BluetoothShare.STATUS_PENDING; + private int totalBytes = 1023; + private int currentBytes = 42; + private int timestamp = 123456789; + private boolean mediaScanned = false; + + @Before + public void setUp() throws Exception { + mBluetoothOppShareInfo = new BluetoothOppShareInfo(0, uri, hintString, filename, + mimetype, direction, destination, visibility, confirm, status, totalBytes, + currentBytes, timestamp, mediaScanned); + } + + @Test + public void testConstructor() { + assertThat(mBluetoothOppShareInfo.mUri).isEqualTo(uri); + assertThat(mBluetoothOppShareInfo.mFilename).isEqualTo(filename); + assertThat(mBluetoothOppShareInfo.mMimetype).isEqualTo(mimetype); + assertThat(mBluetoothOppShareInfo.mDirection).isEqualTo(direction); + assertThat(mBluetoothOppShareInfo.mDestination).isEqualTo(destination); + assertThat(mBluetoothOppShareInfo.mVisibility).isEqualTo(visibility); + assertThat(mBluetoothOppShareInfo.mConfirm).isEqualTo(confirm); + assertThat(mBluetoothOppShareInfo.mStatus).isEqualTo(status); + assertThat(mBluetoothOppShareInfo.mTotalBytes).isEqualTo(totalBytes); + assertThat(mBluetoothOppShareInfo.mCurrentBytes).isEqualTo(currentBytes); + assertThat(mBluetoothOppShareInfo.mTimestamp).isEqualTo(timestamp); + assertThat(mBluetoothOppShareInfo.mMediaScanned).isEqualTo(mediaScanned); + } + + @Test + public void testReadyToStart() { + assertThat(mBluetoothOppShareInfo.isReadyToStart()).isTrue(); + + mBluetoothOppShareInfo.mDirection = BluetoothShare.DIRECTION_OUTBOUND; + assertThat(mBluetoothOppShareInfo.isReadyToStart()).isTrue(); + + mBluetoothOppShareInfo.mStatus = BluetoothShare.STATUS_RUNNING; + assertThat(mBluetoothOppShareInfo.isReadyToStart()).isFalse(); + } + + @Test + public void testHasCompletionNotification() { + assertThat(mBluetoothOppShareInfo.hasCompletionNotification()).isFalse(); + + mBluetoothOppShareInfo.mStatus = BluetoothShare.STATUS_CANCELED; + assertThat(mBluetoothOppShareInfo.hasCompletionNotification()).isTrue(); + + mBluetoothOppShareInfo.mVisibility = BluetoothShare.VISIBILITY_HIDDEN; + assertThat(mBluetoothOppShareInfo.hasCompletionNotification()).isFalse(); + } + + @Test + public void testIsObsolete() { + assertThat(mBluetoothOppShareInfo.isObsolete()).isFalse(); + mBluetoothOppShareInfo.mStatus = BluetoothShare.STATUS_RUNNING; + assertThat(mBluetoothOppShareInfo.isObsolete()).isTrue(); + } +} -- GitLab From fce48529873d0774bb1810c1ba0dd390c87c10c5 Mon Sep 17 00:00:00 2001 From: hieudz Date: Tue, 20 Sep 2022 01:15:58 +0000 Subject: [PATCH 002/667] Fix BluetoothOppBatchTest typo Bug: 244534014 Tag: #refactor Test: atest android.bluetooth.opp.BluetoothOppBatchTest Change-Id: I80b742bb221fbc0aced1064a8ad6c4b0b74664d4 (cherry picked from commit 1c19f807e8100d13a36f6b907faa0405b2c6fd21) Merged-In: I80b742bb221fbc0aced1064a8ad6c4b0b74664d4 --- .../app/src/com/android/bluetooth/opp/BluetoothOppBatch.java | 2 +- .../app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppBatch.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppBatch.java index ef2502e4b9f..45e79d40960 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppBatch.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppBatch.java @@ -180,7 +180,7 @@ public class BluetoothOppBatch { */ /** register a listener for the batch change */ - public void registerListern(BluetoothOppBatchListener listener) { + public void registerListener(BluetoothOppBatchListener listener) { mListener = listener; } diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java index 9d9cdbca8d7..ed02f9a8fc5 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java @@ -183,7 +183,7 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch mBatch = batch; mSession = session; - mBatch.registerListern(this); + mBatch.registerListener(this); mAdapter = BluetoothAdapter.getDefaultAdapter(); } -- GitLab From 789d4bc617da23dc86d288c53c80a242d3a6850f Mon Sep 17 00:00:00 2001 From: Brian Delwiche Date: Sat, 24 Sep 2022 00:49:46 +0000 Subject: [PATCH 003/667] Add bounds check in avdt_scb_act.cc Bug: 242535997 Test: BT unit tests, validated against researcher POC Tag: #security Ignore-AOSP-First: Security Change-Id: Id3b665da0214e3c93f277e31077214fcd1d936e7 --- system/stack/avdt/avdt_scb_act.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/stack/avdt/avdt_scb_act.cc b/system/stack/avdt/avdt_scb_act.cc index 74c7cf264ed..9a7c05d2a3e 100644 --- a/system/stack/avdt/avdt_scb_act.cc +++ b/system/stack/avdt/avdt_scb_act.cc @@ -1023,6 +1023,11 @@ void avdt_scb_hdl_write_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { /* Build a media packet, and add an RTP header if required. */ if (add_rtp_header) { + if (p_data->apiwrite.p_buf->offset < AVDT_MEDIA_HDR_SIZE) { + android_errorWriteWithInfoLog(0x534e4554, "242535997", -1, NULL, 0); + return; + } + ssrc = avdt_scb_gen_ssrc(p_scb); p_data->apiwrite.p_buf->len += AVDT_MEDIA_HDR_SIZE; -- GitLab From 9902b829b9e92a523c597066cae38adebaa058ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Y=C4=B1lmaz?= Date: Wed, 21 Sep 2022 04:55:32 +0000 Subject: [PATCH 004/667] Fix missing parameter assignment for CallbackInfo Bug: 237467631 Test: none Change-Id: I7beb8b48a3e0ac6ef6c348d4a2e016220b979d40 (cherry picked from commit f15549654698d1588ecd42f3d1fc06c862a98136) --- .../app/src/com/android/bluetooth/gatt/CallbackInfo.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/android/app/src/com/android/bluetooth/gatt/CallbackInfo.java b/android/app/src/com/android/bluetooth/gatt/CallbackInfo.java index f6035b392a5..ab3fe4ea4b8 100644 --- a/android/app/src/com/android/bluetooth/gatt/CallbackInfo.java +++ b/android/app/src/com/android/bluetooth/gatt/CallbackInfo.java @@ -20,9 +20,7 @@ package com.android.bluetooth.gatt; * These are held during congestion and reported when congestion clears. * @hide */ -/*package*/ - -class CallbackInfo { +/* package */ class CallbackInfo { public String address; public int status; public int handle; @@ -58,6 +56,6 @@ class CallbackInfo { this.address = address; this.status = status; this.handle = handle; + this.value = value; } } - -- GitLab From 29a297b4c31acac87fe854fa28a2054226fd9e8c Mon Sep 17 00:00:00 2001 From: Brian Delwiche Date: Wed, 28 Sep 2022 00:17:44 +0000 Subject: [PATCH 005/667] Fix URI check in BluetoothOppUtility.java Bug: 225880741 Test: BT unit tests, validated against researcher POC Tag: #security Ignore-AOSP-First: Security Change-Id: I1330080abfd638fb36aad9535b6f5f5872986a7f --- .../src/com/android/bluetooth/opp/BluetoothOppUtility.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java index 90f151413f1..f1f1faa2c3c 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java @@ -48,6 +48,7 @@ import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemProperties; +import android.util.EventLog; import android.util.Log; import com.android.bluetooth.R; @@ -78,7 +79,11 @@ public class BluetoothOppUtility { new ConcurrentHashMap(); public static boolean isBluetoothShareUri(Uri uri) { - return uri.toString().startsWith(BluetoothShare.CONTENT_URI.toString()); + if (uri.toString().startsWith(BluetoothShare.CONTENT_URI.toString()) + && !uri.getAuthority().equals(BluetoothShare.CONTENT_URI.getAuthority())) { + EventLog.writeEvent(0x534e4554, "225880741", -1, ""); + } + return uri.getAuthority().equals(BluetoothShare.CONTENT_URI.getAuthority()); } public static BluetoothOppTransferInfo queryRecord(Context context, Uri uri) { -- GitLab From c57bc7e72502bb9bb0b627175ef640617381e271 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Tue, 27 Sep 2022 10:20:28 +0000 Subject: [PATCH 006/667] LeAudioService: Fix failing unit test Now when the device is unbonded, we trigger the disconnection if device is not in a disconnected state. Tests need to catch this intent. Fixes the following: [31/39] com.android.bluetooth.le_audio.LeAudioServiceTest#testDeleteStateMachineUnbondEvents: FAILED (237ms) STACKTRACE: expected: 0 but was : 2 at com.android.bluetooth.le_audio.LeAudioServiceTest.verifyConnectionStateIntent(LeAudioServiceTest.java:289) at com.android.bluetooth.le_audio.LeAudioServiceTest.generateConnectionMessageFromNative(LeAudioServiceTest.java:846) at com.android.bluetooth.le_audio.LeAudioServiceTest.testDeleteStateMachineUnbondEvents(LeAudioServiceTest.java:713) Test: atest com.android.bluetooth.le_audio --no-bazel-mode Bug: 249250843 Tag: #feature Change-Id: I1f87ab52bb57af1fe1e55c9d7613b851c013c25c Merged-In: I1f87ab52bb57af1fe1e55c9d7613b851c013c25c (cherry picked from commit c6f6d5ee81ec45bf9c137e5f35b67e156d157b9a) --- .../le_audio/LeAudioServiceTest.java | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) 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 b37ae31cbce..ff996e56681 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 @@ -285,10 +285,9 @@ public class LeAudioServiceTest { assertThat(intent).isNotNull(); assertThat(intent.getAction()) .isEqualTo(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); - assertThat(device).isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); - assertThat(newState).isEqualTo(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); - assertThat(prevState).isEqualTo(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, - -1)); + assertThat((BluetoothDevice)intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).isEqualTo(device); + assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)).isEqualTo(newState); + assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)).isEqualTo(prevState); } /** @@ -701,31 +700,35 @@ public class LeAudioServiceTest { // LeAudio stack event: CONNECTION_STATE_CONNECTING - state machine should be created generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); - assertThat(BluetoothProfile.STATE_CONNECTING) - .isEqualTo(mService.getConnectionState(mLeftDevice)); + assertThat(mService.getConnectionState(mLeftDevice)) + .isEqualTo(BluetoothProfile.STATE_CONNECTING); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); // Device unbond - state machine is not removed mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); + verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, + BluetoothProfile.STATE_CONNECTING); // LeAudio stack event: CONNECTION_STATE_CONNECTED - state machine is not removed mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED); generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED, - BluetoothProfile.STATE_CONNECTING); - assertThat(BluetoothProfile.STATE_CONNECTED) - .isEqualTo(mService.getConnectionState(mLeftDevice)); + BluetoothProfile.STATE_DISCONNECTED); + assertThat(mService.getConnectionState(mLeftDevice)) + .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); // Device unbond - state machine is not removed mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); + verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING, + BluetoothProfile.STATE_CONNECTED); + assertThat(mService.getConnectionState(mLeftDevice)) + .isEqualTo(BluetoothProfile.STATE_DISCONNECTING); + assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); // LeAudio stack event: CONNECTION_STATE_DISCONNECTING - state machine is not removed mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED); - generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTING, - BluetoothProfile.STATE_CONNECTED); - assertThat(BluetoothProfile.STATE_DISCONNECTING) - .isEqualTo(mService.getConnectionState(mLeftDevice)); - assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); + assertThat(mService.getConnectionState(mLeftDevice)) + .isEqualTo(BluetoothProfile.STATE_DISCONNECTING); // Device unbond - state machine is not removed mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); @@ -734,8 +737,8 @@ public class LeAudioServiceTest { mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED); generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_DISCONNECTING); - assertThat(BluetoothProfile.STATE_DISCONNECTED) - .isEqualTo(mService.getConnectionState(mLeftDevice)); + assertThat(mService.getConnectionState(mLeftDevice)) + .isEqualTo(BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); // Device unbond - state machine is removed mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); -- GitLab From 0ad8e0ab7d38db10c21312709cce9cb30f223dc5 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Sun, 11 Sep 2022 20:40:40 +0000 Subject: [PATCH 007/667] LeAudio: Remove redundant context type This member variable was set by the state machine but read only in the unit tests which still work without it. Therefore it is redundant. Bug: 249737696 Tag: #feature Test: atest --host bluetooth_le_audio_test bluetooth_le_audio_client_test bluetooth_test_broadcaster --no-bazel-mode Change-Id: Ibed8531d2692bb5eb834177f8b9ceb83aabf0dfa Merged-In: Ibed8531d2692bb5eb834177f8b9ceb83aabf0dfa (cherry picked from commit 04857bffaab0018ad0407c8f7fd36fbc3a93bc31) --- system/bta/le_audio/devices.cc | 18 ------------------ system/bta/le_audio/devices.h | 18 +++++++++++------- system/bta/le_audio/le_audio_client_test.cc | 15 +++------------ system/bta/le_audio/state_machine.cc | 1 - 4 files changed, 14 insertions(+), 38 deletions(-) diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc index 54ff6ed5390..c89c505af33 100644 --- a/system/bta/le_audio/devices.cc +++ b/system/bta/le_audio/devices.cc @@ -410,17 +410,6 @@ LeAudioDevice* LeAudioDeviceGroup::GetNextActiveDeviceByDataPathState( return iter->lock().get(); } -bool LeAudioDeviceGroup::SetContextType(LeAudioContextType context_type) { - /* XXX: group context policy ? / may it disallow to change type ?) */ - context_type_ = context_type; - - return true; -} - -LeAudioContextType LeAudioDeviceGroup::GetContextType(void) { - return context_type_; -} - uint32_t LeAudioDeviceGroup::GetSduInterval(uint8_t direction) { for (LeAudioDevice* leAudioDevice = GetFirstActiveDevice(); leAudioDevice != nullptr; @@ -1579,9 +1568,6 @@ const set_configurations::AudioSetConfiguration* LeAudioDeviceGroup::GetActiveConfiguration(void) { return active_context_to_configuration_map[active_context_type_]; } -AudioContexts LeAudioDeviceGroup::GetActiveContexts(void) { - return active_contexts_mask_; -} std::optional LeAudioDeviceGroup::GetCodecConfigurationByDirection( @@ -1777,10 +1763,6 @@ void LeAudioDeviceGroup::CreateStreamVectorForOffloader(uint8_t direction) { } } -types::LeAudioContextType LeAudioDeviceGroup::GetCurrentContextType(void) { - return active_context_type_; -} - bool LeAudioDeviceGroup::IsPendingConfiguration(void) { return stream_conf.pending_configuration; } diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h index c7195397281..cd5a0fe8a3a 100644 --- a/system/bta/le_audio/devices.h +++ b/system/bta/le_audio/devices.h @@ -214,10 +214,10 @@ class LeAudioDeviceGroup { transport_latency_stom_us_(0), active_context_type_(types::LeAudioContextType::UNINITIALIZED), metadata_context_type_(0), + active_contexts_mask_(0), pending_update_available_contexts_(std::nullopt), target_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE), - current_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE), - context_type_(types::LeAudioContextType::UNINITIALIZED) {} + current_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {} ~LeAudioDeviceGroup(void); void AddNode(const std::shared_ptr& leAudioDevice); @@ -265,8 +265,6 @@ class LeAudioDeviceGroup { bool Configure(types::LeAudioContextType context_type, types::AudioContexts metadata_context_type, std::vector ccid_list = {}); - bool SetContextType(types::LeAudioContextType context_type); - types::LeAudioContextType GetContextType(void); uint32_t GetSduInterval(uint8_t direction); uint8_t GetSCA(void); uint8_t GetPacking(void); @@ -286,14 +284,12 @@ class LeAudioDeviceGroup { bool ReloadAudioLocations(void); bool ReloadAudioDirections(void); const set_configurations::AudioSetConfiguration* GetActiveConfiguration(void); - types::LeAudioContextType GetCurrentContextType(void); bool IsPendingConfiguration(void); void SetPendingConfiguration(void); void ClearPendingConfiguration(void); bool IsConfigurationSupported( LeAudioDevice* leAudioDevice, const set_configurations::AudioSetConfiguration* audio_set_conf); - types::AudioContexts GetActiveContexts(void); std::optional GetCodecConfigurationByDirection( types::LeAudioContextType group_context_type, uint8_t direction); bool IsContextSupported(types::LeAudioContextType group_context_type); @@ -325,10 +321,18 @@ class LeAudioDeviceGroup { pending_update_available_contexts_ = audio_contexts; } + inline types::LeAudioContextType GetCurrentContextType(void) const { + return active_context_type_; + } + inline types::AudioContexts GetMetadataContextType(void) const { return metadata_context_type_; } + inline types::AudioContexts GetActiveContexts(void) { + return active_contexts_mask_; + } + bool IsInTransition(void); bool IsReleasing(void); void Dump(int fd); @@ -353,6 +357,7 @@ class LeAudioDeviceGroup { types::LeAudioContextType active_context_type_; types::AudioContexts metadata_context_type_; types::AudioContexts active_contexts_mask_; + std::optional pending_update_available_contexts_; std::map @@ -360,7 +365,6 @@ class LeAudioDeviceGroup { types::AseState target_state_; types::AseState current_state_; - types::LeAudioContextType context_type_; std::vector> leAudioDevices_; }; diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc index ae5bf998694..36f25aeb3f1 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc @@ -677,8 +677,9 @@ class UnicastTestNoInit : public Test { return false; } - group->Configure(group->GetContextType(), - static_cast(group->GetContextType()), {}); + group->Configure( + group->GetCurrentContextType(), + static_cast(group->GetCurrentContextType()), {}); if (!group->CigAssignCisIds(leAudioDevice)) return false; group->CigAssignCisConnHandlesToAses(leAudioDevice); @@ -761,15 +762,6 @@ class UnicastTestNoInit : public Test { types::LeAudioContextType context_type, types::AudioContexts metadata_context_type, std::vector ccid_list) { - if (group->GetState() == - types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { - if (group->GetContextType() != context_type) { - /* TODO: Switch context of group */ - group->SetContextType(context_type); - } - return true; - } - /* Do what ReleaseCisIds(group) does: start */ LeAudioDevice* leAudioDevice = group->GetFirstDevice(); while (leAudioDevice != nullptr) { @@ -790,7 +782,6 @@ class UnicastTestNoInit : public Test { if (group->GetState() == types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) { group->CigGenerateCisIds(context_type); - group->SetContextType(context_type); std::vector conn_handles; for (uint8_t i = 0; i < (uint8_t)(group->cises_.size()); i++) { diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc index 82914610f0c..734a45341f0 100644 --- a/system/bta/le_audio/state_machine.cc +++ b/system/bta/le_audio/state_machine.cc @@ -193,7 +193,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { } group->CigGenerateCisIds(context_type); - group->SetContextType(context_type); /* All ASEs should aim to achieve target state */ SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); PrepareAndSendCodecConfigure(group, group->GetFirstActiveDevice()); -- GitLab From c55ac6b86fde3333510a9e8e931413c4564025c1 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 22 Aug 2022 13:22:04 -0700 Subject: [PATCH 008/667] Add an IRK rotation sl4a_sl4a test This test verifies that GATT reconnections succeed after an IRK rotation. Bug: 237600923 Test: system/gd/cert/run --clean --sl4a_sl4a IrkRotationTest Merged-In: Idc54f21f366074070f245fccd9fdda14a50e1645 Change-Id: Idc54f21f366074070f245fccd9fdda14a50e1645 --- .../sl4a_sl4a/advertising/le_advertising.py | 6 +- .../tests/sl4a_sl4a/lib/le_advertiser.py | 5 +- .../tests/sl4a_sl4a/lib/le_scanner.py | 1 + .../blueberry/tests/sl4a_sl4a/lib/security.py | 1 + .../sl4a_sl4a/security/irk_rotation_test.py | 168 ++++++++++++++++++ .../sl4a_sl4a/security/oob_pairing_test.py | 2 +- .../tests/sl4a_sl4a/sl4a_sl4a_test_runner.py | 2 + 7 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py diff --git a/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py b/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py index 4e0f2bcc008..bd0283cf634 100644 --- a/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py +++ b/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py @@ -38,7 +38,7 @@ class LeAdvertisingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass): super().teardown_test() def test_advertise_name(self): - rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu() + rpa_address = self.cert_advertiser_.advertise_public_extended_pdu() self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name()) self.dut_scanner_.stop_scanning() self.cert_advertiser_.stop_advertising() @@ -48,10 +48,10 @@ class LeAdvertisingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass): self.test_advertise_name() def test_advertise_name_twice_no_stop(self): - rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu() + rpa_address = self.cert_advertiser_.advertise_public_extended_pdu() self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name()) self.dut_scanner_.stop_scanning() - rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu() + rpa_address = self.cert_advertiser_.advertise_public_extended_pdu() self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name()) self.dut_scanner_.stop_scanning() self.cert_advertiser_.stop_advertising() diff --git a/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py b/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py index b6d5cfe049e..1c075a3b4c4 100644 --- a/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py +++ b/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py @@ -49,16 +49,17 @@ class LeAdvertiser(Closable): return False return True - def advertise_rpa_public_extended_pdu(self, name="SL4A Device"): + def advertise_public_extended_pdu(self, address_type=common.RANDOM_DEVICE_ADDRESS, name="SL4A Device"): if self.is_advertising: logging.info("Already advertising!") return + logging.info("Configuring advertisement with address type %d", address_type) self.is_advertising = True self.device.sl4a.bleSetScanSettingsLegacy(False) self.device.sl4a.bleSetAdvertiseSettingsIsConnectable(True) self.device.sl4a.bleSetAdvertiseDataIncludeDeviceName(True) self.device.sl4a.bleSetAdvertiseSettingsAdvertiseMode(ble_advertise_settings_modes['low_latency']) - self.device.sl4a.bleSetAdvertiseSettingsOwnAddressType(common.RANDOM_DEVICE_ADDRESS) + self.device.sl4a.bleSetAdvertiseSettingsOwnAddressType(address_type) self.advertise_callback, self.advertise_data, self.advertise_settings = generate_ble_advertise_objects( self.device.sl4a) self.device.sl4a.bleStartBleAdvertising(self.advertise_callback, self.advertise_data, self.advertise_settings) diff --git a/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py b/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py index c7874e68e90..0148f47e23f 100644 --- a/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py +++ b/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py @@ -142,6 +142,7 @@ class LeScanner(Closable): self.device.sl4a.bleSetScanSettingsLegacy(False) self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a) expected_event_name = scan_result.format(1) + self.device.ed.clear_events(expected_event_name) # Start scanning on SL4A DUT self.device.sl4a.bleSetScanFilterDeviceName(name) diff --git a/system/blueberry/tests/sl4a_sl4a/lib/security.py b/system/blueberry/tests/sl4a_sl4a/lib/security.py index 05a66459875..83e4cd6b921 100644 --- a/system/blueberry/tests/sl4a_sl4a/lib/security.py +++ b/system/blueberry/tests/sl4a_sl4a/lib/security.py @@ -83,6 +83,7 @@ class Security: def remove_all_bonded_devices(self): bonded_devices = self.__device.sl4a.bluetoothGetBondedDevices() for device in bonded_devices: + logging.info(device) self.remove_bond(device["address"]) def remove_bond(self, address): diff --git a/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py b/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py new file mode 100644 index 00000000000..9061f13f1a5 --- /dev/null +++ b/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# +# 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. + +import binascii +import io +import logging +import os +import queue + +from blueberry.facade import common_pb2 as common +from blueberry.tests.gd.cert.context import get_current_context +from blueberry.tests.gd.cert.truth import assertThat +from blueberry.tests.gd_sl4a.lib.ble_lib import disable_bluetooth +from blueberry.tests.gd_sl4a.lib.ble_lib import enable_bluetooth +from blueberry.tests.gd_sl4a.lib.bt_constants import ble_address_types +from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test +from blueberry.tests.sl4a_sl4a.lib.security import Security +from blueberry.utils.bt_gatt_constants import GattCallbackString +from blueberry.utils.bt_gatt_constants import GattTransport + + +class IrkRotationTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass): + + def setup_class(self): + super().setup_class() + self.default_timeout = 10 # seconds + + def setup_test(self): + assertThat(super().setup_test()).isTrue() + + def teardown_test(self): + current_test_dir = get_current_context().get_full_output_path() + self.cert.adb.pull([ + "/data/misc/bluetooth/logs/btsnoop_hci.log", + os.path.join(current_test_dir, "CERT_%s_btsnoop_hci.log" % self.cert.serial) + ]) + self.cert.adb.pull([ + "/data/misc/bluetooth/logs/btsnoop_hci.log.last", + os.path.join(current_test_dir, "CERT_%s_btsnoop_hci.log.last" % self.cert.serial) + ]) + super().teardown_test() + self.cert.adb.shell("setprop bluetooth.core.gap.le.privacy.enabled \'\'") + + def _wait_for_event(self, expected_event_name, device): + try: + event_info = device.ed.pop_event(expected_event_name, self.default_timeout) + logging.info(event_info) + except queue.Empty as error: + logging.error("Failed to find event: %s", expected_event_name) + return False + return True + + def __get_cert_public_address_and_irk_from_bt_config(self): + # Pull IRK from SL4A cert side to pass in from SL4A DUT side when scanning + bt_config_file_path = os.path.join(get_current_context().get_full_output_path(), + "DUT_%s_bt_config.conf" % self.cert.serial) + try: + self.cert.adb.pull(["/data/misc/bluedroid/bt_config.conf", bt_config_file_path]) + except AdbError as error: + logging.error("Failed to pull SL4A cert BT config") + return False + logging.debug("Reading SL4A cert BT config") + with io.open(bt_config_file_path) as f: + for line in f.readlines(): + stripped_line = line.strip() + if (stripped_line.startswith("Address")): + address_fields = stripped_line.split(' ') + # API currently requires public address to be capitalized + address = address_fields[2].upper() + logging.debug("Found cert address: %s" % address) + continue + if (stripped_line.startswith("LE_LOCAL_KEY_IRK")): + irk_fields = stripped_line.split(' ') + irk = irk_fields[2] + logging.debug("Found cert IRK: %s" % irk) + continue + + return address, irk + + def test_le_reconnect_after_irk_rotation_cert_privacy_enabled(self): + self._test_le_reconnect_after_irk_rotation(True) + + def test_le_reconnect_after_irk_rotation_cert_privacy_disabled(self): + 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") + enable_bluetooth(self.cert.sl4a, self.cert.ed) + self.cert.sl4a.bluetoothDisableBLE() + self._test_le_reconnect_after_irk_rotation(False) + + def _bond_remote_device(self, cert_privacy_enabled, cert_public_address): + if cert_privacy_enabled: + self.cert_advertiser_.advertise_public_extended_pdu() + else: + self.cert_advertiser_.advertise_public_extended_pdu(common.PUBLIC_DEVICE_ADDRESS) + + advertising_device_name = self.cert_advertiser_.get_local_advertising_name() + connect_address = self.dut_scanner_.scan_for_name(advertising_device_name) + + # Bond + logging.info("Bonding with %s", connect_address) + self.dut_security_.create_bond_numeric_comparison(connect_address) + self.dut_scanner_.stop_scanning() + self.cert_advertiser_.stop_advertising() + + return connect_address + + def _test_le_reconnect_after_irk_rotation(self, cert_privacy_enabled): + + cert_public_address, irk = self.__get_cert_public_address_and_irk_from_bt_config() + self._bond_remote_device(cert_privacy_enabled, cert_public_address) + + # Remove all bonded devices to rotate the IRK + logging.info("Unbonding all devices") + self.dut_security_.remove_all_bonded_devices() + self.cert_security_.remove_all_bonded_devices() + + # Bond again + logging.info("Rebonding remote device") + connect_address = self._bond_remote_device(cert_privacy_enabled, cert_public_address) + + # Connect GATT + logging.info("Connecting GATT to %s", connect_address) + gatt_callback = self.dut.sl4a.gattCreateGattCallback() + bluetooth_gatt = self.dut.sl4a.gattClientConnectGatt(gatt_callback, connect_address, False, + GattTransport.TRANSPORT_LE, False, None) + assertThat(bluetooth_gatt).isNotNone() + expected_event_name = GattCallbackString.GATT_CONN_CHANGE.format(gatt_callback) + assertThat(self._wait_for_event(expected_event_name, self.dut)).isTrue() + + # Close GATT connection + logging.info("Closing GATT connection") + self.dut.sl4a.gattClientClose(bluetooth_gatt) + + # Reconnect GATT + logging.info("Reconnecting GATT") + gatt_callback = self.dut.sl4a.gattCreateGattCallback() + bluetooth_gatt = self.dut.sl4a.gattClientConnectGatt(gatt_callback, connect_address, False, + GattTransport.TRANSPORT_LE, False, None) + assertThat(bluetooth_gatt).isNotNone() + expected_event_name = GattCallbackString.GATT_CONN_CHANGE.format(gatt_callback) + assertThat(self._wait_for_event(expected_event_name, self.dut)).isTrue() + + # Disconnect GATT + logging.info("Disconnecting GATT") + self.dut.sl4a.gattClientDisconnect(gatt_callback) + expected_event_name = GattCallbackString.GATT_CONN_CHANGE.format(gatt_callback) + assertThat(self._wait_for_event(expected_event_name, self.dut)).isTrue() + + # Reconnect GATT + logging.info("Reconnecting GATT") + self.dut.sl4a.gattClientReconnect(gatt_callback) + expected_event_name = GattCallbackString.GATT_CONN_CHANGE.format(gatt_callback) + assertThat(self._wait_for_event(expected_event_name, self.dut)).isTrue() diff --git a/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py b/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py index becdbc7fab1..cbb25f299a7 100644 --- a/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py +++ b/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py @@ -67,7 +67,7 @@ class OobPairingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass): def __test_scan(self, address_type="public"): cert_public_address, irk = self.__get_cert_public_address_and_irk_from_bt_config() - rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu() + rpa_address = self.cert_advertiser_.advertise_public_extended_pdu() self.dut_scanner_.start_identity_address_scan(cert_public_address, ble_address_types[address_type]) self.dut_scanner_.stop_scanning() self.cert_advertiser_.stop_advertising() diff --git a/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py b/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py index c08836d7066..e9cd9d375e2 100644 --- a/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py +++ b/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py @@ -18,6 +18,7 @@ from blueberry.tests.sl4a_sl4a.advertising.le_advertising import LeAdvertisingTe from blueberry.tests.sl4a_sl4a.gatt.gatt_connect_test import GattConnectTest from blueberry.tests.sl4a_sl4a.gatt.gatt_connect_with_irk_test import GattConnectWithIrkTest from blueberry.tests.sl4a_sl4a.gatt.gatt_notify_test import GattNotifyTest +from blueberry.tests.sl4a_sl4a.security.irk_rotation_test import IrkRotationTest from blueberry.tests.sl4a_sl4a.security.oob_pairing_test import OobPairingTest from mobly import suite_runner @@ -27,6 +28,7 @@ ALL_TESTS = [ GattConnectTest, GattConnectWithIrkTest, GattNotifyTest, + IrkRotationTest, LeAdvertisingTest, OobPairingTest, ] -- GitLab From aed6766081986a5a40238982c9bb636798997cc8 Mon Sep 17 00:00:00 2001 From: chenlei7 Date: Wed, 17 Aug 2022 20:39:23 +0800 Subject: [PATCH 009/667] Bluetooth: Fix gatt can't writeCharacteristic Le device connected by more then one APP, fix can't write characteristic issue. Tag: #bugfix Bug: 243105389 Bug: 243184580 Test: manual Signed-off-by: chenlei7 Merged-In: Icff2d56ac885bf04b212d2b608bfc69f25350179 Change-Id: Icff2d56ac885bf04b212d2b608bfc69f25350179 --- .../android/bluetooth/gatt/GattService.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java index 12d3bf1dd41..cec3bfd2fd9 100644 --- a/android/app/src/com/android/bluetooth/gatt/GattService.java +++ b/android/app/src/com/android/bluetooth/gatt/GattService.java @@ -258,9 +258,9 @@ public class GattService extends ProfileService { /** * HashMap used to synchronize writeCharacteristic calls mapping remote device address to - * available permit (either 1 or 0). + * available permit (connectId or -1). */ - private final HashMap mPermits = new HashMap<>(); + private final HashMap mPermits = new HashMap<>(); private AdapterService mAdapterService; private BluetoothAdapterProxy mBluetoothAdapterProxy; @@ -2025,7 +2025,7 @@ public class GattService extends ProfileService { synchronized (mPermits) { Log.d(TAG, "onConnected() - adding permit for address=" + address); - mPermits.putIfAbsent(address, new AtomicBoolean(true)); + mPermits.putIfAbsent(address, -1); } connectionState = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED; @@ -2057,6 +2057,13 @@ public class GattService extends ProfileService { + address); mPermits.remove(address); } + } else { + synchronized (mPermits) { + if (mPermits.get(address) == connId) { + Log.d(TAG, "onDisconnected() - set permit -1 for address=" + address); + mPermits.put(address, -1); + } + } } if (app != null) { @@ -2362,7 +2369,7 @@ public class GattService extends ProfileService { synchronized (mPermits) { Log.d(TAG, "onWriteCharacteristic() - increasing permit for address=" + address); - mPermits.get(address).set(true); + mPermits.put(address, -1); } if (VDBG) { @@ -3655,18 +3662,18 @@ public class GattService extends ProfileService { Log.d(TAG, "writeCharacteristic() - trying to acquire permit."); // Lock the thread until onCharacteristicWrite callback comes back. synchronized (mPermits) { - AtomicBoolean atomicBoolean = mPermits.get(address); - if (atomicBoolean == null) { + Integer permit = mPermits.get(address); + if (permit == null) { Log.d(TAG, "writeCharacteristic() - atomicBoolean uninitialized!"); return BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED; } - boolean success = atomicBoolean.get(); + boolean success = (permit == -1); if (!success) { Log.d(TAG, "writeCharacteristic() - no permit available."); return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; } - atomicBoolean.set(false); + mPermits.put(address, connId); } gattClientWriteCharacteristicNative(connId, handle, writeType, authReq, value); -- GitLab From 30f465d5ce8f4cf9825446337136f5100abf911e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Wed, 28 Sep 2022 14:22:27 +0000 Subject: [PATCH 010/667] leAudioService: Fix reconnecting lead devices. mLostLeadDeviceWhileStreaming shall be cleared only whe lead device reconnects. Bug: 248931295 Test: atest BluetoothInstrumentationTests Tag: #feature Merged-In: I67f3a66edbb826b3622d55d958898be4effcd6e4 Change-Id: I67f3a66edbb826b3622d55d958898be4effcd6e4 (cherry picked from commit 768ddc6707884820f001555e462713e86a5adae2) --- .../android/bluetooth/le_audio/LeAudioService.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 19809999462..b9a6fe68a71 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -1076,7 +1076,7 @@ public class LeAudioService extends ProfileService { } if (DBG) { - Log.d(TAG, "connect(): " + device); + Log.d(TAG, "connect(): " + storedDevice); } synchronized (mStateMachines) { @@ -1248,8 +1248,13 @@ public class LeAudioService extends ProfileService { break; case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: - if (descriptor != null) { - if (DBG) Log.d(TAG, "Removing from lost devices : " + device); + if (descriptor != null + && Objects.equals( + descriptor.mLostLeadDeviceWhileStreaming, + device)) { + if (DBG) { + Log.d(TAG, "Removing from lost devices : " + device); + } descriptor.mLostLeadDeviceWhileStreaming = null; /* Try to connect other devices from the group */ connectSet(device); -- GitLab From a41e6a017ba857531bf1c6b87d40c88ae5ab4c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Wed, 28 Sep 2022 14:50:09 +0000 Subject: [PATCH 011/667] leaudio: Clear ASE cis id and cis handle on ACL disconnected Bug: 248931295 Test: atest BluetoothInstrumentationTests Tag: #feature Merged-In: Ie3a96cd842b289255a7991a42d2d944a75b5e296 Change-Id: Ie3a96cd842b289255a7991a42d2d944a75b5e296 (cherry picked from commit bd85efe2e9fd3f1b47b56bdcd2c22da5ea8be823) --- system/bta/le_audio/devices.cc | 5 ++--- system/bta/le_audio/devices_test.cc | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc index 54ff6ed5390..c2e3ed83b4e 100644 --- a/system/bta/le_audio/devices.cc +++ b/system/bta/le_audio/devices.cc @@ -2450,14 +2450,13 @@ bool LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) { } void LeAudioDevice::DeactivateAllAses(void) { - /* Just clear states and keep previous configuration for use - * in case device will get reconnected - */ for (auto& ase : ases_) { if (ase.active) { ase.state = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE; ase.data_path_state = AudioStreamDataPathState::IDLE; ase.active = false; + ase.cis_id = le_audio::kInvalidCisId; + ase.cis_conn_hdl = 0; } } } diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc index 78b5df187bf..2077e37c188 100644 --- a/system/bta/le_audio/devices_test.cc +++ b/system/bta/le_audio/devices_test.cc @@ -651,6 +651,8 @@ class LeAudioAseConfigurationTest : public Test { void TestAsesInactivated(const LeAudioDevice* device) { for (const auto& ase : device->ases_) { ASSERT_FALSE(ase.active); + ASSERT_TRUE(ase.cis_id == ::le_audio::kInvalidCisId); + ASSERT_TRUE(ase.cis_conn_hdl == 0); } } @@ -948,10 +950,20 @@ TEST_F(LeAudioAseConfigurationTest, test_reconnection_media) { TestSingleAseConfiguration(LeAudioContextType::MEDIA, data, 2, configuration); - SetCisInformationToActiveAse(); + /* Generate CISes, symulate CIG creation and assign cis handles to ASEs.*/ + group_->CigGenerateCisIds(LeAudioContextType::MEDIA); + std::vector handles = {0x0012, 0x0013}; + group_->CigAssignCisConnHandles(handles); + group_->CigAssignCisIds(left); + group_->CigAssignCisIds(right); + TestActiveAses(); /* Left got disconnected */ left->DeactivateAllAses(); + + /* Unassign from the group*/ + group_->CigUnassignCis(left); + TestAsesInactivated(left); /* Prepare reconfiguration */ @@ -979,6 +991,12 @@ TEST_F(LeAudioAseConfigurationTest, test_reconnection_media) { TestGroupAseConfigurationVerdict(data[i]); } + /* Before device is rejoining, and group already exist, cis handles are + * assigned before sending codec config + */ + group_->CigAssignCisIds(left); + group_->CigAssignCisConnHandlesToAses(left); + TestActiveAses(); } } // namespace -- GitLab From e6cd07eb004eaf575f49451864a6d0a45d591828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Fri, 30 Sep 2022 14:36:21 +0000 Subject: [PATCH 012/667] eatt: Fix getting channel with the queue data to send When GATT is asking about channel with a non empty data queue to send, EATT shall check also if the outstanding command is scheduled to send or maybe is already waiting for the response. Otherwise, we might end up with having channel not beeing used. Bug: 249992170 Test: atest BluetoothInstrumentationTests Test: atest --host net_test_eatt Tag: #feature Merged-In: Idceaf76847851cc8e256da4a3c4069c9e5cc93fd Change-Id: Idceaf76847851cc8e256da4a3c4069c9e5cc93fd (cherry picked from commit e84baf9ca92c09f57cd0d643c73b2e4620a7a12c) --- system/stack/eatt/eatt.cc | 2 +- system/stack/eatt/eatt.h | 3 ++- system/stack/eatt/eatt_impl.h | 10 ++++++++-- system/stack/gatt/gatt_cl.cc | 3 ++- system/stack/test/common/mock_eatt.cc | 4 ++-- system/stack/test/common/mock_eatt.h | 2 +- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/system/stack/eatt/eatt.cc b/system/stack/eatt/eatt.cc index a023026309b..8e88be96bcb 100644 --- a/system/stack/eatt/eatt.cc +++ b/system/stack/eatt/eatt.cc @@ -175,7 +175,7 @@ bool EattExtension::IsOutstandingMsgInSendQueue(const RawAddress& bd_addr) { return pimpl_->eatt_impl_->is_outstanding_msg_in_send_queue(bd_addr); } -EattChannel* EattExtension::GetChannelWithQueuedData( +EattChannel* EattExtension::GetChannelWithQueuedDataToSend( const RawAddress& bd_addr) { return pimpl_->eatt_impl_->get_channel_with_queued_data(bd_addr); } diff --git a/system/stack/eatt/eatt.h b/system/stack/eatt/eatt.h index 828d17ac0e3..40542b00ce6 100644 --- a/system/stack/eatt/eatt.h +++ b/system/stack/eatt/eatt.h @@ -224,7 +224,8 @@ class EattExtension { * * @return pointer to EATT channel. */ - virtual EattChannel* GetChannelWithQueuedData(const RawAddress& bd_addr); + virtual EattChannel* GetChannelWithQueuedDataToSend( + const RawAddress& bd_addr); /** * Get EATT channel available to send GATT request. diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h index 4943b345e08..f8a37701271 100644 --- a/system/stack/eatt/eatt_impl.h +++ b/system/stack/eatt/eatt_impl.h @@ -709,7 +709,10 @@ struct eatt_impl { auto iter = find_if( eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), [](const std::pair>& el) { - return !el.second->cl_cmd_q_.empty(); + if (el.second->cl_cmd_q_.empty()) return false; + + tGATT_CMD_Q& cmd = el.second->cl_cmd_q_.front(); + return cmd.to_send; }); return (iter != eatt_dev->eatt_channels.end()); } @@ -721,7 +724,10 @@ struct eatt_impl { auto iter = find_if( eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), [](const std::pair>& el) { - return !el.second->cl_cmd_q_.empty(); + if (el.second->cl_cmd_q_.empty()) return false; + + tGATT_CMD_Q& cmd = el.second->cl_cmd_q_.front(); + return cmd.to_send; }); return (iter == eatt_dev->eatt_channels.end()) ? nullptr : iter->second.get(); diff --git a/system/stack/gatt/gatt_cl.cc b/system/stack/gatt/gatt_cl.cc index d62a0872d14..215ea2abdfd 100644 --- a/system/stack/gatt/gatt_cl.cc +++ b/system/stack/gatt/gatt_cl.cc @@ -1138,7 +1138,8 @@ bool gatt_cl_send_next_cmd_inq(tGATT_TCB& tcb) { cl_cmd_q = &tcb.cl_cmd_q; } else { EattChannel* channel = - EattExtension::GetInstance()->GetChannelWithQueuedData(tcb.peer_bda); + EattExtension::GetInstance()->GetChannelWithQueuedDataToSend( + tcb.peer_bda); cl_cmd_q = &channel->cl_cmd_q_; } diff --git a/system/stack/test/common/mock_eatt.cc b/system/stack/test/common/mock_eatt.cc index 6e07bf5469c..dc9867fb36d 100644 --- a/system/stack/test/common/mock_eatt.cc +++ b/system/stack/test/common/mock_eatt.cc @@ -86,9 +86,9 @@ bool EattExtension::IsOutstandingMsgInSendQueue(const RawAddress& bd_addr) { return pimpl_->IsOutstandingMsgInSendQueue(bd_addr); } -EattChannel* EattExtension::GetChannelWithQueuedData( +EattChannel* EattExtension::GetChannelWithQueuedDataToSend( const RawAddress& bd_addr) { - return pimpl_->GetChannelWithQueuedData(bd_addr); + return pimpl_->GetChannelWithQueuedDataToSend(bd_addr); } EattChannel* EattExtension::GetChannelAvailableForClientRequest( diff --git a/system/stack/test/common/mock_eatt.h b/system/stack/test/common/mock_eatt.h index 6d1682befcc..bbeb5022979 100644 --- a/system/stack/test/common/mock_eatt.h +++ b/system/stack/test/common/mock_eatt.h @@ -52,7 +52,7 @@ class MockEattExtension : public EattExtension { (const RawAddress& bd_addr)); MOCK_METHOD((void), FreeGattResources, (const RawAddress& bd_addr)); MOCK_METHOD((bool), IsOutstandingMsgInSendQueue, (const RawAddress& bd_addr)); - MOCK_METHOD((EattChannel*), GetChannelWithQueuedData, + MOCK_METHOD((EattChannel*), GetChannelWithQueuedDataToSend, (const RawAddress& bd_addr)); MOCK_METHOD((EattChannel*), GetChannelAvailableForClientRequest, (const RawAddress& bd_addr)); -- GitLab From 3c885df4d1b70111ae335584071c2c8a2f83ce8a Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Mon, 26 Sep 2022 13:24:15 +0000 Subject: [PATCH 013/667] VolumeControl: Fix volume for the activated device For the newly activated device we should be passing volume index rather than Le Audio volume level. Bug: 243334827 Bug: 241501978 Tag: #feature Test: tested manually Change-Id: I8392120eef653f67b5ca91452847943b4cebb644 Merged-In: I8392120eef653f67b5ca91452847943b4cebb644 (cherry picked from commit f0e34592935ec0b09bc9f4f2127a7c0787524f7a) --- .../android/bluetooth/le_audio/LeAudioService.java | 6 +++--- .../android/bluetooth/vc/VolumeControlService.java | 14 ++++++++++++++ .../bluetooth/le_audio/LeAudioServiceTest.java | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) 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 b9a6fe68a71..3b4de0d7211 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -415,7 +415,7 @@ public class LeAudioService extends ProfileService { sLeAudioService = instance; } - private int getGroupVolume(int groupId) { + private int getAudioDeviceGroupVolume(int groupId) { if (mVolumeControlService == null) { mVolumeControlService = mServiceFactory.getVolumeControlService(); if (mVolumeControlService == null) { @@ -424,7 +424,7 @@ public class LeAudioService extends ProfileService { } } - return mVolumeControlService.getGroupVolume(groupId); + return mVolumeControlService.getAudioDeviceGroupVolume(groupId); } public boolean connect(BluetoothDevice device) { @@ -926,7 +926,7 @@ public class LeAudioService extends ProfileService { } int volume = IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; if (mActiveAudioOutDevice != null) { - volume = getGroupVolume(groupId); + volume = getAudioDeviceGroupVolume(groupId); } mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioOutDevice, diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java index 0b0fa265e3a..e5cd625cb05 100644 --- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java +++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java @@ -586,6 +586,11 @@ public class VolumeControlService extends ProfileService { * {@hide} */ public void setGroupVolume(int groupId, int volume) { + if (volume < 0) { + Log.w(TAG, "Tried to set invalid volume " + volume + ". Ignored."); + return; + } + mGroupVolumeCache.put(groupId, volume); mVolumeControlNativeInterface.setGroupVolume(groupId, volume); } @@ -683,6 +688,15 @@ public class VolumeControlService extends ProfileService { } } + /** + * {@hide} + */ + public int getAudioDeviceGroupVolume(int groupId) { + int volume = getGroupVolume(groupId); + if (volume == IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) return -1; + return getDeviceVolume(getBluetoothContextualVolumeStream(), volume); + } + int getDeviceVolume(int streamType, int bleVolume) { int deviceMaxVolume = mAudioManager.getStreamMaxVolume(streamType); 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 b37ae31cbce..becde7ec676 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 @@ -1428,7 +1428,7 @@ public class LeAudioServiceTest { //Add location support. injectAudioConfChanged(groupId, availableContexts); - doReturn(-1).when(mVolumeControlService).getGroupVolume(groupId); + doReturn(-1).when(mVolumeControlService).getAudioDeviceGroupVolume(groupId); //Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); @@ -1445,7 +1445,7 @@ public class LeAudioServiceTest { verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), any(), any(BluetoothProfileConnectionInfo.class)); - doReturn(100).when(mVolumeControlService).getGroupVolume(groupId); + doReturn(100).when(mVolumeControlService).getAudioDeviceGroupVolume(groupId); //Set back to active and check if last volume is restored. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); -- GitLab From 37d0b68981762f8682058b483e76a02e6ccb409f Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 3 Oct 2022 12:33:51 -0700 Subject: [PATCH 014/667] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I5171c01c2bd17da6507b5758042b1ff7fbd78569 --- android/app/res/values-ar/strings.xml | 2 +- android/app/res/values-eu/strings.xml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/android/app/res/values-ar/strings.xml b/android/app/res/values-ar/strings.xml index 92ad4d75357..f61cb1452fa 100644 --- a/android/app/res/values-ar/strings.xml +++ b/android/app/res/values-ar/strings.xml @@ -115,7 +115,7 @@ "فتح" "محو من القائمة" "محو" - "التعرّف التلقائي على الموسيقى" + "قيد التشغيل الآن" "حفظ" "إلغاء" "حدد الحسابات التي تريد مشاركتها عبر البلوتوث. لا يزال يتعين عليك قبول أي دخول إلى الحسابات أثناء الاتصال." diff --git a/android/app/res/values-eu/strings.xml b/android/app/res/values-eu/strings.xml index 6ea17fc0a8c..72589bfeb47 100644 --- a/android/app/res/values-eu/strings.xml +++ b/android/app/res/values-eu/strings.xml @@ -20,14 +20,14 @@ "Bluetooth bidezko partekatzeen kudeatzailea atzitzea eta fitxategiak transferitzeko erabiltzeko baimena ematen die aplikazioei." "Ezarri Bluetooth bidezko gailuak onartutakoen zerrendan." "Bluetooth bidezko gailu bat aldi baterako onartutakoen zerrendan ezartzeko baimena ematen die aplikazioei, gailu honetara fitxategiak bidaltzeko baimena izan dezan, baina gailu honen erabiltzaileari berrespena eskatu beharrik gabe." - "Bluetooth-a" + "Bluetootha" "Identifikatu ezin den gailua" "Ezezaguna" "Hegaldi modua" - "Ezin duzu erabili Bluetooth-a Hegaldi moduan." + "Ezin duzu erabili Bluetootha Hegaldi moduan." - "Bluetooth-zerbitzuak erabiltzeko, Bluetooth-a aktibatu behar duzu." - "Bluetooth-a aktibatu nahi duzu?" + "Bluetooth-zerbitzuak erabiltzeko, Bluetootha aktibatu behar duzu." + "Bluetootha aktibatu nahi duzu?" "Utzi" "Aktibatu" "Fitxategi-transferentzia" @@ -76,7 +76,7 @@ "Ez dago fitxategirik" "Ez dago horrelako fitxategirik. \n" "Itxaron…" - "Bluetooth-a aktibatzen…" + "Bluetootha aktibatzen…" "Fitxategia jasoko da. Egoera kontrolatzeko, joan Jakinarazpenen panelera." "Ezin da fitxategia jaso." "\"%1$s\" igorlearen fitxategia jasotzeari utzi zaio" @@ -127,11 +127,11 @@ "Deskonektatu da Bluetooth bidezko audioa" "Bluetooth bidezko audioa" "Ezin dira transferitu 4 GB baino gehiagoko fitxategiak" - "Konektatu Bluetooth-era" - "Bluetooth-a aktibatuta mantentzen da hegaldi moduan" - "Bluetooth-a aktibatuta utziz gero, aktibatuta mantenduko da hegaldi modua erabiltzen duzun hurrengoan" - "Bluetooth-a aktibatuta mantenduko da" - "Hegaldi moduan, Bluetooth-a aktibatuta mantentzeaz gogoratzen da telefonoa. Hori aldatzeko, desaktibatu Bluetooth-a." - "Wifia eta Bluetooth-a aktibatuta mantentzen dira" - "Hegaldi moduan, wifia eta Bluetooth-a aktibatuta mantentzeaz gogoratzen da telefonoa. Hori aldatzeko, desaktiba itzazu." + "Konektatu Bluetoothera" + "Bluetootha aktibatuta mantentzen da hegaldi moduan" + "Bluetootha aktibatuta utziz gero, aktibatuta mantenduko da hegaldi modua erabiltzen duzun hurrengoan" + "Bluetootha aktibatuta mantenduko da" + "Hegaldi moduan, Bluetootha aktibatuta mantentzeaz gogoratzen da telefonoa. Hori aldatzeko, desaktibatu Bluetootha." + "Wifia eta Bluetootha aktibatuta mantentzen dira" + "Hegaldi moduan, wifia eta Bluetootha aktibatuta mantentzeaz gogoratzen da telefonoa. Hori aldatzeko, desaktiba itzazu." -- GitLab From 3bb9f4f78000df92f40189d85fe95e484b9f4d6f Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 3 Oct 2022 12:34:10 -0700 Subject: [PATCH 015/667] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I701ff0198ff25f6963889f3178cb69552506bb79 --- android/app/res/values-eu/test_strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/res/values-eu/test_strings.xml b/android/app/res/values-eu/test_strings.xml index e6016491941..4c501ab19e1 100644 --- a/android/app/res/values-eu/test_strings.xml +++ b/android/app/res/values-eu/test_strings.xml @@ -1,7 +1,7 @@ - "Bluetooth-a" + "Bluetootha" "Sartu erregistroa" "Berretsi erregistroa" "ACK erregistroa" -- GitLab From 3ae98d68be89a76bd83ebd7ea0d42356991fd2c1 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 21 Sep 2022 18:35:38 -0700 Subject: [PATCH 016/667] Add a timeout for generating local oob data Tag: #feature Bug: 245770306 Test: system/gd/cert/run --clean --sl4a_sl4a OobPairingTest Ignore-AOSP-First: Merging into QPR branch first Change-Id: If935583dab725ea41585e04e67aeb6e283e23683 --- .../bluetooth/btservice/AdapterService.java | 19 ++++- system/blueberry/tests/sl4a_sl4a/lib/l2cap.py | 38 ++++++++++ .../blueberry/tests/sl4a_sl4a/lib/oob_data.py | 7 ++ .../blueberry/tests/sl4a_sl4a/lib/security.py | 72 ++++++++++++------- .../sl4a_sl4a/security/oob_pairing_test.py | 46 +++++++----- 5 files changed, 141 insertions(+), 41 deletions(-) create mode 100644 system/blueberry/tests/sl4a_sl4a/lib/l2cap.py diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index f29e1649b63..fdd365169e3 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -169,6 +169,7 @@ public class AdapterService extends Service { private static final int MIN_OFFLOADED_FILTERS = 10; private static final int MIN_OFFLOADED_SCAN_STORAGE_BYTES = 1024; private static final Duration PENDING_SOCKET_HANDOFF_TIMEOUT = Duration.ofMinutes(1); + private static final Duration GENERATE_LOCAL_OOB_DATA_TIMEOUT = Duration.ofSeconds(2); private final Object mEnergyInfoLock = new Object(); private int mStackReportedState; @@ -3999,15 +4000,31 @@ public class AdapterService extends Service { if (mOobDataCallbackQueue.peek() != null) { try { callback.onError(BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST); - return; } catch (RemoteException e) { Log.e(TAG, "Failed to make callback", e); } + return; } mOobDataCallbackQueue.offer(callback); + mHandler.postDelayed(() -> removeFromOobDataCallbackQueue(callback), + GENERATE_LOCAL_OOB_DATA_TIMEOUT.toMillis()); generateLocalOobDataNative(transport); } + private synchronized void removeFromOobDataCallbackQueue(IBluetoothOobDataCallback callback) { + if (callback == null) { + return; + } + + if (mOobDataCallbackQueue.peek() == callback) { + try { + mOobDataCallbackQueue.poll().onError(BluetoothStatusCodes.ERROR_UNKNOWN); + } catch (RemoteException e) { + Log.e(TAG, "Failed to make OobDataCallback to remove callback from queue", e); + } + } + } + /* package */ synchronized void notifyOobDataCallback(int transport, OobData oobData) { if (mOobDataCallbackQueue.peek() == null) { Log.e(TAG, "Failed to make callback, no callback exists"); diff --git a/system/blueberry/tests/sl4a_sl4a/lib/l2cap.py b/system/blueberry/tests/sl4a_sl4a/lib/l2cap.py new file mode 100644 index 00000000000..3957146cf07 --- /dev/null +++ b/system/blueberry/tests/sl4a_sl4a/lib/l2cap.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# 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. + +import logging + +from blueberry.tests.gd.cert.truth import assertThat + + +class L2cap: + + __l2cap_connection_timeout = 30 #seconds + __device = None + + def __init__(self, device): + self.__device = device + + def create_l2cap_le_coc(self, address, psm, secure): + logging.info("creating l2cap channel with secure=%r and psm %s", secure, psm) + self.__device.sl4a.bluetoothSocketConnBeginConnectThreadPsm(address, True, psm, secure) + + # Starts listening on the l2cap server socket, returns the psm + def listen_using_l2cap_coc(self, secure): + logging.info("Listening for l2cap channel with secure=%r and psm %s", secure, psm) + self.__device.sl4a.bluetoothSocketConnBeginAcceptThreadPsm(__l2cap_connection_timeout, True, secure) + return self.__device.sl4a.bluetoothSocketConnGetPsm() diff --git a/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py b/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py index d37f3f46d09..69f753a1c6b 100644 --- a/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py +++ b/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py @@ -24,6 +24,8 @@ class OobData: confirmation = None randomizer = None + ADDRESS_WITH_TYPE_LENGTH = 14 + def __init__(self, address, confirmation, randomizer): self.address = address self.confirmation = confirmation @@ -43,3 +45,8 @@ class OobData: address_str_octets = address_str_octets[:6] address_str_octets.reverse() return ":".join(address_str_octets) + + def to_sl4a_address_type(self): + if len(self.address) != self.ADDRESS_WITH_TYPE_LENGTH: + return -1 + return self.address.upper()[-2] diff --git a/system/blueberry/tests/sl4a_sl4a/lib/security.py b/system/blueberry/tests/sl4a_sl4a/lib/security.py index 83e4cd6b921..fcfb4ba0e8b 100644 --- a/system/blueberry/tests/sl4a_sl4a/lib/security.py +++ b/system/blueberry/tests/sl4a_sl4a/lib/security.py @@ -25,8 +25,8 @@ from blueberry.tests.sl4a_sl4a.lib.oob_data import OobData class Security: # Events sent from SL4A - SL4A_EVENT_GENERATED = "GeneratedOobData" - SL4A_EVENT_ERROR = "ErrorOobData" + SL4A_EVENT_GENERATE_OOB_DATA_SUCCESS = "GeneratedOobData" + SL4A_EVENT_GENERATE_OOB_DATA_ERROR = "ErrorOobData" SL4A_EVENT_BONDED = "Bonded" SL4A_EVENT_UNBONDED = "Unbonded" @@ -44,15 +44,34 @@ class Security: self.__device = device self.__device.sl4a.bluetoothStartPairingHelper(True) - def generate_oob_data(self, transport): + # Returns a tuple formatted as . The OobData is + # populated if the statuscode is 0 (SUCCESS), else it will be None + def generate_oob_data(self, transport, wait_for_oob_data_callback=True): + logging.info("Generating local OOB data") self.__device.sl4a.bluetoothGenerateLocalOobData(transport) - try: - event_info = self.__device.ed.pop_event(self.SL4A_EVENT_GENERATED, self.__default_timeout) - except queue.Empty as error: - logging.error("Failed to generate OOB data!") - return None - return OobData(event_info["data"]["address_with_type"], event_info["data"]["confirmation"], - event_info["data"]["randomizer"]) + + if wait_for_oob_data_callback is False: + return 0, None + else: + # Check for oob data generation success + try: + generate_success_event = self.__device.ed.pop_event(self.SL4A_EVENT_GENERATE_OOB_DATA_SUCCESS, + self.__default_timeout) + except queue.Empty as error: + logging.error("Failed to generate OOB data!") + # Check if generating oob data failed without blocking + try: + generate_failure_event = self.__device.ed.pop_event(self.SL4A_EVENT_GENERATE_OOB_DATA_FAILURE, 0) + except queue.Empty as error: + logging.error("Failed to generate OOB Data without error code") + assertThat(True).isFalse() + + errorcode = generate_failure_event["data"]["Error"] + logging.info("Generating local oob data failed with error code %d", errorcode) + return errorcode, None + + return 0, OobData(generate_success_event["data"]["address_with_type"], + generate_success_event["data"]["confirmation"], generate_success_event["data"]["randomizer"]) def ensure_device_bonded(self): bond_state = None @@ -65,14 +84,18 @@ class Security: logging.info("Bonded: %s", bond_state["data"]["bonded_state"]) assertThat(bond_state["data"]["bonded_state"]).isEqualTo(True) - def create_bond_out_of_band(self, oob_data): + def create_bond_out_of_band(self, oob_data, wait_for_device_bonded=True): assertThat(oob_data).isNotNone() address = oob_data.to_sl4a_address() - self.__device.sl4a.bluetoothCreateBondOutOfBand(address, self.TRANSPORT_LE, oob_data.confirmation, - oob_data.randomizer) - self.ensure_device_bonded() + address_type = oob_data.to_sl4a_address_type() + logging.info("Bonding OOB with %s and address type=%s", address, address_type) + self.__device.sl4a.bluetoothCreateLeBondOutOfBand(address, oob_data.confirmation, oob_data.randomizer, + address_type) + + if wait_for_device_bonded: + self.ensure_device_bonded() - def create_bond_numeric_comparison(self, address, transport=TRANSPORT_LE): + def create_bond_numeric_comparison(self, address, transport=TRANSPORT_LE, wait_for_device_bonded=True): assertThat(address).isNotNone() if transport == self.TRANSPORT_LE: self.__device.sl4a.bluetoothLeBond(address) @@ -87,15 +110,16 @@ class Security: self.remove_bond(device["address"]) def remove_bond(self, address): - self.__device.sl4a.bluetoothUnbond(address) - bond_state = None - try: - bond_state = self.__device.ed.pop_event(self.SL4A_EVENT_UNBONDED, self.__default_timeout) - except queue.Empty as error: - logging.error("Failed to get bond event!") - - assertThat(bond_state).isNotNone() - assertThat(bond_state["data"]["bonded_state"]).isEqualTo(False) + if self.__device.sl4a.bluetoothUnbond(address): + bond_state = None + try: + bond_state = self.__device.ed.pop_event(self.SL4A_EVENT_UNBONDED, self.__default_timeout) + except queue.Empty as error: + logging.error("Failed to get bond event!") + assertThat(bond_state).isNotNone() + assertThat(bond_state["data"]["bonded_state"]).isEqualTo(False) + else: + logging.info("remove_bond: Bluetooth Device with address: %s does not exist", address) def close(self): self.remove_all_bonded_devices() diff --git a/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py b/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py index cbb25f299a7..5bb2dce0620 100644 --- a/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py +++ b/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py @@ -19,6 +19,7 @@ import io import logging import os import queue +import time from blueberry.tests.gd.cert.context import get_current_context from blueberry.tests.gd.cert.truth import assertThat @@ -72,18 +73,29 @@ class OobPairingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass): self.dut_scanner_.stop_scanning() self.cert_advertiser_.stop_advertising() + def __create_le_bond_oob_single_sided(self, wait_for_oob_data=True, wait_for_device_bonded=True): + oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE, wait_for_oob_data) + if wait_for_oob_data: + assertThat(oob_data[0]).isEqualTo(0) + assertThat(oob_data[1]).isNotNone() + self.dut_security_.create_bond_out_of_band(oob_data[1], wait_for_device_bonded) + + def __create_le_bond_oob_double_sided(self, wait_for_oob_data=True, wait_for_device_bonded=True): + # Genearte OOB data on DUT, but we don't use it + self.dut_security_.generate_oob_data(Security.TRANSPORT_LE, wait_for_oob_data) + self.__create_le_bond_oob_single_sided(wait_for_oob_data, wait_for_device_bonded) + def test_classic_generate_local_oob_data(self): oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR) - assertThat(oob_data).isNotNone() + assertThat(oob_data[0]).isEqualTo(0) + assertThat(oob_data[1]).isNotNone() oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR) - assertThat(oob_data).isNotNone() + assertThat(oob_data[0]).isEqualTo(0) + assertThat(oob_data[1]).isNotNone() def test_classic_generate_local_oob_data_stress(self): for i in range(1, 20): - oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR) - assertThat(oob_data).isNotNone() - oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR) - assertThat(oob_data).isNotNone() + self.test_classic_generate_local_oob_data() def test_le_generate_local_oob_data(self): oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_LE) @@ -93,23 +105,25 @@ class OobPairingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass): def test_le_generate_local_oob_data_stress(self): for i in range(1, 20): - oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_LE) - assertThat(oob_data).isNotNone() - oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE) - assertThat(oob_data).isNotNone() + self.test_le_generate_local_oob_data() - def test_le_bond_oob(self): - oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE) - assertThat(oob_data).isNotNone() - self.dut_security_.create_bond_out_of_band(oob_data) + def test_le_bond(self): + self.__create_le_bond_oob_single_sided() def test_le_bond_oob_stress(self): for i in range(0, 10): logging.info("Stress #%d" % i) - self.test_le_bond_oob() + self.__create_le_bond_oob_single_sided() self.dut_security_.remove_all_bonded_devices() self.cert_security_.remove_all_bonded_devices() def test_le_generate_local_oob_data_after_le_bond_oob(self): - self.test_le_bond_oob() + self.__create_le_bond_oob_single_sided() self.test_le_generate_local_oob_data() + + def test_le_generate_oob_data_while_bonding(self): + self.__create_le_bond_oob_double_sided(True, False) + self.dut_security_.generate_oob_data(Security.TRANSPORT_LE, False) + for i in range(0, 10): + oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_LE, True) + logging.info("OOB Data came back with code: %d", oob_data[0]) -- GitLab From 3cc11eaa0ba93b54baf9e62885570b5fdf7af4d6 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 29 Sep 2022 14:29:27 -0700 Subject: [PATCH 017/667] Adds SL4A_SL4A test for LE scanning Tag: #stability Bug: 249828494 Test: system/gd/cert/run --clean --sl4a_sl4a LeScanningTest Ignore-AOSP-First: Merging into QPR branch first Change-Id: I8a9d0b67d41a40ff62ce5d7c8b886e2de562c5c4 --- .../tests/sl4a_sl4a/lib/le_scanner.py | 12 ++- .../tests/sl4a_sl4a/scanning/le_scanning.py | 91 +++++++++++++++++++ .../tests/sl4a_sl4a/sl4a_sl4a_test_runner.py | 2 + 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 system/blueberry/tests/sl4a_sl4a/scanning/le_scanning.py diff --git a/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py b/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py index 0148f47e23f..2e67d8af551 100644 --- a/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py +++ b/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py @@ -49,7 +49,7 @@ class LeScanner(Closable): def scan_for_address_expect_none(self, address, addr_type): if self.is_scanning: print("Already scanning!") - return + return None self.is_scanning = True logging.info("Start scanning for identity address {} or type {}".format(address, addr_type)) self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency']) @@ -66,11 +66,12 @@ class LeScanner(Closable): advertising_address = self.__wait_for_scan_result_event(expected_event_name, 1) assertThat(advertising_address).isNone() logging.info("Filter advertisement with address {}".format(advertising_address)) + return advertising_address def scan_for_address(self, address, addr_type): if self.is_scanning: print("Already scanning!") - return + return None self.is_scanning = True logging.info("Start scanning for identity address {} or type {}".format(address, addr_type)) self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency']) @@ -87,11 +88,12 @@ class LeScanner(Closable): advertising_address = self.__wait_for_scan_result_event(expected_event_name) assertThat(advertising_address).isNotNone() logging.info("Filter advertisement with address {}".format(advertising_address)) + return advertising_address def scan_for_address_with_irk(self, address, addr_type, irk): if self.is_scanning: print("Already scanning!") - return + return None self.is_scanning = True logging.info("Start scanning for identity address {} or type {} using irk {}".format(address, addr_type, irk)) self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency']) @@ -108,11 +110,12 @@ class LeScanner(Closable): advertising_address = self.__wait_for_scan_result_event(expected_event_name) assertThat(advertising_address).isNotNone() logging.info("Filter advertisement with address {}".format(advertising_address)) + return advertising_address def scan_for_address_with_irk_pending_intent(self, address, addr_type, irk): if self.is_scanning: print("Already scanning!") - return + return None self.is_scanning = True logging.info("Start scanning for identity address {} or type {} using irk {}".format(address, addr_type, irk)) self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency']) @@ -131,6 +134,7 @@ class LeScanner(Closable): advertising_address = self.__wait_for_scan_result_event(expected_event_name) assertThat(advertising_address).isNotNone() logging.info("Filter advertisement with address {}".format(advertising_address)) + return advertising_address def scan_for_name(self, name): if self.is_scanning: diff --git a/system/blueberry/tests/sl4a_sl4a/scanning/le_scanning.py b/system/blueberry/tests/sl4a_sl4a/scanning/le_scanning.py new file mode 100644 index 00000000000..f21ba28648a --- /dev/null +++ b/system/blueberry/tests/sl4a_sl4a/scanning/le_scanning.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# +# 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. + +import binascii +import io +import logging +import os +import queue +import time + +from blueberry.tests.gd.cert.context import get_current_context +from blueberry.tests.gd.cert.truth import assertThat +from blueberry.tests.gd_sl4a.lib.bt_constants import ble_address_types +from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test +from blueberry.tests.sl4a_sl4a.lib.security import Security + + +class LeScanningTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass): + + def __get_cert_public_address_and_irk_from_bt_config(self): + # Pull IRK from SL4A cert side to pass in from SL4A DUT side when scanning + bt_config_file_path = os.path.join(get_current_context().get_full_output_path(), + "DUT_%s_bt_config.conf" % self.cert.serial) + try: + self.cert.adb.pull(["/data/misc/bluedroid/bt_config.conf", bt_config_file_path]) + except AdbError as error: + logging.error("Failed to pull SL4A cert BT config") + return False + logging.debug("Reading SL4A cert BT config") + with io.open(bt_config_file_path) as f: + for line in f.readlines(): + stripped_line = line.strip() + if (stripped_line.startswith("Address")): + address_fields = stripped_line.split(' ') + # API currently requires public address to be capitalized + address = address_fields[2].upper() + logging.debug("Found cert address: %s" % address) + continue + if (stripped_line.startswith("LE_LOCAL_KEY_IRK")): + irk_fields = stripped_line.split(' ') + irk = irk_fields[2] + logging.debug("Found cert IRK: %s" % irk) + continue + + return address, irk + + def setup_class(self): + super().setup_class() + + def setup_test(self): + assertThat(super().setup_test()).isTrue() + + def teardown_test(self): + super().teardown_test() + + def test_scan_result_address(self): + cert_public_address, irk = self.__get_cert_public_address_and_irk_from_bt_config() + self.cert_advertiser_.advertise_public_extended_pdu() + advertising_name = self.cert_advertiser_.get_local_advertising_name() + + # Scan with name and verify we get back a scan result with the RPA + scan_result_addr = self.dut_scanner_.scan_for_name(advertising_name) + assertThat(scan_result_addr).isNotNone() + assertThat(scan_result_addr).isNotEqualTo(cert_public_address) + + # Bond + logging.info("Bonding with %s", scan_result_addr) + self.dut_security_.create_bond_numeric_comparison(scan_result_addr) + self.dut_scanner_.stop_scanning() + + # Start advertising again and scan for identity address + scan_result_addr = self.dut_scanner_.scan_for_address(cert_public_address, ble_address_types["public"]) + assertThat(scan_result_addr).isNotNone() + assertThat(scan_result_addr).isNotEqualTo(cert_public_address) + + # Teardown advertiser and scanner + self.dut_scanner_.stop_scanning() + self.cert_advertiser_.stop_advertising() diff --git a/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py b/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py index e9cd9d375e2..7b2b933b649 100644 --- a/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py +++ b/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py @@ -18,6 +18,7 @@ from blueberry.tests.sl4a_sl4a.advertising.le_advertising import LeAdvertisingTe from blueberry.tests.sl4a_sl4a.gatt.gatt_connect_test import GattConnectTest from blueberry.tests.sl4a_sl4a.gatt.gatt_connect_with_irk_test import GattConnectWithIrkTest from blueberry.tests.sl4a_sl4a.gatt.gatt_notify_test import GattNotifyTest +from blueberry.tests.sl4a_sl4a.scanning.le_scanning import LeScanningTest from blueberry.tests.sl4a_sl4a.security.irk_rotation_test import IrkRotationTest from blueberry.tests.sl4a_sl4a.security.oob_pairing_test import OobPairingTest @@ -30,6 +31,7 @@ ALL_TESTS = [ GattNotifyTest, IrkRotationTest, LeAdvertisingTest, + LeScanningTest, OobPairingTest, ] -- GitLab From 76a718d2dc723071fe564fb4a1b93982dc7f2871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Mon, 3 Oct 2022 13:07:36 +0000 Subject: [PATCH 018/667] leaudio: Fix start stream after reconfiguration When group is in Configured state, ASE can be either activated (if reconfiguration was in process) or inactivated if remote gets into Configured state autonomusly after stream is stopped. This should be taken into account when starting stream for the configured group. Bug: 250460929 Test: atest BluetoothInstrumentationTests Test: atest --host bluetooth_le_audio_test Tag: #feature Merged-In: I65e7aa6ae71e06378d70b6883e94886329fd2a32 Change-Id: I65e7aa6ae71e06378d70b6883e94886329fd2a32 (cherry picked from commit 1485ae956f4b833addc440471d54dd331ce9e346) --- system/bta/le_audio/devices.cc | 3 +- system/bta/le_audio/state_machine_test.cc | 149 ++++++++++++++++++++++ 2 files changed, 150 insertions(+), 2 deletions(-) diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc index c2e3ed83b4e..05fb2c1f6d6 100644 --- a/system/bta/le_audio/devices.cc +++ b/system/bta/le_audio/devices.cc @@ -2435,8 +2435,7 @@ bool LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) { LOG_INFO(" Configuring device %s", address_.ToString().c_str()); for (auto& ase : ases_) { - if (!ase.active && - ase.state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED && + if (ase.state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED && ase.configured_for_context_type == context_type) { LOG_INFO( " conn_id: %d, ase id %d, cis id %d, cis_handle 0x%04x is activated.", diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc index 1e8f8632ce0..56cb8cc4cd0 100644 --- a/system/bta/le_audio/state_machine_test.cc +++ b/system/bta/le_audio/state_machine_test.cc @@ -2775,5 +2775,154 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) { ASSERT_NE(std::find(ccids->begin(), ccids->end(), media_ccid), ccids->end()); } +TEST_F(StateMachineTest, StartStreamAfterConfigure) { + const auto context_type = kContextTypeMedia; + const auto leaudio_group_id = 6; + const auto num_devices = 2; + + ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid); + + // Prepare multiple fake connected devices in a group + auto* group = + PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); + ASSERT_EQ(group->Size(), num_devices); + + PrepareConfigureCodecHandler(group, 0, true); + PrepareConfigureQosHandler(group); + PrepareEnableHandler(group); + PrepareDisableHandler(group); + PrepareReleaseHandler(group); + + InjectInitialIdleNotification(group); + + auto* leAudioDevice = group->GetFirstDevice(); + auto expected_devices_written = 0; + while (leAudioDevice) { + /* Three Writes: + * 1. Codec configure + * 2: Codec QoS + * 3: Enabling + */ + EXPECT_CALL(gatt_queue, + WriteCharacteristic(leAudioDevice->conn_id_, + leAudioDevice->ctp_hdls_.val_hdl, _, + GATT_WRITE_NO_RSP, _, _)) + .Times(3); + expected_devices_written++; + leAudioDevice = group->GetNextDevice(leAudioDevice); + } + ASSERT_EQ(expected_devices_written, num_devices); + + // Validate GroupStreamStatus + EXPECT_CALL(mock_callbacks_, + StatusReportCb( + leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::CONFIGURED_BY_USER)); + + // Start the configuration and stream Media content + group->SetPendingConfiguration(); + LeAudioGroupStateMachine::Get()->ConfigureStream( + group, static_cast(context_type), + context_type); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); + + group->ClearPendingConfiguration(); + // Validate GroupStreamStatus + EXPECT_CALL( + mock_callbacks_, + StatusReportCb(leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::STREAMING)); + + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->StartStream( + group, static_cast(context_type), + context_type); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); +} + +TEST_F(StateMachineTest, StartStreamCachedConfig) { + const auto context_type = kContextTypeMedia; + const auto leaudio_group_id = 6; + const auto num_devices = 2; + + ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid); + + // Prepare multiple fake connected devices in a group + auto* group = + PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); + ASSERT_EQ(group->Size(), num_devices); + + PrepareConfigureCodecHandler(group, 0, true); + PrepareConfigureQosHandler(group); + PrepareEnableHandler(group); + PrepareDisableHandler(group); + PrepareReleaseHandler(group); + + InjectInitialIdleNotification(group); + + auto* leAudioDevice = group->GetFirstDevice(); + auto expected_devices_written = 0; + while (leAudioDevice) { + /* Three Writes: + * 1: Codec config + * 2: Codec QoS (+1 after restart) + * 3: Enabling (+1 after restart) + * 4: Release (1) + */ + EXPECT_CALL(gatt_queue, + WriteCharacteristic(leAudioDevice->conn_id_, + leAudioDevice->ctp_hdls_.val_hdl, _, + GATT_WRITE_NO_RSP, _, _)) + .Times(6); + expected_devices_written++; + leAudioDevice = group->GetNextDevice(leAudioDevice); + } + ASSERT_EQ(expected_devices_written, num_devices); + + // Validate GroupStreamStatus + EXPECT_CALL( + mock_callbacks_, + StatusReportCb(leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::STREAMING)); + + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->StartStream( + group, static_cast(context_type), + context_type); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); + + // Validate GroupStreamStatus + EXPECT_CALL( + mock_callbacks_, + StatusReportCb(leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::RELEASING)); + + EXPECT_CALL( + mock_callbacks_, + StatusReportCb( + leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS)); + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->StopStream(group); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); + + // Restart stream + EXPECT_CALL( + mock_callbacks_, + StatusReportCb(leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::STREAMING)); + + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->StartStream( + group, static_cast(context_type), + context_type); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); +} + } // namespace internal } // namespace le_audio -- GitLab From 57d4dc3e02b6b4e8a06f96880fabc9e9df525f59 Mon Sep 17 00:00:00 2001 From: Rahul Arya Date: Tue, 13 Sep 2022 17:50:52 +0000 Subject: [PATCH 019/667] Rotate advertising set addresses on IRK change We need to pause all advertising before changing the random address, and we need to also change the addresses used for each advertising set. We also should ensure no active connections etc. are taking place when the address is changed. Note that this means there will be some latency between the address change in legacy before it is changed in GD, but I don't know how to avoid this. Ignore-AOSP-First: Security fix Test: manual / TODO Bug: 245453591 Bug: 195410559 Tag: #stability BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines Change-Id: Ia897a55320741993467d286550949a802657d399 (cherry picked from commit b275c66d9403b1ab090037e73d2b5dc437fa973f) Merged-In: Ia897a55320741993467d286550949a802657d399 --- system/gd/hci/le_address_manager.cc | 80 +++++++++++++++++-------- system/gd/hci/le_address_manager.h | 23 ++++++- system/gd/hci/le_advertising_manager.cc | 54 ++++++++++++----- 3 files changed, 115 insertions(+), 42 deletions(-) diff --git a/system/gd/hci/le_address_manager.cc b/system/gd/hci/le_address_manager.cc index 1dde2f4b679..f05050df4ea 100644 --- a/system/gd/hci/le_address_manager.cc +++ b/system/gd/hci/le_address_manager.cc @@ -15,6 +15,7 @@ */ #include "hci/le_address_manager.h" + #include "common/init_flags.h" #include "os/log.h" #include "os/rand.h" @@ -43,7 +44,7 @@ LeAddressManager::~LeAddressManager() { } } -// Aborts if called more than once +// Called on initialization, and on IRK rotation void LeAddressManager::SetPrivacyPolicyForInitiatorAddress( AddressPolicy address_policy, AddressWithType fixed_address, @@ -51,15 +52,15 @@ void LeAddressManager::SetPrivacyPolicyForInitiatorAddress( bool supports_ble_privacy, std::chrono::milliseconds minimum_rotation_time, std::chrono::milliseconds maximum_rotation_time) { - // Handle repeated calls to the function + // Handle repeated calls to the function for IRK rotation if (address_policy_ != AddressPolicy::POLICY_NOT_SET) { // Need to update some parameteres like IRK if privacy is supported if (supports_ble_privacy) { LOG_INFO("Updating rotation parameters."); - rotation_irk_ = rotation_irk; - minimum_rotation_time_ = minimum_rotation_time; - maximum_rotation_time_ = maximum_rotation_time; - set_random_address(); + handler_->CallOn( + this, + &LeAddressManager::prepare_to_update_irk, + UpdateIRKCommand{rotation_irk, minimum_rotation_time, maximum_rotation_time}); } return; } @@ -276,7 +277,7 @@ void LeAddressManager::ack_resume(LeAddressManagerCallback* callback) { } void LeAddressManager::prepare_to_rotate() { - Command command = {CommandType::ROTATE_RANDOM_ADDRESS, nullptr}; + Command command = {CommandType::ROTATE_RANDOM_ADDRESS, RotateRandomAddressCommand{}}; cached_commands_.push(std::move(command)); pause_registered_clients(); } @@ -316,6 +317,26 @@ void LeAddressManager::rotate_random_address() { set_random_address(); } +void LeAddressManager::prepare_to_update_irk(UpdateIRKCommand update_irk_command) { + Command command = {CommandType::UPDATE_IRK, update_irk_command}; + cached_commands_.push(std::move(command)); + if (registered_clients_.empty()) { + handle_next_command(); + } else { + pause_registered_clients(); + } +} + +void LeAddressManager::update_irk(UpdateIRKCommand command) { + rotation_irk_ = command.rotation_irk; + minimum_rotation_time_ = command.minimum_rotation_time; + maximum_rotation_time_ = command.maximum_rotation_time; + set_random_address(); + for (auto& client : registered_clients_) { + client.first->NotifyOnIRKChange(); + } +} + /* This function generates Resolvable Private Address (RPA) from Identity * Resolving Key |irk| and |prand|*/ hci::Address LeAddressManager::generate_rpa() { @@ -396,17 +417,26 @@ void LeAddressManager::handle_next_command() { auto command = std::move(cached_commands_.front()); cached_commands_.pop(); - if (command.command_type == CommandType::ROTATE_RANDOM_ADDRESS) { - rotate_random_address(); - } else { - enqueue_command_.Run(std::move(command.command_packet)); - } + std::visit( + [this](auto&& command) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + update_irk(command); + } else if constexpr (std::is_same_v) { + rotate_random_address(); + } else if constexpr (std::is_same_v) { + enqueue_command_.Run(std::move(command.command)); + } else { + static_assert(!sizeof(T*), "non-exhaustive visitor!"); + } + }, + command.contents); } void LeAddressManager::AddDeviceToFilterAcceptList( FilterAcceptListAddressType connect_list_address_type, bluetooth::hci::Address address) { auto packet_builder = hci::LeAddDeviceToFilterAcceptListBuilder::Create(connect_list_address_type, address); - Command command = {CommandType::ADD_DEVICE_TO_CONNECT_LIST, std::move(packet_builder)}; + Command command = {CommandType::ADD_DEVICE_TO_CONNECT_LIST, HCICommand{std::move(packet_builder)}}; handler_->BindOnceOn(this, &LeAddressManager::push_command, std::move(command)).Invoke(); } @@ -417,24 +447,24 @@ void LeAddressManager::AddDeviceToResolvingList( const std::array& local_irk) { // Disable Address resolution auto disable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::DISABLED); - Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(disable_builder)}; + Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, HCICommand{std::move(disable_builder)}}; cached_commands_.push(std::move(disable)); auto packet_builder = hci::LeAddDeviceToResolvingListBuilder::Create( peer_identity_address_type, peer_identity_address, peer_irk, local_irk); - Command command = {CommandType::ADD_DEVICE_TO_RESOLVING_LIST, std::move(packet_builder)}; + Command command = {CommandType::ADD_DEVICE_TO_RESOLVING_LIST, HCICommand{std::move(packet_builder)}}; cached_commands_.push(std::move(command)); if (supports_ble_privacy_) { auto packet_builder = hci::LeSetPrivacyModeBuilder::Create(peer_identity_address_type, peer_identity_address, PrivacyMode::DEVICE); - Command command = {CommandType::LE_SET_PRIVACY_MODE, std::move(packet_builder)}; + Command command = {CommandType::LE_SET_PRIVACY_MODE, HCICommand{std::move(packet_builder)}}; cached_commands_.push(std::move(command)); } // Enable Address resolution auto enable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::ENABLED); - Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(enable_builder)}; + Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, HCICommand{std::move(enable_builder)}}; cached_commands_.push(std::move(enable)); if (registered_clients_.empty()) { @@ -447,7 +477,7 @@ void LeAddressManager::AddDeviceToResolvingList( void LeAddressManager::RemoveDeviceFromFilterAcceptList( FilterAcceptListAddressType connect_list_address_type, bluetooth::hci::Address address) { auto packet_builder = hci::LeRemoveDeviceFromFilterAcceptListBuilder::Create(connect_list_address_type, address); - Command command = {CommandType::REMOVE_DEVICE_FROM_CONNECT_LIST, std::move(packet_builder)}; + Command command = {CommandType::REMOVE_DEVICE_FROM_CONNECT_LIST, HCICommand{std::move(packet_builder)}}; handler_->BindOnceOn(this, &LeAddressManager::push_command, std::move(command)).Invoke(); } @@ -455,17 +485,17 @@ void LeAddressManager::RemoveDeviceFromResolvingList( PeerAddressType peer_identity_address_type, Address peer_identity_address) { // Disable Address resolution auto disable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::DISABLED); - Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(disable_builder)}; + Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, HCICommand{std::move(disable_builder)}}; cached_commands_.push(std::move(disable)); auto packet_builder = hci::LeRemoveDeviceFromResolvingListBuilder::Create(peer_identity_address_type, peer_identity_address); - Command command = {CommandType::REMOVE_DEVICE_FROM_RESOLVING_LIST, std::move(packet_builder)}; + Command command = {CommandType::REMOVE_DEVICE_FROM_RESOLVING_LIST, HCICommand{std::move(packet_builder)}}; cached_commands_.push(std::move(command)); // Enable Address resolution auto enable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::ENABLED); - Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(enable_builder)}; + Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, HCICommand{std::move(enable_builder)}}; cached_commands_.push(std::move(enable)); if (registered_clients_.empty()) { @@ -477,23 +507,23 @@ void LeAddressManager::RemoveDeviceFromResolvingList( void LeAddressManager::ClearFilterAcceptList() { auto packet_builder = hci::LeClearFilterAcceptListBuilder::Create(); - Command command = {CommandType::CLEAR_CONNECT_LIST, std::move(packet_builder)}; + Command command = {CommandType::CLEAR_CONNECT_LIST, HCICommand{std::move(packet_builder)}}; handler_->BindOnceOn(this, &LeAddressManager::push_command, std::move(command)).Invoke(); } void LeAddressManager::ClearResolvingList() { // Disable Address resolution auto disable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::DISABLED); - Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(disable_builder)}; + Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, HCICommand{std::move(disable_builder)}}; cached_commands_.push(std::move(disable)); auto packet_builder = hci::LeClearResolvingListBuilder::Create(); - Command command = {CommandType::CLEAR_RESOLVING_LIST, std::move(packet_builder)}; + Command command = {CommandType::CLEAR_RESOLVING_LIST, HCICommand{std::move(packet_builder)}}; cached_commands_.push(std::move(command)); // Enable Address resolution auto enable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::ENABLED); - Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(enable_builder)}; + Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, HCICommand{std::move(enable_builder)}}; cached_commands_.push(std::move(enable)); handler_->BindOnceOn(this, &LeAddressManager::pause_registered_clients).Invoke(); diff --git a/system/gd/hci/le_address_manager.h b/system/gd/hci/le_address_manager.h index 159bf772c5b..dcc7b0d4cb6 100644 --- a/system/gd/hci/le_address_manager.h +++ b/system/gd/hci/le_address_manager.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "common/callback.h" #include "hci/address_with_type.h" @@ -34,6 +35,7 @@ class LeAddressManagerCallback { virtual ~LeAddressManagerCallback() = default; virtual void OnPause() = 0; virtual void OnResume() = 0; + virtual void NotifyOnIRKChange(){}; }; class LeAddressManager { @@ -111,12 +113,25 @@ class LeAddressManager { REMOVE_DEVICE_FROM_RESOLVING_LIST, CLEAR_RESOLVING_LIST, SET_ADDRESS_RESOLUTION_ENABLE, - LE_SET_PRIVACY_MODE + LE_SET_PRIVACY_MODE, + UPDATE_IRK, + }; + + struct RotateRandomAddressCommand {}; + + struct UpdateIRKCommand { + crypto_toolbox::Octet16 rotation_irk; + std::chrono::milliseconds minimum_rotation_time; + std::chrono::milliseconds maximum_rotation_time; + }; + + struct HCICommand { + std::unique_ptr command; }; struct Command { - CommandType command_type; - std::unique_ptr command_packet; + CommandType command_type; // Note that this field is only intended for logging, not control flow + std::variant contents; }; void pause_registered_clients(); @@ -130,6 +145,8 @@ class LeAddressManager { void rotate_random_address(); void schedule_rotate_random_address(); void set_random_address(); + void prepare_to_update_irk(UpdateIRKCommand command); + void update_irk(UpdateIRKCommand command); hci::Address generate_rpa(); hci::Address generate_nrpa(); void handle_next_command(); diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc index 0832c257908..4f744ed179b 100644 --- a/system/gd/hci/le_advertising_manager.cc +++ b/system/gd/hci/le_advertising_manager.cc @@ -393,7 +393,7 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb // start timer for random address advertising_sets_[id].address_rotation_alarm = std::make_unique(module_handler_); advertising_sets_[id].address_rotation_alarm->Schedule( - common::BindOnce(&impl::set_advertising_set_random_address, common::Unretained(this), id), + common::BindOnce(&impl::set_advertising_set_random_address_on_timer, common::Unretained(this), id), le_address_manager_->GetNextPrivateAddressIntervalMs()); } else { advertising_sets_[id].current_address = le_address_manager_->GetCurrentAddress(); @@ -473,8 +473,21 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb enabled_sets_[advertiser_id].advertising_handle_ = kInvalidHandle; } - void set_advertising_set_random_address(AdvertiserId advertiser_id) { - // This function should only be trigger by enabled advertising set + void rotate_advertiser_address(AdvertiserId advertiser_id) { + if (advertising_api_type_ == AdvertisingApiType::EXTENDED) { + AddressWithType address_with_type = le_address_manager_->GetAnotherAddress(); + le_advertising_interface_->EnqueueCommand( + hci::LeSetExtendedAdvertisingRandomAddressBuilder::Create(advertiser_id, address_with_type.GetAddress()), + module_handler_->BindOnceOn( + this, + &impl::on_set_advertising_set_random_address_complete, + advertiser_id, + address_with_type)); + } + } + + void set_advertising_set_random_address_on_timer(AdvertiserId advertiser_id) { + // This function should only be trigger by enabled advertising set or IRK rotation if (enabled_sets_[advertiser_id].advertising_handle_ == kInvalidHandle) { if (advertising_sets_[advertiser_id].address_rotation_alarm != nullptr) { advertising_sets_[advertiser_id].address_rotation_alarm->Cancel(); @@ -497,23 +510,20 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb module_handler_->BindOnce(impl::check_status)); } - AddressWithType address_with_type = le_address_manager_->GetAnotherAddress(); - le_advertising_interface_->EnqueueCommand( - hci::LeSetExtendedAdvertisingRandomAddressBuilder::Create(advertiser_id, address_with_type.GetAddress()), - module_handler_->BindOnceOn( - this, - &impl::on_set_advertising_set_random_address_complete, - advertiser_id, - address_with_type)); + rotate_advertiser_address(advertiser_id); - if (advertising_sets_[advertiser_id].connectable) { + // If we are paused, we will be enabled in OnResume(), so don't resume now. + // Note that OnResume() can never re-enable us while we are changing our address, since the + // DISABLED and ENABLED commands are enqueued synchronously, so OnResume() doesn't need an + // analogous check. + if (advertising_sets_[advertiser_id].connectable && !paused) { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets), module_handler_->BindOnce(impl::check_status)); } advertising_sets_[advertiser_id].address_rotation_alarm->Schedule( - common::BindOnce(&impl::set_advertising_set_random_address, common::Unretained(this), advertiser_id), + common::BindOnce(&impl::set_advertising_set_random_address_on_timer, common::Unretained(this), advertiser_id), le_address_manager_->GetNextPrivateAddressIntervalMs()); } @@ -1007,6 +1017,22 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb le_address_manager_->AckResume(this); } + // Note: this needs to be synchronous (i.e. NOT on a handler) for two reasons: + // 1. For parity with OnPause() and OnResume() + // 2. If we don't enqueue our HCI commands SYNCHRONOUSLY, then it is possible that we OnResume() in addressManager + // before our commands complete. So then our commands reach the HCI layer *after* the resume commands from address + // manager, which is racey (even if it might not matter). + // + // If you are a future developer making this asynchronous, you need to add some kind of ->AckIRKChange() method to the + // address manager so we can defer resumption to after this completes. + void NotifyOnIRKChange() override { + for (size_t i = 0; i < enabled_sets_.size(); i++) { + if (enabled_sets_[i].advertising_handle_ != kInvalidHandle) { + rotate_advertiser_address(i); + } + } + } + common::Callback scan_callback_; common::ContextualCallback set_terminated_callback_{}; AdvertisingCallback* advertising_callbacks_ = nullptr; @@ -1171,7 +1197,7 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb auto complete_view = LeSetExtendedAdvertisingRandomAddressCompleteView::Create(view); ASSERT(complete_view.IsValid()); if (complete_view.GetStatus() != ErrorCode::SUCCESS) { - LOG_INFO("Got a command complete with status %s", ErrorCodeText(complete_view.GetStatus()).c_str()); + LOG_ERROR("Got a command complete with status %s", ErrorCodeText(complete_view.GetStatus()).c_str()); } else { LOG_INFO( "update random address for advertising set %d : %s", -- GitLab From acffc03f14b37144a5687525440effc8c6dd1b93 Mon Sep 17 00:00:00 2001 From: Rahul Arya Date: Wed, 14 Sep 2022 19:02:37 +0000 Subject: [PATCH 020/667] Reschedule address rotation after connection to advertising set Otherwise we stop rotating our address and lose all benefits of privacy. Ignore-AOSP-First: Security fix Bug: 246692649 Bug: 195410559 Test: Nearby QA test, and I'll write a cert test eventually Tag: #stability Change-Id: Ib63d79256acbb956413849c17f761aa902fa2eae (cherry picked from commit 279e3c3a8656808582e3ebcf81d8a936c320498d) Merged-In: Ib63d79256acbb956413849c17f761aa902fa2eae --- system/gd/hci/le_advertising_manager.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc index 4f744ed179b..eca691b0317 100644 --- a/system/gd/hci/le_advertising_manager.cc +++ b/system/gd/hci/le_advertising_manager.cc @@ -182,7 +182,9 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb uint8_t advertiser_id = event_view.GetAdvertisingHandle(); + bool was_rotating_address = false; if (advertising_sets_[advertiser_id].address_rotation_alarm != nullptr) { + was_rotating_address = true; advertising_sets_[advertiser_id].address_rotation_alarm->Cancel(); advertising_sets_[advertiser_id].address_rotation_alarm.reset(); } @@ -209,6 +211,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb if (advertising_sets_[advertiser_id].duration == 0 && advertising_sets_[advertiser_id].max_extended_advertising_events == 0) { LOG_INFO("Reenable advertising"); + if (was_rotating_address) { + advertising_sets_[advertiser_id].address_rotation_alarm = std::make_unique(module_handler_); + advertising_sets_[advertiser_id].address_rotation_alarm->Schedule( + common::BindOnce( + &impl::set_advertising_set_random_address_on_timer, common::Unretained(this), advertiser_id), + le_address_manager_->GetNextPrivateAddressIntervalMs()); + } enable_advertiser(advertiser_id, true, 0, 0); } } -- GitLab From 031dc503f11bcd3c20f86696fdfe19f9413642e8 Mon Sep 17 00:00:00 2001 From: Rahul Arya Date: Mon, 26 Sep 2022 23:02:06 +0000 Subject: [PATCH 021/667] Add flag to toggle IRK rotation Based on discussion in linked bug, to prevent further regressions if the latest fixes are still incomplete. Bug: 195410559 Test: compiles Ignore-AOSP-First: security Change-Id: I1741929c639f2dbc6417974bf3287d8d141e33df (cherry picked from commit 924c3a96379e0ab86a907079c307de3de2f06a56) Merged-In: I1741929c639f2dbc6417974bf3287d8d141e33df --- system/btif/src/btif_storage.cc | 4 +++- system/gd/rust/common/src/init_flags.rs | 3 ++- system/gd/rust/shim/src/init_flags.rs | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/system/btif/src/btif_storage.cc b/system/btif/src/btif_storage.cc index bd7e89c8b5e..41fa4df5fbe 100644 --- a/system/btif/src/btif_storage.cc +++ b/system/btif/src/btif_storage.cc @@ -54,6 +54,7 @@ #include "btif_hh.h" #include "btif_util.h" #include "device/include/controller.h" +#include "gd/common/init_flags.h" #include "osi/include/allocator.h" #include "osi/include/compat.h" #include "osi/include/config.h" @@ -899,7 +900,8 @@ bt_status_t btif_storage_remove_bonded_device( /* Check the length of the paired devices, and if 0 then reset IRK */ auto paired_devices = btif_config_get_paired_devices(); - if (paired_devices.empty()) { + if (paired_devices.empty() && + bluetooth::common::init_flags::irk_rotation_is_enabled()) { LOG_INFO("Last paired device removed, resetting IRK"); BTA_DmBleResetId(); } diff --git a/system/gd/rust/common/src/init_flags.rs b/system/gd/rust/common/src/init_flags.rs index 80fe6da399b..52425a83687 100644 --- a/system/gd/rust/common/src/init_flags.rs +++ b/system/gd/rust/common/src/init_flags.rs @@ -80,7 +80,8 @@ init_flags!( gatt_robust_caching, btaa_hci, gd_rust, - gd_link_policy + gd_link_policy, + irk_rotation }, dependencies: { gd_core => gd_security diff --git a/system/gd/rust/shim/src/init_flags.rs b/system/gd/rust/shim/src/init_flags.rs index fd3015cc341..d1cbfbf0688 100644 --- a/system/gd/rust/shim/src/init_flags.rs +++ b/system/gd/rust/shim/src/init_flags.rs @@ -11,6 +11,7 @@ mod ffi { fn btaa_hci_is_enabled() -> bool; fn gd_rust_is_enabled() -> bool; fn gd_link_policy_is_enabled() -> bool; + fn irk_rotation_is_enabled() -> bool; } } -- GitLab From 9e001f2f11d93f598e46e584e76ea692490883e1 Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Tue, 4 Oct 2022 19:45:38 +0800 Subject: [PATCH 022/667] Add broadcast support on ISO dump tool Support Broadcast stream audio data dump from hci log Bug: 229346816 Test: python3 ./dump_le_audio.py BTSNOOP.cfa -v --header Change-Id: Ia8e535daa8033adef37d57fc1da0c50752b81dd1 Merged-In: Ia8e535daa8033adef37d57fc1da0c50752b81dd1 (cherry picked from commit 9c3630f66be14e536a469826757e117fd58da8e3) --- system/tools/scripts/dump_le_audio.py | 344 ++++++++++++++++++++++---- 1 file changed, 297 insertions(+), 47 deletions(-) diff --git a/system/tools/scripts/dump_le_audio.py b/system/tools/scripts/dump_le_audio.py index 806cdfb3576..048facbfa87 100755 --- a/system/tools/scripts/dump_le_audio.py +++ b/system/tools/scripts/dump_le_audio.py @@ -79,6 +79,13 @@ OPCODE_RELEASE = 0x08 # opcode for hci command OPCODE_HCI_CREATE_CIS = 0x2064 OPCODE_REMOVE_ISO_DATA_PATH = 0x206F +OPCODE_LE_SET_PERIODIC_ADVERTISING_DATA = 0x203F +OPCODE_LE_CREATE_BIG = 0x2068 +OPCODE_LE_SETUP_ISO_DATA_PATH = 0x206E + +# HCI event +EVENT_CODE_LE_META_EVENT = 0x3E +SUBEVENT_CODE_LE_CREATE_BIG_COMPLETE = 0x1B TYPE_STREAMING_AUDIO_CONTEXTS = 0x02 @@ -114,6 +121,9 @@ AUDIO_LOCATION_LEFT = 0x01 AUDIO_LOCATION_RIGHT = 0x02 AUDIO_LOCATION_CENTER = 0x04 +AD_TYPE_SERVICE_DATA_16_BIT = 0x16 +BASIC_AUDIO_ANNOUNCEMENT_SERVICE = 0x1851 + packet_number = 0 debug_enable = False add_header = False @@ -158,35 +168,77 @@ class AseStream: print("octets_per_frame: " + str(self.octets_per_frame)) +class Broadcast: + + def __init__(self): + self.num_of_bis = defaultdict(int) # subgroup - num_of_bis + self.bis = defaultdict(BisStream) # bis_index - codec_config + self.bis_index_handle_map = defaultdict(int) # bis_index - bis_handle + self.bis_index_list = [] + + def dump(self): + for bis_index, iso_stream in self.bis.items(): + print("bis_index: " + str(bis_index) + " bis handle: " + str(self.bis_index_handle_map[bis_index])) + iso_stream.dump() + + +class BisStream: + + def __init__(self): + self.sampling_frequencies = 0xFF + self.frame_duration = 0xFF + self.channel_allocation = 0xFFFFFFFF + self.octets_per_frame = 0xFFFF + self.output_dump = [] + self.start_time = 0xFFFFFFFF + + def dump(self): + print("start_time: " + str(self.start_time)) + print("sampling_frequencies: " + str(self.sampling_frequencies)) + print("frame_duration: " + str(self.frame_duration)) + print("channel_allocation: " + str(self.channel_allocation)) + print("octets_per_frame: " + str(self.octets_per_frame)) + + connection_map = defaultdict(Connection) cis_acl_map = defaultdict(int) +broadcast_map = defaultdict(Broadcast) +big_adv_map = defaultdict(int) +bis_stream_map = defaultdict(BisStream) + + +def generate_header(file, stream, is_cis): + sf_case = { + SAMPLE_FREQUENCY_8000: 80, + SAMPLE_FREQUENCY_11025: 110, + SAMPLE_FREQUENCY_16000: 160, + SAMPLE_FREQUENCY_22050: 220, + SAMPLE_FREQUENCY_24000: 240, + SAMPLE_FREQUENCY_32000: 320, + SAMPLE_FREQUENCY_44100: 441, + SAMPLE_FREQUENCY_48000: 480, + SAMPLE_FREQUENCY_88200: 882, + SAMPLE_FREQUENCY_96000: 960, + SAMPLE_FREQUENCY_176400: 1764, + SAMPLE_FREQUENCY_192000: 1920, + SAMPLE_FREQUENCY_384000: 2840, + } + fd_case = {FRAME_DURATION_7_5: 7.5, FRAME_DURATION_10: 10} + al_case = {AUDIO_LOCATION_MONO: 1, AUDIO_LOCATION_LEFT: 1, AUDIO_LOCATION_RIGHT: 1, AUDIO_LOCATION_CENTER: 2} - -def generate_header(file, connection): header = bytearray.fromhex('1ccc1200') - for ase in connection.ase.values(): - sf_case = { - SAMPLE_FREQUENCY_8000: 80, - SAMPLE_FREQUENCY_11025: 110, - SAMPLE_FREQUENCY_16000: 160, - SAMPLE_FREQUENCY_22050: 220, - SAMPLE_FREQUENCY_24000: 240, - SAMPLE_FREQUENCY_32000: 320, - SAMPLE_FREQUENCY_44100: 441, - SAMPLE_FREQUENCY_48000: 480, - SAMPLE_FREQUENCY_88200: 882, - SAMPLE_FREQUENCY_96000: 960, - SAMPLE_FREQUENCY_176400: 1764, - SAMPLE_FREQUENCY_192000: 1920, - SAMPLE_FREQUENCY_384000: 2840, - } - header = header + struct.pack(" 0: + config_length, packet = unpack_data(packet, 1, False) + config_type, packet = unpack_data(packet, 1, False) + value, packet = unpack_data(packet, config_length - 1, False) + if config_type == TYPE_SAMPLING_FREQUENCIES: + sampling_frequencies = value + elif config_type == TYPE_FRAME_DURATION: + frame_duration = value + elif config_type == TYPE_OCTETS_PER_FRAME: + octets_per_frame = value + else: + print("Unknown config type") + length -= (config_length + 1) + + # Ignore metadata + metadata_length, packet = unpack_data(packet, 1, False) + packet = unpack_data(packet, metadata_length, True) + + for count in range(num_of_bis): + bis_index, packet = unpack_data(packet, 1, False) + broadcast_map[adv_handle].bis_index_list.append(bis_index) + length, packet = unpack_data(packet, 1, False) + if len(packet) < length: + print("Invalid level 3 codec information length") + return + + while length > 0: + config_length, packet = unpack_data(packet, 1, False) + config_type, packet = unpack_data(packet, 1, False) + value, packet = unpack_data(packet, config_length - 1, False) + if config_type == TYPE_CHANNEL_ALLOCATION: + channel_allocation = value + else: + print("Ignored config type") + length -= (config_length + 1) + + broadcast_map[adv_handle].bis[bis_index].sampling_frequencies = sampling_frequencies + broadcast_map[adv_handle].bis[bis_index].frame_duration = frame_duration + broadcast_map[adv_handle].bis[bis_index].octets_per_frame = octets_per_frame + broadcast_map[adv_handle].bis[bis_index].channel_allocation = channel_allocation + + return packet + + def debug_print(log): global packet_number print("#" + str(packet_number) + ": " + log) @@ -303,7 +413,7 @@ def unpack_data(data, byte, ignore): return value, data[byte:] -def parse_command_packet(packet): +def parse_command_packet(packet, timestamp): opcode, packet = unpack_data(packet, 2, False) if opcode == OPCODE_HCI_CREATE_CIS: debug_print("OPCODE_HCI_CREATE_CIS") @@ -330,9 +440,96 @@ def parse_command_packet(packet): debug_print("Invalid cmd length") return - cis_handle, packet = unpack_data(packet, 2, False) - acl_handle = cis_acl_map[cis_handle] - dump_audio_data_to_file(acl_handle) + iso_handle, packet = unpack_data(packet, 2, False) + # CIS stream + if iso_handle in cis_acl_map: + acl_handle = cis_acl_map[iso_handle] + dump_cis_audio_data_to_file(acl_handle) + # To Do: BIS stream + elif iso_handle in bis_stream_map: + dump_bis_audio_data_to_file(iso_handle) + elif opcode == OPCODE_LE_SET_PERIODIC_ADVERTISING_DATA: + debug_print("OPCODE_LE_SET_PERIODIC_ADVERTISING_DATA") + + length, packet = unpack_data(packet, 1, False) + if length != len(packet): + debug_print("Invalid cmd length") + return + + if length < 21: + debug_print("Ignored. Not basic audio announcement") + return + + adv_hdl, packet = unpack_data(packet, 1, False) + #ignore operation, advertising_data_length + packet = unpack_data(packet, 2, True) + length, packet = unpack_data(packet, 1, False) + if length != len(packet): + debug_print("Invalid AD element length") + return + + ad_type, packet = unpack_data(packet, 1, False) + service, packet = unpack_data(packet, 2, False) + if ad_type != AD_TYPE_SERVICE_DATA_16_BIT or service != BASIC_AUDIO_ANNOUNCEMENT_SERVICE: + debug_print("Ignored. Not basic audio announcement") + return + + packet = parse_big_codec_information(adv_hdl, packet) + elif opcode == OPCODE_LE_CREATE_BIG: + debug_print("OPCODE_LE_CREATE_BIG") + + length, packet = unpack_data(packet, 1, False) + if length != len(packet) and length < 31: + debug_print("Invalid Create BIG command length") + return + + big_handle, packet = unpack_data(packet, 1, False) + adv_handle, packet = unpack_data(packet, 1, False) + big_adv_map[big_handle] = adv_handle + elif opcode == OPCODE_LE_SETUP_ISO_DATA_PATH: + debug_print("OPCODE_LE_SETUP_ISO_DATA_PATH") + length, packet = unpack_data(packet, 1, False) + if len(packet) != length: + debug_print("Invalid LE SETUP ISO DATA PATH command length") + return + + iso_handle, packet = unpack_data(packet, 2, False) + if iso_handle in bis_stream_map: + bis_stream_map[iso_handle].start_time = timestamp + + +def parse_event_packet(packet): + event_code, packet = unpack_data(packet, 1, False) + if event_code != EVENT_CODE_LE_META_EVENT: + return + + length, packet = unpack_data(packet, 1, False) + if len(packet) != length: + print("Invalid LE mata event length") + return + + subevent_code, packet = unpack_data(packet, 1, False) + if subevent_code != SUBEVENT_CODE_LE_CREATE_BIG_COMPLETE: + return + + status, packet = unpack_data(packet, 1, False) + if status != 0x00: + debug_print("Create_BIG failed") + return + + big_handle, packet = unpack_data(packet, 1, False) + if big_handle not in big_adv_map: + print("Invalid BIG handle") + return + adv_handle = big_adv_map[big_handle] + # Ignore, we don't care these parameter + packet = unpack_data(packet, 15, True) + num_of_bis, packet = unpack_data(packet, 1, False) + for count in range(num_of_bis): + bis_handle, packet = unpack_data(packet, 2, False) + bis_index = broadcast_map[adv_handle].bis_index_list[count] + broadcast_map[adv_handle].bis_index_handle_map[bis_index] = bis_handle + bis_stream_map[bis_handle] = broadcast_map[adv_handle].bis[bis_index] def convert_time_str(timestamp): @@ -348,7 +545,7 @@ def convert_time_str(timestamp): return full_str_format -def dump_audio_data_to_file(acl_handle): +def dump_cis_audio_data_to_file(acl_handle): if debug_enable: connection_map[acl_handle].dump() file_name = "" @@ -389,20 +586,20 @@ def dump_audio_data_to_file(acl_handle): break if connection_map[acl_handle].input_dump != []: - debug_print("Dump input...") + debug_print("Dump unicast input...") f = open(file_name + "_input.bin", 'wb') if add_header == True: - generate_header(f, connection_map[acl_handle]) + generate_header(f, connection_map[acl_handle], True) arr = bytearray(connection_map[acl_handle].input_dump) f.write(arr) f.close() connection_map[acl_handle].input_dump = [] if connection_map[acl_handle].output_dump != []: - debug_print("Dump output...") + debug_print("Dump unicast output...") f = open(file_name + "_output.bin", 'wb') if add_header == True: - generate_header(f, connection_map[acl_handle]) + generate_header(f, connection_map[acl_handle], True) arr = bytearray(connection_map[acl_handle].output_dump) f.write(arr) f.close() @@ -411,6 +608,51 @@ def dump_audio_data_to_file(acl_handle): return +def dump_bis_audio_data_to_file(iso_handle): + if debug_enable: + bis_stream_map[iso_handle].dump() + file_name = "broadcast" + sf_case = { + SAMPLE_FREQUENCY_8000: "8000", + SAMPLE_FREQUENCY_11025: "11025", + SAMPLE_FREQUENCY_16000: "16000", + SAMPLE_FREQUENCY_22050: "22050", + SAMPLE_FREQUENCY_24000: "24000", + SAMPLE_FREQUENCY_32000: "32000", + SAMPLE_FREQUENCY_44100: "44100", + SAMPLE_FREQUENCY_48000: "48000", + SAMPLE_FREQUENCY_88200: "88200", + SAMPLE_FREQUENCY_96000: "96000", + SAMPLE_FREQUENCY_176400: "176400", + SAMPLE_FREQUENCY_192000: "192000", + SAMPLE_FREQUENCY_384000: "284000" + } + file_name += ("_sf" + sf_case[bis_stream_map[iso_handle].sampling_frequencies]) + fd_case = {FRAME_DURATION_7_5: "7_5", FRAME_DURATION_10: "10"} + file_name += ("_fd" + fd_case[bis_stream_map[iso_handle].frame_duration]) + al_case = { + AUDIO_LOCATION_MONO: "mono", + AUDIO_LOCATION_LEFT: "left", + AUDIO_LOCATION_RIGHT: "right", + AUDIO_LOCATION_CENTER: "center" + } + file_name += ("_" + al_case[bis_stream_map[iso_handle].channel_allocation]) + file_name += ("_frame" + str(bis_stream_map[iso_handle].octets_per_frame)) + file_name += ("_" + convert_time_str(bis_stream_map[iso_handle].start_time)) + + if bis_stream_map[iso_handle].output_dump != []: + debug_print("Dump broadcast output...") + f = open(file_name + "_output.bin", 'wb') + if add_header == True: + generate_header(f, bis_stream_map[iso_handle], False) + arr = bytearray(bis_stream_map[iso_handle].output_dump) + f.write(arr) + f.close() + bis_stream_map[iso_handle].output_dump = [] + + return + + def parse_acl_packet(packet, flags, timestamp): # Check the minimum acl length, HCI leader (4 bytes) # + L2CAP header (4 bytes) @@ -441,8 +683,8 @@ def parse_acl_packet(packet, flags, timestamp): def parse_iso_packet(packet, flags): - cis_handle, packet = unpack_data(packet, 2, False) - cis_handle &= 0x0EFF + iso_handle, packet = unpack_data(packet, 2, False) + iso_handle &= 0x0EFF iso_data_load_length, packet = unpack_data(packet, 2, False) if iso_data_load_length != len(packet): debug_print("Invalid iso data load length") @@ -457,13 +699,18 @@ def parse_iso_packet(packet, flags): debug_print("Invalid iso sdu length") return - acl_handle = cis_acl_map[cis_handle] - if flags == SENT: - connection_map[acl_handle].output_dump.extend(struct.pack(" Date: Wed, 28 Sep 2022 20:54:29 +0000 Subject: [PATCH 023/667] Pass IRK rotation flag into native Ignore-AOSP-First: Security fix Test: Flashed build, set the prop and watched the stack restart Bug: 195410559 Change-Id: I6496b34c144e697308109d5ee9528f4ae5c9b4b4 Merged-In: I6496b34c144e697308109d5ee9528f4ae5c9b4b4 --- .../src/com/android/bluetooth/btservice/AdapterService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index b627e07afcc..9e007aae60a 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -5080,6 +5080,7 @@ public class AdapterService extends Service { private static final String GD_RUST_FLAG = "INIT_gd_rust"; private static final String GD_LINK_POLICY_FLAG = "INIT_gd_link_policy"; private static final String GATT_ROBUST_CACHING_FLAG = "INIT_gatt_robust_caching"; + private static final String IRK_ROTATION_FLAG = "INIT_irk_rotation"; /** * Logging flags logic (only applies to DEBUG and VERBOSE levels): @@ -5136,6 +5137,9 @@ public class AdapterService extends Service { GATT_ROBUST_CACHING_FLAG, false)) { initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_FLAG, "true")); } + if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, IRK_ROTATION_FLAG, false)) { + initFlags.add(String.format("%s=%s", IRK_ROTATION_FLAG, "true")); + } if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, false)) { initFlags.add(String.format("%s=%s", LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, "true")); -- GitLab From 9f5c4bc934c972df872be301524176b92bd8e573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Tue, 4 Oct 2022 13:37:41 +0000 Subject: [PATCH 024/667] bta_gattc_cache: Use GATT caching for devices with LMP version 5.1 This is a workaround for devices which are on the market and does not like Androids Database hash read. With this patch, Android requires LMP version 5.1 to read Database hash. Bug: 241324007 Test: Manual testes with devices with different LMP version Test: atest BluetoothInstrumentationTests Tag: #feature Merged-In: If48656d7eac5daeb1d7befce921270c1911b45e2 Change-Id: If48656d7eac5daeb1d7befce921270c1911b45e2 (cherry picked from commit bf2260c4aa2ba42dc02f5286957419dcd48f9178) --- system/bta/gatt/bta_gattc_act.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/system/bta/gatt/bta_gattc_act.cc b/system/bta/gatt/bta_gattc_act.cc index 9de15c9422c..ecb9a872598 100644 --- a/system/bta/gatt/bta_gattc_act.cc +++ b/system/bta/gatt/bta_gattc_act.cc @@ -768,6 +768,26 @@ void bta_gattc_start_discover(tBTA_GATTC_CLCB* p_clcb, p_clcb->p_srcb->update_count = 0; p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC_ACT; + /* This is workaround for the embedded devices being already on the market + * and having a serious problem with handle Read By Type with + * GATT_UUID_DATABASE_HASH. With this workaround, Android will assume that + * embedded device having LMP version lower than 5.1 (0x0a), it does not + * support GATT Caching. + */ + uint8_t lmp_version = 0; + if (!BTM_ReadRemoteVersion(p_clcb->bda, &lmp_version, nullptr, nullptr)) { + LOG_WARN("Could not read remote version for %s", + p_clcb->bda.ToString().c_str()); + } + + if (lmp_version < 0x0a) { + LOG_WARN( + " Device LMP version 0x%02x < Bluetooth 5.1. Ignore database cache " + "read.", + lmp_version); + p_clcb->p_srcb->srvc_hdl_db_hash = false; + } + /* read db hash if db hash characteristic exists */ if (bta_gattc_is_robust_caching_enabled() && p_clcb->p_srcb->srvc_hdl_db_hash && -- GitLab From dae4c2fd683172c57a4509089a686ec96ed0c7b6 Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Mon, 26 Sep 2022 02:21:09 +0000 Subject: [PATCH 025/667] Add BluetoothMapAccountItemTest Bug: 237467631 Test: atest BluetoothMapAccountItemTest Change-Id: I1a1c0cfc0ba88b76510550cf427dcc699250ea45 (cherry picked from commit f6f342b32dafab227795c6f83289fab531a7a5bb) --- .../map/BluetoothMapAccountItem.java | 2 +- .../map/BluetoothMapAccountItemTest.java | 112 ++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapAccountItemTest.java diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapAccountItem.java b/android/app/src/com/android/bluetooth/map/BluetoothMapAccountItem.java index cb9481bd747..f36cf3e7777 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapAccountItem.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapAccountItem.java @@ -41,7 +41,7 @@ public class BluetoothMapAccountItem implements Comparable Date: Mon, 26 Sep 2022 11:50:29 +0000 Subject: [PATCH 026/667] Add BluetoothMapConvoContactElementTest Bug: 237467631 Test: atest BluetoothMapConvoContactElementTest Change-Id: I231fdac347ade7f0d4908f148c0407151a7c2b51 (cherry picked from commit ef0bba6355da9aa982bd6404f622c6b7faa5a323) --- .../BluetoothMapConvoContactElementTest.java | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoContactElementTest.java diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoContactElementTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoContactElementTest.java new file mode 100644 index 00000000000..4e6a144e90e --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoContactElementTest.java @@ -0,0 +1,203 @@ +/* + * 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. + */ + +package com.android.bluetooth.map; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.SignedLongLong; +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; +import org.xmlpull.v1.XmlSerializer; + +import java.io.StringReader; +import java.io.StringWriter; +import java.text.SimpleDateFormat; + +@RunWith(AndroidJUnit4.class) +public class BluetoothMapConvoContactElementTest { + private static final String TEST_UCI = "test_bt_uci"; + private static final String TEST_NAME = "test_name"; + private static final String TEST_DISPLAY_NAME = "test_display_name"; + private static final String TEST_PRESENCE_STATUS = "test_presence_status"; + private static final int TEST_PRESENCE_AVAILABILITY = 2; + private static final long TEST_LAST_ACTIVITY = 1; + private static final int TEST_CHAT_STATE = 2; + private static final int TEST_PRIORITY = 1; + private static final String TEST_BT_UID = "1111"; + + private final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); + + @Mock + private MapContact mMapContact; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void constructorWithArguments() { + BluetoothMapConvoContactElement contactElement = + new BluetoothMapConvoContactElement(TEST_UCI, TEST_NAME, TEST_DISPLAY_NAME, + TEST_PRESENCE_STATUS, TEST_PRESENCE_AVAILABILITY, TEST_LAST_ACTIVITY, + TEST_CHAT_STATE, TEST_PRIORITY, TEST_BT_UID); + + assertThat(contactElement.getContactId()).isEqualTo(TEST_UCI); + assertThat(contactElement.getName()).isEqualTo(TEST_NAME); + assertThat(contactElement.getDisplayName()).isEqualTo(TEST_DISPLAY_NAME); + assertThat(contactElement.getPresenceStatus()).isEqualTo(TEST_PRESENCE_STATUS); + assertThat(contactElement.getPresenceAvailability()).isEqualTo(TEST_PRESENCE_AVAILABILITY); + assertThat(contactElement.getLastActivityString()).isEqualTo( + format.format(TEST_LAST_ACTIVITY)); + assertThat(contactElement.getChatState()).isEqualTo(TEST_CHAT_STATE); + assertThat(contactElement.getPriority()).isEqualTo(TEST_PRIORITY); + assertThat(contactElement.getBtUid()).isEqualTo(TEST_BT_UID); + } + + @Test + public void createFromMapContact() { + final long id = 1111; + final SignedLongLong signedLongLong = new SignedLongLong(id, 0); + when(mMapContact.getId()).thenReturn(id); + when(mMapContact.getName()).thenReturn(TEST_DISPLAY_NAME); + BluetoothMapConvoContactElement contactElement = + BluetoothMapConvoContactElement.createFromMapContact(mMapContact, TEST_UCI); + assertThat(contactElement.getContactId()).isEqualTo(TEST_UCI); + assertThat(contactElement.getBtUid()).isEqualTo(signedLongLong.toHexString()); + assertThat(contactElement.getDisplayName()).isEqualTo(TEST_DISPLAY_NAME); + } + + @Test + public void settersAndGetters() throws Exception { + BluetoothMapConvoContactElement contactElement = new BluetoothMapConvoContactElement(); + contactElement.setDisplayName(TEST_DISPLAY_NAME); + contactElement.setPresenceStatus(TEST_PRESENCE_STATUS); + contactElement.setPresenceAvailability(TEST_PRESENCE_AVAILABILITY); + contactElement.setPriority(TEST_PRIORITY); + contactElement.setName(TEST_NAME); + contactElement.setBtUid(SignedLongLong.fromString(TEST_BT_UID)); + contactElement.setChatState(TEST_CHAT_STATE); + contactElement.setLastActivity(TEST_LAST_ACTIVITY); + contactElement.setContactId(TEST_UCI); + + assertThat(contactElement.getContactId()).isEqualTo(TEST_UCI); + assertThat(contactElement.getName()).isEqualTo(TEST_NAME); + assertThat(contactElement.getDisplayName()).isEqualTo(TEST_DISPLAY_NAME); + assertThat(contactElement.getPresenceStatus()).isEqualTo(TEST_PRESENCE_STATUS); + assertThat(contactElement.getPresenceAvailability()).isEqualTo(TEST_PRESENCE_AVAILABILITY); + assertThat(contactElement.getLastActivityString()).isEqualTo( + format.format(TEST_LAST_ACTIVITY)); + assertThat(contactElement.getChatState()).isEqualTo(TEST_CHAT_STATE); + assertThat(contactElement.getPriority()).isEqualTo(TEST_PRIORITY); + assertThat(contactElement.getBtUid()).isEqualTo(TEST_BT_UID); + } + + @Test + public void encodeToXml_thenDecodeToInstance_returnsCorrectly() throws Exception { + BluetoothMapConvoContactElement contactElement = new + BluetoothMapConvoContactElement(TEST_UCI, TEST_NAME, TEST_DISPLAY_NAME, + TEST_PRESENCE_STATUS, TEST_PRESENCE_AVAILABILITY, TEST_LAST_ACTIVITY, + TEST_CHAT_STATE, TEST_PRIORITY, TEST_BT_UID); + + final XmlSerializer serializer = new FastXmlSerializer(); + final StringWriter writer = new StringWriter(); + + serializer.setOutput(writer); + serializer.startDocument("UTF-8", true); + contactElement.encode(serializer); + serializer.endDocument(); + + final XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance(); + parserFactory.setNamespaceAware(true); + final XmlPullParser parser; + parser = parserFactory.newPullParser(); + + parser.setInput(new StringReader(writer.toString())); + parser.next(); + + BluetoothMapConvoContactElement contactElementFromXml = + BluetoothMapConvoContactElement.createFromXml(parser); + + assertThat(contactElementFromXml.getContactId()).isEqualTo(TEST_UCI); + assertThat(contactElementFromXml.getName()).isEqualTo(TEST_NAME); + assertThat(contactElementFromXml.getDisplayName()).isEqualTo(TEST_DISPLAY_NAME); + assertThat(contactElementFromXml.getPresenceStatus()).isEqualTo(TEST_PRESENCE_STATUS); + assertThat(contactElementFromXml.getPresenceAvailability()).isEqualTo( + TEST_PRESENCE_AVAILABILITY); + assertThat(contactElementFromXml.getLastActivityString()).isEqualTo( + format.format(TEST_LAST_ACTIVITY)); + assertThat(contactElementFromXml.getChatState()).isEqualTo(TEST_CHAT_STATE); + assertThat(contactElementFromXml.getPriority()).isEqualTo(TEST_PRIORITY); + assertThat(contactElementFromXml.getBtUid()).isEqualTo(TEST_BT_UID); + } + + @Test + public void equalsWithSameValues_returnsTrue() { + BluetoothMapConvoContactElement contactElement = + new BluetoothMapConvoContactElement(TEST_UCI, TEST_NAME, TEST_DISPLAY_NAME, + TEST_PRESENCE_STATUS, TEST_PRESENCE_AVAILABILITY, TEST_LAST_ACTIVITY, + TEST_CHAT_STATE, TEST_PRIORITY, TEST_BT_UID); + + BluetoothMapConvoContactElement contactElementEqual = + new BluetoothMapConvoContactElement(TEST_UCI, TEST_NAME, TEST_DISPLAY_NAME, + TEST_PRESENCE_STATUS, TEST_PRESENCE_AVAILABILITY, TEST_LAST_ACTIVITY, + TEST_CHAT_STATE, TEST_PRIORITY, TEST_BT_UID); + + assertThat(contactElement).isEqualTo(contactElementEqual); + } + + @Test + public void equalsWithDifferentPriority_returnsFalse() { + BluetoothMapConvoContactElement contactElement = + new BluetoothMapConvoContactElement(TEST_UCI, TEST_NAME, TEST_DISPLAY_NAME, + TEST_PRESENCE_STATUS, TEST_PRESENCE_AVAILABILITY, TEST_LAST_ACTIVITY, + TEST_CHAT_STATE, TEST_PRIORITY, TEST_BT_UID); + + BluetoothMapConvoContactElement contactElementWithDifferentPriority = + new BluetoothMapConvoContactElement(TEST_UCI, TEST_NAME, TEST_DISPLAY_NAME, + TEST_PRESENCE_STATUS, TEST_PRESENCE_AVAILABILITY, TEST_LAST_ACTIVITY, + TEST_CHAT_STATE, /*priority=*/0, TEST_BT_UID); + + assertThat(contactElement).isNotEqualTo(contactElementWithDifferentPriority); + } + + @Test + public void compareTo_withSameValues_returnsZero() { + BluetoothMapConvoContactElement contactElement = + new BluetoothMapConvoContactElement(TEST_UCI, TEST_NAME, TEST_DISPLAY_NAME, + TEST_PRESENCE_STATUS, TEST_PRESENCE_AVAILABILITY, TEST_LAST_ACTIVITY, + TEST_CHAT_STATE, TEST_PRIORITY, TEST_BT_UID); + + BluetoothMapConvoContactElement contactElementSameLastActivity = + new BluetoothMapConvoContactElement(TEST_UCI, TEST_NAME, TEST_DISPLAY_NAME, + TEST_PRESENCE_STATUS, TEST_PRESENCE_AVAILABILITY, TEST_LAST_ACTIVITY, + TEST_CHAT_STATE, TEST_PRIORITY, TEST_BT_UID); + + assertThat(contactElement.compareTo(contactElementSameLastActivity)).isEqualTo(0); + } +} \ No newline at end of file -- GitLab From c799f263e27c37c443f63787b3f37ead57015fd5 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Fri, 30 Sep 2022 08:17:54 +0000 Subject: [PATCH 027/667] VolumeControl: Do not set the same volume level twice Devices may connect or be discovered as a new group memeber after the streaming was already started, or services can be connected in a different order each time. The newly connected device may need additional volume level corrections. Some of these volume changes may already be pending on the native layer due to previous group API calls, while Java has not yet been notified about all the native stack events. This change eliminates any additional volume state changes if the volume is already set to the desired level but the Java layer did not know that, or when the volume changing GATT operation with same parameters is already on the operation queue. Bug: 248969553 Bug: 248915809 Tag: #feature Test: atest --host bluetooth_vc_test --no-bazel-mode Change-Id: Ic7ad61a8f8be6a48bb09aa3b07059e8f1abbd528 Merged-In: Ic7ad61a8f8be6a48bb09aa3b07059e8f1abbd528 (cherry picked from commit dcb0043016d667f57d6360408c4b9889e7af7bc4) --- system/bta/vc/vc.cc | 55 +++++++++++++++++++++++++--------------- system/bta/vc/vc_test.cc | 33 +++++++++++++++++++++--- 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc index 403b93d936a..1d406a973a2 100644 --- a/system/bta/vc/vc.cc +++ b/system/bta/vc/vc.cc @@ -743,13 +743,23 @@ class VolumeControlImpl : public VolumeControl { int group_id, bool is_autonomous, uint8_t opcode, std::vector& arguments) { - DLOG(INFO) << __func__ << " num of devices: " << devices.size() - << " group_id: " << group_id - << " is_autonomous: " << is_autonomous << " opcode: " << +opcode - << " arg size: " << arguments.size(); - - ongoing_operations_.emplace_back(latest_operation_id_++, group_id, - is_autonomous, opcode, arguments, devices); + LOG_DEBUG( + "num of devices: %zu, group_id: %d, is_autonomous: %s opcode: %d, arg " + "size: %zu", + devices.size(), group_id, is_autonomous ? "true" : "false", +opcode, + arguments.size()); + + if (std::find_if(ongoing_operations_.begin(), ongoing_operations_.end(), + [opcode, &arguments](const VolumeOperation& op) { + return (op.opcode_ == opcode) && + std::equal(op.arguments_.begin(), + op.arguments_.end(), + arguments.begin()); + }) == ongoing_operations_.end()) { + ongoing_operations_.emplace_back(latest_operation_id_++, group_id, + is_autonomous, opcode, arguments, + devices); + } } void MuteUnmute(std::variant addr_or_group_id, bool mute) { @@ -760,11 +770,13 @@ class VolumeControlImpl : public VolumeControl { if (std::holds_alternative(addr_or_group_id)) { LOG_DEBUG("Address: %s: ", (std::get(addr_or_group_id)).ToString().c_str()); - std::vector devices = { - std::get(addr_or_group_id)}; - - PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown, - false, opcode, arg); + VolumeControlDevice* dev = volume_control_devices_.FindByAddress( + std::get(addr_or_group_id)); + if (dev && dev->IsConnected()) { + std::vector devices = {dev->address}; + PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown, + false, opcode, arg); + } } else { /* Handle group change */ auto group_id = std::get(addr_or_group_id); @@ -815,14 +827,17 @@ class VolumeControlImpl : public VolumeControl { uint8_t opcode = kControlPointOpcodeSetAbsoluteVolume; if (std::holds_alternative(addr_or_group_id)) { - DLOG(INFO) << __func__ << " " << std::get(addr_or_group_id); - std::vector devices = { - std::get(addr_or_group_id)}; - - RemovePendingVolumeControlOperations(devices, - bluetooth::groups::kGroupUnknown); - PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown, - false, opcode, arg); + LOG_DEBUG("Address: %s: ", + std::get(addr_or_group_id).ToString().c_str()); + VolumeControlDevice* dev = volume_control_devices_.FindByAddress( + std::get(addr_or_group_id)); + if (dev && dev->IsConnected() && (dev->volume != volume)) { + std::vector devices = {dev->address}; + RemovePendingVolumeControlOperations(devices, + bluetooth::groups::kGroupUnknown); + PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown, + false, opcode, arg); + } } else { /* Handle group change */ auto group_id = std::get(addr_or_group_id); diff --git a/system/bta/vc/vc_test.cc b/system/bta/vc/vc_test.cc index bdc93e1f40c..71fca365c88 100644 --- a/system/bta/vc/vc_test.cc +++ b/system/bta/vc/vc_test.cc @@ -915,10 +915,37 @@ class VolumeControlValueSetTest : public VolumeControlTest { }; TEST_F(VolumeControlValueSetTest, test_set_volume) { - std::vector expected_data({0x04, 0x00, 0x10}); - EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, expected_data, - GATT_WRITE, _, _)); + ON_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, _, GATT_WRITE, _, _)) + .WillByDefault([this](uint16_t conn_id, uint16_t handle, + std::vector value, + tGATT_WRITE_TYPE write_type, GATT_WRITE_OP_CB cb, + void* cb_data) { + std::vector ntf_value({ + value[2], // volume level + 0, // muted + static_cast(value[1] + 1), // change counter + }); + GetNotificationEvent(0x0021, ntf_value); + }); + + const std::vector vol_x10({0x04, 0x00, 0x10}); + EXPECT_CALL(gatt_queue, + WriteCharacteristic(conn_id, 0x0024, vol_x10, GATT_WRITE, _, _)) + .Times(1); + VolumeControl::Get()->SetVolume(test_address, 0x10); + + // Same volume level should not be applied twice + const std::vector vol_x10_2({0x04, 0x01, 0x10}); + EXPECT_CALL(gatt_queue, + WriteCharacteristic(conn_id, 0x0024, vol_x10_2, GATT_WRITE, _, _)) + .Times(0); VolumeControl::Get()->SetVolume(test_address, 0x10); + + const std::vector vol_x20({0x04, 0x01, 0x20}); + EXPECT_CALL(gatt_queue, + WriteCharacteristic(conn_id, 0x0024, vol_x20, GATT_WRITE, _, _)) + .Times(1); + VolumeControl::Get()->SetVolume(test_address, 0x20); } TEST_F(VolumeControlValueSetTest, test_mute) { -- GitLab From 292a44faad074c81e1cf37e7ad364b01e01ee013 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Fri, 30 Sep 2022 12:06:33 +0000 Subject: [PATCH 028/667] VolumeControl: Set proper volume on joining device When one of the devices connects to an already streaming group it needs to have it volume level corrected to the already cached group volume level. Additionally on the initial connection when the second device is not yet recognised by CSIS as a group member but the Volume Control Service has already been connected and group volume was already set by the LeAudioService, the newly added group member also needs its volume level to be corrected to the already set group volume level. Bug: 248969553 Bug: 248915809 Tag: #feature Test: atest BluetoothInstrumentationTests Change-Id: I02e1827c4f652f78ab86365b7b9e72ef0c1c77c2 Merged-In: I02e1827c4f652f78ab86365b7b9e72ef0c1c77c2 (cherry picked from commit 6fd6f1ae93668ce4fa7bf1fa51bbed866b2a9b53) --- .../csip/CsipSetCoordinatorService.java | 18 ++++ .../bluetooth/le_audio/LeAudioService.java | 7 ++ .../bluetooth/vc/VolumeControlService.java | 39 +++++++- .../vc/VolumeControlServiceTest.java | 96 +++++++++++++++++++ 4 files changed, 159 insertions(+), 1 deletion(-) diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java index f85dbbc4bff..172383e2f2a 100644 --- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java +++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java @@ -597,6 +597,24 @@ public class CsipSetCoordinatorService extends ProfileService { .collect(Collectors.toList()); } + /** + * Get group ID for a given device and UUID + * @param device potential group member + * @param uuid profile context UUID + * @return group ID + */ + public Integer getGroupId(BluetoothDevice device, ParcelUuid uuid) { + Map device_groups = + mDeviceGroupIdRankMap.getOrDefault(device, new HashMap<>()); + return mGroupIdToUuidMap.entrySet() + .stream() + .filter(e -> (device_groups.containsKey(e.getKey()) + && e.getValue().equals(uuid))) + .map(Map.Entry::getKey) + .findFirst() + .orElse(IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID); + } + /** * Get device's groups/ * @param device group member device diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java index 3b4de0d7211..ed9776881be 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -1933,6 +1933,13 @@ public class LeAudioService extends ProfileService { } private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) { + if (mVolumeControlService == null) { + mVolumeControlService = mServiceFactory.getVolumeControlService(); + } + if (mVolumeControlService != null) { + mVolumeControlService.handleGroupNodeAdded(groupId, device); + } + if (mLeAudioCallbacks != null) { int n = mLeAudioCallbacks.beginBroadcast(); for (int i = 0; i < n; i++) { diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java index e5cd625cb05..d1e5fd0fe62 100644 --- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java +++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java @@ -26,6 +26,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.bluetooth.BluetoothVolumeControl; +import android.bluetooth.IBluetoothCsipSetCoordinator; import android.bluetooth.IBluetoothLeAudio; import android.bluetooth.IBluetoothVolumeControl; import android.bluetooth.IBluetoothVolumeControlCallback; @@ -47,6 +48,7 @@ import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; +import com.android.bluetooth.csip.CsipSetCoordinatorService; import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.SynchronousResultReceiver; @@ -193,7 +195,8 @@ public class VolumeControlService extends ProfileService { private BroadcastReceiver mBondStateChangedReceiver; private BroadcastReceiver mConnectionStateChangedReceiver; - private final ServiceFactory mFactory = new ServiceFactory(); + @VisibleForTesting + ServiceFactory mFactory = new ServiceFactory(); public static boolean isEnabled() { return BluetoothProperties.isProfileVcpControllerEnabled().orElse(false); @@ -632,6 +635,29 @@ public class VolumeControlService extends ProfileService { mVolumeControlNativeInterface.unmuteGroup(groupId); } + /** + * {@hide} + */ + public void handleGroupNodeAdded(int groupId, BluetoothDevice device) { + // Ignore disconnected device, its volume will be set once it connects + synchronized (mStateMachines) { + VolumeControlStateMachine sm = mStateMachines.get(device); + if (sm == null) { + return; + } + if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { + return; + } + } + + // If group volume has already changed, the new group member should set it + Integer groupVolume = mGroupVolumeCache.getOrDefault(groupId, + IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME); + if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { + mVolumeControlNativeInterface.setVolume(device, groupVolume); + } + } + void handleVolumeControlChanged(BluetoothDevice device, int groupId, int volume, boolean mute, boolean isAutonomous) { @@ -974,6 +1000,17 @@ public class VolumeControlService extends ProfileService { } removeStateMachine(device); } + } else if (toState == BluetoothProfile.STATE_CONNECTED) { + // Restore the group volume if it was changed while the device was not yet connected. + CsipSetCoordinatorService csipClient = mFactory.getCsipSetCoordinatorService(); + Integer groupId = csipClient.getGroupId(device, BluetoothUuid.CAP); + if (groupId != IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID) { + Integer groupVolume = mGroupVolumeCache.getOrDefault(groupId, + IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME); + if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { + mVolumeControlNativeInterface.setVolume(device, groupVolume); + } + } } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java index 7ca76237923..bdd81699bba 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java @@ -41,7 +41,9 @@ import androidx.test.rule.ServiceTestRule; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.btservice.AdapterService; +import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; +import com.android.bluetooth.csip.CsipSetCoordinatorService; import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import com.android.bluetooth.x.com.android.modules.utils.SynchronousResultReceiver; @@ -73,6 +75,7 @@ public class VolumeControlServiceTest { private VolumeControlService mService; private VolumeControlService.BluetoothVolumeControlBinder mServiceBinder; private BluetoothDevice mDevice; + private BluetoothDevice mDeviceTwo; private HashMap> mDeviceQueueMap; private static final int TIMEOUT_MS = 1000; private static final int BT_LE_AUDIO_MAX_VOL = 255; @@ -87,6 +90,8 @@ public class VolumeControlServiceTest { @Mock private DatabaseManager mDatabaseManager; @Mock private VolumeControlNativeInterface mNativeInterface; @Mock private AudioManager mAudioManager; + @Mock private ServiceFactory mServiceFactory; + @Mock private CsipSetCoordinatorService mCsipService; @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule(); @@ -119,9 +124,12 @@ public class VolumeControlServiceTest { startService(); mService.mVolumeControlNativeInterface = mNativeInterface; mService.mAudioManager = mAudioManager; + mService.mFactory = mServiceFactory; mServiceBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder(); mServiceBinder.mIsTesting = true; + doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService(); + // Override the timeout value to speed up the test VolumeControlStateMachine.sConnectTimeoutMs = TIMEOUT_MS; // 1s @@ -134,8 +142,10 @@ public class VolumeControlServiceTest { // Get a device for testing mDevice = TestUtils.getTestDevice(mAdapter, 0); + mDeviceTwo = TestUtils.getTestDevice(mAdapter, 1); mDeviceQueueMap = new HashMap<>(); mDeviceQueueMap.put(mDevice, new LinkedBlockingQueue<>()); + mDeviceQueueMap.put(mDeviceTwo, new LinkedBlockingQueue<>()); doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService) .getBondState(any(BluetoothDevice.class)); doReturn(new ParcelUuid[]{BluetoothUuid.VOLUME_CONTROL}).when(mAdapterService) @@ -631,6 +641,92 @@ public class VolumeControlServiceTest { Assert.assertEquals(volume, mService.getGroupVolume(groupId)); } + /** + * Test setting volume for a group member who connects after the volume level + * for a group was already changed and cached. + */ + @Test + public void testLateConnectingDevice() throws Exception { + int groupId = 1; + int groupVolume = 56; + + // Both devices are in the same group + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); + + // Update the device policy so okToConnect() returns true + when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); + when(mDatabaseManager + .getProfileConnectionPolicy(any(BluetoothDevice.class), + eq(BluetoothProfile.VOLUME_CONTROL))) + .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); + doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); + doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); + + generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_DISCONNECTED); + Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, + mService.getConnectionState(mDevice)); + Assert.assertTrue(mService.getDevices().contains(mDevice)); + + mService.setGroupVolume(groupId, groupVolume); + verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume)); + verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume)); + + // Verify that second device gets the proper group volume level when connected + generateConnectionMessageFromNative(mDeviceTwo, BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_DISCONNECTED); + Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, + mService.getConnectionState(mDeviceTwo)); + Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); + verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume)); + } + + /** + * Test setting volume for a new group member who is discovered after the volume level + * for a group was already changed and cached. + */ + @Test + public void testLateDiscoveredGroupMember() throws Exception { + int groupId = 1; + int groupVolume = 56; + + // For now only one device is in the group + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1); + + // Update the device policy so okToConnect() returns true + when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); + when(mDatabaseManager + .getProfileConnectionPolicy(any(BluetoothDevice.class), + eq(BluetoothProfile.VOLUME_CONTROL))) + .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); + doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); + doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); + + generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_DISCONNECTED); + Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, + mService.getConnectionState(mDevice)); + Assert.assertTrue(mService.getDevices().contains(mDevice)); + + // Set the group volume + mService.setGroupVolume(groupId, groupVolume); + + // Verify that second device will not get the group volume level if it is not a group member + generateConnectionMessageFromNative(mDeviceTwo, BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_DISCONNECTED); + Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, + mService.getConnectionState(mDeviceTwo)); + Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); + verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume)); + + // But gets the volume when it becomes the group member + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); + mService.handleGroupNodeAdded(groupId, mDeviceTwo); + verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume)); + } + @Test public void testServiceBinderGetDevicesMatchingConnectionStates() throws Exception { final SynchronousResultReceiver> recv = -- GitLab From 52a366a085e5b8868a5aaba4dedc7e014e56f95c Mon Sep 17 00:00:00 2001 From: Chienyuan Date: Wed, 5 Oct 2022 15:20:03 +0800 Subject: [PATCH 029/667] Read APCF extended features Bug: 247032118 Test: gd/cert/run Test: ./bluetooth_test_gd_unit64 --gtest_filter=*ScanningManagerTest* --gtest_repeat=1000 Tag: #refactor BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines Change-Id: Id956c8e0d37fad934033dc462f393b050d19d9fb Merged-In: Id956c8e0d37fad934033dc462f393b050d19d9fb --- system/bta/csis/csis_client.cc | 11 +- system/gd/hci/hci_packets.pdl | 17 ++ system/gd/hci/le_scanning_manager.cc | 164 +++++++++++------- system/gd/hci/le_scanning_manager.h | 2 + system/gd/hci/le_scanning_manager_test.cc | 58 +++++-- system/main/shim/le_scanning_manager.cc | 4 + system/main/shim/le_scanning_manager.h | 1 + .../mock_main_shim_le_scanning_manager.cc | 5 + 8 files changed, 180 insertions(+), 82 deletions(-) diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc index 2e1d0d5d1fc..08771b4dd24 100644 --- a/system/bta/csis/csis_client.cc +++ b/system/bta/csis/csis_client.cc @@ -1169,9 +1169,16 @@ class CsisClientImpl : public CsisClient { } void CsisActiveObserverSet(bool enable) { - LOG(INFO) << __func__ << " CSIS Discovery SET: " << enable; + bool is_ad_type_filter_supported = + bluetooth::shim::is_ad_type_filter_supported(); + LOG_INFO("CSIS Discovery SET: %d, is_ad_type_filter_supported: %d", enable, + is_ad_type_filter_supported); + if (is_ad_type_filter_supported) { + bluetooth::shim::set_ad_type_rsi_filter(enable); + } else { + bluetooth::shim::set_empty_filter(enable); + } - bluetooth::shim::set_empty_filter(enable); BTA_DmBleCsisObserve( enable, [](tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* p_data) { /* If there's no instance we are most likely shutting diff --git a/system/gd/hci/hci_packets.pdl b/system/gd/hci/hci_packets.pdl index 34f8885d63a..2b0defbcce5 100644 --- a/system/gd/hci/hci_packets.pdl +++ b/system/gd/hci/hci_packets.pdl @@ -4840,6 +4840,7 @@ enum ApcfOpcode : 8 { MANUFACTURER_DATA = 0x06, SERVICE_DATA = 0x07, AD_TYPE = 0x08, + READ_EXTENDED_FEATURES = 0xFF, } // https://source.android.com/devices/bluetooth/hci_requirements#advertising-packet-content-filter @@ -5007,6 +5008,22 @@ packet LeAdvFilterADTypeComplete : LeAdvFilterComplete (apcf_opcode = AD_TYPE) { apcf_available_spaces : 8, } +packet LeAdvFilterReadExtendedFeatures : LeAdvFilter (apcf_opcode = READ_EXTENDED_FEATURES) { +} + +test LeAdvFilterReadExtendedFeatures { + "\x57\xfd\x01\xff", +} + +packet LeAdvFilterReadExtendedFeaturesComplete : LeAdvFilterComplete (apcf_opcode = READ_EXTENDED_FEATURES) { + ad_type_filter : 1, + _reserved_ : 15, +} + +test LeAdvFilterReadExtendedFeaturesComplete { + "\x0e\x07\x01\x57\xfd\x00\xff\x01\x00", +} + packet LeEnergyInfo : VendorCommand (op_code = LE_ENERGY_INFO) { } diff --git a/system/gd/hci/le_scanning_manager.cc b/system/gd/hci/le_scanning_manager.cc index 97c73661371..a4e267ea236 100644 --- a/system/gd/hci/le_scanning_manager.cc +++ b/system/gd/hci/le_scanning_manager.cc @@ -136,7 +136,7 @@ class AdvertisingCache { }; class NullScanningCallback : public ScanningCallback { - void OnScannerRegistered(const bluetooth::hci::Uuid app_uuid, ScannerId scanner_id, ScanningStatus status) override { + void OnScannerRegistered(const Uuid app_uuid, ScannerId scanner_id, ScanningStatus status) override { LOG_INFO("OnScannerRegistered in NullScanningCallback"); } void OnSetScannerParameterComplete(ScannerId scanner_id, ScanningStatus status) override { @@ -222,7 +222,7 @@ struct BatchScanConfig { ScannerId ref_value; }; -struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback { +struct LeScanningManager::impl : public LeAddressManagerCallback { impl(Module* module) : module_(module), le_scanning_interface_(nullptr) {} ~impl() { @@ -233,10 +233,10 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback void start( os::Handler* handler, - hci::HciLayer* hci_layer, - hci::Controller* controller, - hci::AclManager* acl_manager, - hci::VendorSpecificEventManager* vendor_specific_event_manager, + HciLayer* hci_layer, + Controller* controller, + AclManager* acl_manager, + VendorSpecificEventManager* vendor_specific_event_manager, storage::StorageModule* storage_module) { module_handler_ = handler; hci_layer_ = hci_layer; @@ -259,12 +259,17 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } else { api_type_ = ScanApiType::LEGACY; } - is_filter_support_ = controller_->IsSupported(OpCode::LE_ADV_FILTER); - is_batch_scan_support_ = controller->IsSupported(OpCode::LE_BATCH_SCAN); - is_periodic_advertising_sync_transfer_sender_support_ = + is_filter_supported_ = controller_->IsSupported(OpCode::LE_ADV_FILTER); + if (is_filter_supported_) { + le_scanning_interface_->EnqueueCommand( + LeAdvFilterReadExtendedFeaturesBuilder::Create(), + module_handler_->BindOnceOn(this, &impl::on_apcf_read_extended_features_complete)); + } + is_batch_scan_supported_ = controller->IsSupported(OpCode::LE_BATCH_SCAN); + is_periodic_advertising_sync_transfer_sender_supported_ = controller_->SupportsBlePeriodicAdvertisingSyncTransferSender(); total_num_of_advt_tracked_ = controller->GetVendorCapabilities().total_num_of_advt_tracked_; - if (is_batch_scan_support_) { + if (is_batch_scan_supported_) { vendor_specific_event_manager_->RegisterEventHandler( VseSubeventCode::BLE_THRESHOLD, handler->BindOn(this, &LeScanningManager::impl::on_storage_threshold_breach)); vendor_specific_event_manager_->RegisterEventHandler( @@ -284,7 +289,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback for (auto subevent_code : LeScanningEvents) { hci_layer_->UnregisterLeEventHandler(subevent_code); } - if (is_batch_scan_support_) { + if (is_batch_scan_supported_) { // TODO implete vse module // hci_layer_->UnregisterVesEventHandler(VseSubeventCode::BLE_THRESHOLD); // hci_layer_->UnregisterVesEventHandler(VseSubeventCode::BLE_TRACKING); @@ -297,35 +302,35 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback void handle_scan_results(LeMetaEventView event) { switch (event.GetSubeventCode()) { - case hci::SubeventCode::ADVERTISING_REPORT: + case SubeventCode::ADVERTISING_REPORT: handle_advertising_report(LeAdvertisingReportView::Create(event)); break; - case hci::SubeventCode::DIRECTED_ADVERTISING_REPORT: + case SubeventCode::DIRECTED_ADVERTISING_REPORT: handle_directed_advertising_report(LeDirectedAdvertisingReportView::Create(event)); break; - case hci::SubeventCode::EXTENDED_ADVERTISING_REPORT: + case SubeventCode::EXTENDED_ADVERTISING_REPORT: handle_extended_advertising_report(LeExtendedAdvertisingReportView::Create(event)); break; - case hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED: + case SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED: LePeriodicAdvertisingSyncEstablishedView::Create(event); periodic_sync_manager_.HandleLePeriodicAdvertisingSyncEstablished( LePeriodicAdvertisingSyncEstablishedView::Create(event)); break; - case hci::SubeventCode::PERIODIC_ADVERTISING_REPORT: + case SubeventCode::PERIODIC_ADVERTISING_REPORT: periodic_sync_manager_.HandleLePeriodicAdvertisingReport(LePeriodicAdvertisingReportView::Create(event)); break; - case hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST: + case SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST: periodic_sync_manager_.HandleLePeriodicAdvertisingSyncLost(LePeriodicAdvertisingSyncLostView::Create(event)); break; - case hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED: + case SubeventCode::PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED: periodic_sync_manager_.HandleLePeriodicAdvertisingSyncTransferReceived( LePeriodicAdvertisingSyncTransferReceivedView::Create(event)); break; - case hci::SubeventCode::SCAN_TIMEOUT: + case SubeventCode::SCAN_TIMEOUT: scanning_callbacks_->OnTimeout(); break; default: - LOG_ALWAYS_FATAL("Unknown advertising subevent %s", hci::SubeventCodeText(event.GetSubeventCode()).c_str()); + LOG_ALWAYS_FATAL("Unknown advertising subevent %s", SubeventCodeText(event.GetSubeventCode()).c_str()); } } @@ -361,21 +366,21 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback for (LeAdvertisingResponse report : reports) { uint16_t extended_event_type = 0; switch (report.event_type_) { - case hci::AdvertisingEventType::ADV_IND: + case AdvertisingEventType::ADV_IND: transform_to_extended_event_type( &extended_event_type, {.connectable = true, .scannable = true, .legacy = true}); break; - case hci::AdvertisingEventType::ADV_DIRECT_IND: + case AdvertisingEventType::ADV_DIRECT_IND: transform_to_extended_event_type( &extended_event_type, {.connectable = true, .directed = true, .legacy = true}); break; - case hci::AdvertisingEventType::ADV_SCAN_IND: + case AdvertisingEventType::ADV_SCAN_IND: transform_to_extended_event_type(&extended_event_type, {.scannable = true, .legacy = true}); break; - case hci::AdvertisingEventType::ADV_NONCONN_IND: + case AdvertisingEventType::ADV_NONCONN_IND: transform_to_extended_event_type(&extended_event_type, {.legacy = true}); break; - case hci::AdvertisingEventType::SCAN_RESPONSE: + case AdvertisingEventType::SCAN_RESPONSE: transform_to_extended_event_type( &extended_event_type, {.connectable = true, .scannable = true, .scan_response = true, .legacy = true}); break; @@ -452,7 +457,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback int8_t tx_power, int8_t rssi, uint16_t periodic_advertising_interval, - std::vector advertising_data) { + std::vector advertising_data) { bool is_scannable = event_type & (1 << kScannableBit); bool is_scan_response = event_type & (1 << kScanResponseBit); bool is_legacy = event_type & (1 << kLegacyBit); @@ -549,20 +554,21 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback switch (api_type_) { case ScanApiType::EXTENDED: le_scanning_interface_->EnqueueCommand( - hci::LeSetExtendedScanParametersBuilder::Create( + LeSetExtendedScanParametersBuilder::Create( own_address_type_, filter_policy_, phys_in_use, parameter_vector), module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete)); break; case ScanApiType::ANDROID_HCI: le_scanning_interface_->EnqueueCommand( - hci::LeExtendedScanParamsBuilder::Create( + LeExtendedScanParamsBuilder::Create( le_scan_type_, interval_ms_, window_ms_, own_address_type_, filter_policy_), module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete)); break; case ScanApiType::LEGACY: le_scanning_interface_->EnqueueCommand( - hci::LeSetScanParametersBuilder::Create( + + LeSetScanParametersBuilder::Create( le_scan_type_, interval_ms_, window_ms_, own_address_type_, filter_policy_), module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete)); break; @@ -635,14 +641,14 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback switch (api_type_) { case ScanApiType::EXTENDED: le_scanning_interface_->EnqueueCommand( - hci::LeSetExtendedScanEnableBuilder::Create( + LeSetExtendedScanEnableBuilder::Create( Enable::ENABLED, FilterDuplicates::DISABLED /* filter duplicates */, 0, 0), module_handler_->BindOnce(impl::check_status)); break; case ScanApiType::ANDROID_HCI: case ScanApiType::LEGACY: le_scanning_interface_->EnqueueCommand( - hci::LeSetScanEnableBuilder::Create(Enable::ENABLED, Enable::DISABLED /* filter duplicates */), + LeSetScanEnableBuilder::Create(Enable::ENABLED, Enable::DISABLED /* filter duplicates */), module_handler_->BindOnce(impl::check_status)); break; } @@ -658,14 +664,14 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback switch (api_type_) { case ScanApiType::EXTENDED: le_scanning_interface_->EnqueueCommand( - hci::LeSetExtendedScanEnableBuilder::Create( + LeSetExtendedScanEnableBuilder::Create( Enable::DISABLED, FilterDuplicates::DISABLED /* filter duplicates */, 0, 0), module_handler_->BindOnce(impl::check_status)); break; case ScanApiType::ANDROID_HCI: case ScanApiType::LEGACY: le_scanning_interface_->EnqueueCommand( - hci::LeSetScanEnableBuilder::Create(Enable::DISABLED, Enable::DISABLED /* filter duplicates */), + LeSetScanEnableBuilder::Create(Enable::DISABLED, Enable::DISABLED /* filter duplicates */), module_handler_->BindOnce(impl::check_status)); break; } @@ -704,7 +710,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void scan_filter_enable(bool enable) { - if (!is_filter_support_) { + if (!is_filter_supported_) { LOG_WARN("Advertising filter is not supported"); return; } @@ -728,7 +734,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback void scan_filter_parameter_setup( ApcfAction action, uint8_t filter_index, AdvertisingFilterParameter advertising_filter_parameter) { - if (!is_filter_support_) { + if (!is_filter_supported_) { LOG_WARN("Advertising filter is not supported"); return; } @@ -791,7 +797,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void scan_filter_add(uint8_t filter_index, std::vector filters) { - if (!is_filter_support_) { + if (!is_filter_supported_) { LOG_WARN("Advertising filter is not supported"); return; } @@ -1027,6 +1033,11 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback uint8_t ad_type, std::vector data, std::vector data_mask) { + if (!is_ad_type_filter_supported_) { + LOG_ERROR("AD type filter isn't supported"); + return; + } + if (data.size() != data_mask.size()) { LOG_ERROR("ad type mask should have the same length as ad type data"); return; @@ -1051,7 +1062,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback uint8_t batch_scan_truncated_max, uint8_t batch_scan_notify_threshold, ScannerId scanner_id) { - if (!is_batch_scan_support_) { + if (!is_batch_scan_supported_) { LOG_WARN("Batch scan is not supported"); return; } @@ -1078,7 +1089,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback uint32_t duty_cycle_scan_window_slots, uint32_t duty_cycle_scan_interval_slots, BatchScanDiscardRule batch_scan_discard_rule) { - if (!is_batch_scan_support_) { + if (!is_batch_scan_supported_) { LOG_WARN("Batch scan is not supported"); return; } @@ -1102,7 +1113,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void batch_scan_disable() { - if (!is_batch_scan_support_) { + if (!is_batch_scan_supported_) { LOG_WARN("Batch scan is not supported"); return; } @@ -1119,7 +1130,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback uint32_t duty_cycle_scan_window_slots, uint32_t duty_cycle_scan_interval_slots, BatchScanDiscardRule batch_scan_discard_rule) { - if (!is_batch_scan_support_) { + if (!is_batch_scan_supported_) { LOG_WARN("Batch scan is not supported"); return; } @@ -1161,7 +1172,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void batch_scan_read_results(ScannerId scanner_id, uint16_t total_num_of_records, BatchScanMode scan_mode) { - if (!is_batch_scan_support_) { + if (!is_batch_scan_supported_) { LOG_WARN("Batch scan is not supported"); int status = static_cast(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); scanning_callbacks_->OnBatchScanReports(scanner_id, status, 0, 0, {}); @@ -1187,7 +1198,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback void start_sync( uint8_t sid, const AddressWithType& address_with_type, uint16_t skip, uint16_t timeout, int request_id) { - if (!is_periodic_advertising_sync_transfer_sender_support_) { + if (!is_periodic_advertising_sync_transfer_sender_supported_) { LOG_WARN("PAST sender not supported on this device"); int status = static_cast(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); scanning_callbacks_->OnPeriodicSyncStarted(request_id, status, -1, sid, address_with_type, 0, 0); @@ -1204,7 +1215,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void stop_sync(uint16_t handle) { - if (!is_periodic_advertising_sync_transfer_sender_support_) { + if (!is_periodic_advertising_sync_transfer_sender_supported_) { LOG_WARN("PAST sender not supported on this device"); return; } @@ -1212,7 +1223,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void cancel_create_sync(uint8_t sid, const Address& address) { - if (!is_periodic_advertising_sync_transfer_sender_support_) { + if (!is_periodic_advertising_sync_transfer_sender_supported_) { LOG_WARN("PAST sender not supported on this device"); return; } @@ -1220,7 +1231,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void transfer_sync(const Address& address, uint16_t service_data, uint16_t sync_handle, int pa_source) { - if (!is_periodic_advertising_sync_transfer_sender_support_) { + if (!is_periodic_advertising_sync_transfer_sender_supported_) { LOG_WARN("PAST sender not supported on this device"); int status = static_cast(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address); @@ -1237,7 +1248,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void transfer_set_info(const Address& address, uint16_t service_data, uint8_t adv_handle, int pa_source) { - if (!is_periodic_advertising_sync_transfer_sender_support_) { + if (!is_periodic_advertising_sync_transfer_sender_supported_) { LOG_WARN("PAST sender not supported on this device"); int status = static_cast(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address); @@ -1254,7 +1265,7 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } void sync_tx_parameters(const Address& address, uint8_t mode, uint16_t skip, uint16_t timeout, int reg_id) { - if (!is_periodic_advertising_sync_transfer_sender_support_) { + if (!is_periodic_advertising_sync_transfer_sender_supported_) { LOG_WARN("PAST sender not supported on this device"); int status = static_cast(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); AddressWithType address_with_type(address, AddressType::RANDOM_DEVICE_ADDRESS); @@ -1288,6 +1299,10 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback periodic_sync_manager_.SetScanningCallback(scanning_callbacks_); } + bool is_ad_type_filter_supported() { + return is_ad_type_filter_supported_; + } + void on_set_scan_parameter_complete(CommandCompleteView view) { switch (view.GetCommandOpCode()) { case (OpCode::LE_SET_SCAN_PARAMETERS): { @@ -1413,6 +1428,26 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback } } + void on_apcf_read_extended_features_complete(CommandCompleteView view) { + ASSERT(view.IsValid()); + auto status_view = LeAdvFilterCompleteView::Create(view); + if (!status_view.IsValid()) { + LOG_WARN("Can not get valid LeAdvFilterCompleteView, return"); + return; + } + if (status_view.GetStatus() != ErrorCode::SUCCESS) { + LOG_WARN( + "Got a Command complete %s, status %s", + OpCodeText(view.GetCommandOpCode()).c_str(), + ErrorCodeText(status_view.GetStatus()).c_str()); + return; + } + auto complete_view = LeAdvFilterReadExtendedFeaturesCompleteView::Create(status_view); + ASSERT(complete_view.IsValid()); + is_ad_type_filter_supported_ = complete_view.GetAdTypeFilter() == 1; + LOG_INFO("set is_ad_type_filter_supported_ to %d", is_ad_type_filter_supported_); + } + void on_batch_scan_complete(CommandCompleteView view) { ASSERT(view.IsValid()); auto status_view = LeBatchScanCompleteView::Create(view); @@ -1539,13 +1574,13 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback Module* module_; os::Handler* module_handler_; - hci::HciLayer* hci_layer_; - hci::Controller* controller_; - hci::AclManager* acl_manager_; - hci::VendorSpecificEventManager* vendor_specific_event_manager_; + HciLayer* hci_layer_; + Controller* controller_; + AclManager* acl_manager_; + VendorSpecificEventManager* vendor_specific_event_manager_; storage::StorageModule* storage_module_; - hci::LeScanningInterface* le_scanning_interface_; - hci::LeAddressManager* le_address_manager_; + LeScanningInterface* le_scanning_interface_; + LeAddressManager* le_address_manager_; bool address_manager_registered_ = false; NullScanningCallback null_scanning_callback_; ScanningCallback* scanning_callbacks_ = &null_scanning_callback_; @@ -1555,9 +1590,10 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback bool scan_on_resume_ = false; bool paused_ = false; AdvertisingCache advertising_cache_; - bool is_filter_support_ = false; - bool is_batch_scan_support_ = false; - bool is_periodic_advertising_sync_transfer_sender_support_ = false; + bool is_filter_supported_ = false; + bool is_ad_type_filter_supported_ = false; + bool is_batch_scan_supported_ = false; + bool is_periodic_advertising_sync_transfer_sender_supported_ = false; LeScanType le_scan_type_ = LeScanType::ACTIVE; uint32_t interval_ms_{1000}; @@ -1598,18 +1634,18 @@ LeScanningManager::LeScanningManager() { } void LeScanningManager::ListDependencies(ModuleList* list) const { - list->add(); - list->add(); - list->add(); - list->add(); + list->add(); + list->add(); + list->add(); + list->add(); list->add(); } void LeScanningManager::Start() { pimpl_->start( GetHandler(), - GetDependency(), - GetDependency(), + GetDependency(), + GetDependency(), GetDependency(), GetDependency(), GetDependency()); @@ -1727,5 +1763,9 @@ void LeScanningManager::RegisterScanningCallback(ScanningCallback* scanning_call CallOn(pimpl_.get(), &impl::register_scanning_callback, scanning_callback); } +bool LeScanningManager::IsAdTypeFilterSupported() const { + return pimpl_->is_ad_type_filter_supported(); +} + } // namespace hci } // namespace bluetooth diff --git a/system/gd/hci/le_scanning_manager.h b/system/gd/hci/le_scanning_manager.h index d1288e169fe..a7da113f331 100644 --- a/system/gd/hci/le_scanning_manager.h +++ b/system/gd/hci/le_scanning_manager.h @@ -92,6 +92,8 @@ class LeScanningManager : public bluetooth::Module { virtual void RegisterScanningCallback(ScanningCallback* scanning_callback); + virtual bool IsAdTypeFilterSupported() const; + static const ModuleFactory Factory; protected: diff --git a/system/gd/hci/le_scanning_manager_test.cc b/system/gd/hci/le_scanning_manager_test.cc index f7b13bf77e5..1d70ef51fff 100644 --- a/system/gd/hci/le_scanning_manager_test.cc +++ b/system/gd/hci/le_scanning_manager_test.cc @@ -74,6 +74,7 @@ class TestHciLayer : public HciLayer { void EnqueueCommand( std::unique_ptr command, common::ContextualOnceCallback on_status) override { + std::lock_guard lock(mutex_); command_queue_.push(std::move(command)); command_status_callbacks.push_back(std::move(on_status)); command_count_--; @@ -86,6 +87,7 @@ class TestHciLayer : public HciLayer { void EnqueueCommand( std::unique_ptr command, common::ContextualOnceCallback on_complete) override { + std::lock_guard lock(mutex_); command_queue_.push(std::move(command)); command_complete_callbacks.push_back(std::move(on_complete)); command_count_--; @@ -103,31 +105,21 @@ class TestHciLayer : public HciLayer { command_future_ = std::make_unique>(command_promise_->get_future()); } - CommandView GetLastCommand() { - if (command_queue_.size() == 0) { - return empty_command_view_; - } - auto last = std::move(command_queue_.front()); - command_queue_.pop(); - return CommandView::Create(GetPacketView(std::move(last))); - } - CommandView GetCommand() { - if (!command_queue_.empty()) { - std::lock_guard lock(mutex_); - if (command_future_ != nullptr) { - command_future_.reset(); - command_promise_.reset(); - } - } else if (command_future_ != nullptr) { + // Wait for EnqueueCommand if command_queue_ is empty + if (command_queue_.empty() && command_future_ != nullptr) { command_future_->wait_for(std::chrono::milliseconds(1000)); } + std::lock_guard lock(mutex_); if (command_queue_.empty()) { LOG_ERROR("Command queue is empty"); return empty_command_view_; } - CommandView command_packet_view = GetLastCommand(); + + auto last = std::move(command_queue_.front()); + command_queue_.pop(); + CommandView command_packet_view = CommandView::Create(GetPacketView(std::move(last))); if (!command_packet_view.IsValid()) { LOG_ERROR("Got invalid command"); return empty_command_view_; @@ -283,7 +275,12 @@ class LeScanningManagerTest : public ::testing::Test { fake_registry_.InjectTestModule(&AclManager::Factory, test_acl_manager_); client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory); ASSERT_NE(client_handler_, nullptr); - test_hci_layer_->SetCommandFuture(1); + if (is_filter_support_) { + // Will send APCF read extended features command if APCF supported + test_hci_layer_->SetCommandFuture(2); + } else { + test_hci_layer_->SetCommandFuture(1); + } // configure_scan will be trigger by impl.start() and enqueue set scan parameter command fake_registry_.Start(&thread_); le_scanning_manager = @@ -293,6 +290,7 @@ class LeScanningManagerTest : public ::testing::Test { } void TearDown() override { + sync_client_handler(); fake_registry_.SynchronizeModuleHandler(&LeScanningManager::Factory, std::chrono::milliseconds(20)); fake_registry_.StopAll(); } @@ -378,9 +376,13 @@ class LeAndroidHciScanningManagerTest : public LeScanningManagerTest { is_filter_support_ = true; is_batch_scan_support_ = true; LeScanningManagerTest::SetUp(); + sync_client_handler(); } void HandleConfiguration() override { + ASSERT_EQ(OpCode::LE_ADV_FILTER, test_hci_layer_->GetCommand().GetOpCode()); + test_hci_layer_->IncomingEvent(LeAdvFilterReadExtendedFeaturesCompleteBuilder::Create(1, ErrorCode::SUCCESS, 0x01)); + sync_client_handler(); ASSERT_EQ(param_opcode_, test_hci_layer_->GetCommand().GetOpCode()); test_hci_layer_->IncomingEvent(LeExtendedScanParamsCompleteBuilder::Create(1, ErrorCode::SUCCESS)); } @@ -432,6 +434,22 @@ TEST_F(LeScanningManagerTest, start_scan_test) { test_hci_layer_->IncomingLeMetaEvent(LeAdvertisingReportBuilder::Create({report})); } +TEST_F(LeScanningManagerTest, is_ad_type_filter_supported_false_test) { + ASSERT_FALSE(le_scanning_manager->IsAdTypeFilterSupported()); +} + +TEST_F(LeScanningManagerTest, scan_filter_add_ad_type_not_supported_test) { + test_hci_layer_->SetCommandFuture(1); + std::vector filters = {}; + AdvertisingPacketContentFilterCommand filter{}; + filter.filter_type = ApcfFilterType::AD_TYPE; + filter.ad_type = 0x09; + filter.data = {0x12, 0x34, 0x56, 0x78}; + filter.data_mask = {0xff, 0xff, 0xff, 0xff}; + filters.push_back(filter); + le_scanning_manager->ScanFilterAdd(0x01, filters); +} + TEST_F(LeAndroidHciScanningManagerTest, startup_teardown) {} TEST_F(LeAndroidHciScanningManagerTest, start_scan_test) { @@ -464,6 +482,10 @@ TEST_F(LeAndroidHciScanningManagerTest, start_scan_test) { test_hci_layer_->IncomingLeMetaEvent(LeAdvertisingReportBuilder::Create({report})); } +TEST_F(LeAndroidHciScanningManagerTest, is_ad_type_filter_supported_true_test) { + ASSERT_TRUE(le_scanning_manager->IsAdTypeFilterSupported()); +} + TEST_F(LeAndroidHciScanningManagerTest, scan_filter_enable_test) { test_hci_layer_->SetCommandFuture(1); le_scanning_manager->ScanFilterEnable(true); diff --git a/system/main/shim/le_scanning_manager.cc b/system/main/shim/le_scanning_manager.cc index 9c71b06a35a..c3bf705fcfd 100644 --- a/system/main/shim/le_scanning_manager.cc +++ b/system/main/shim/le_scanning_manager.cc @@ -728,6 +728,10 @@ void bluetooth::shim::init_scanning_manager() { ->Init(); } +bool bluetooth::shim::is_ad_type_filter_supported() { + return bluetooth::shim::GetScanning()->IsAdTypeFilterSupported(); +} + void bluetooth::shim::set_ad_type_rsi_filter(bool enable) { bluetooth::hci::AdvertisingFilterParameter advertising_filter_parameter; bluetooth::shim::GetScanning()->ScanFilterParameterSetup( diff --git a/system/main/shim/le_scanning_manager.h b/system/main/shim/le_scanning_manager.h index a9cdd664c3b..13f2f6ecf9a 100644 --- a/system/main/shim/le_scanning_manager.h +++ b/system/main/shim/le_scanning_manager.h @@ -26,6 +26,7 @@ namespace shim { ::BleScannerInterface* get_ble_scanner_instance(); void init_scanning_manager(); +bool is_ad_type_filter_supported(); void set_ad_type_rsi_filter(bool enable); void set_empty_filter(bool enable); diff --git a/system/test/mock/mock_main_shim_le_scanning_manager.cc b/system/test/mock/mock_main_shim_le_scanning_manager.cc index 14a3c60d8e6..1f9899e4d3e 100644 --- a/system/test/mock/mock_main_shim_le_scanning_manager.cc +++ b/system/test/mock/mock_main_shim_le_scanning_manager.cc @@ -39,6 +39,11 @@ void bluetooth::shim::init_scanning_manager() { mock_function_count_map[__func__]++; } +bool bluetooth::shim::is_ad_type_filter_supported() { + mock_function_count_map[__func__]++; + return false; +} + void bluetooth::shim::set_ad_type_rsi_filter(bool enable) { mock_function_count_map[__func__]++; } -- GitLab From 55cfff0406dc744dbb2aff8b1c574b0b1de8a570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Fri, 30 Sep 2022 05:56:08 +0000 Subject: [PATCH 030/667] McpService: Allow to read MCP service by devices supporting LeAudio Current authorication mechanism is done in the way, that remote device needs to have connected LeAudio service in order to be authorize to operate on MCP. This might cause issue, when LeAudio service connects after remote device tries to read MCP service. This patch, looks into supported services by remote device, and if LeAudio is supported, authorization is granded. Bug: 249164851 Test: atest BluetoothInstrumentationTests Tag: #feature Merged-In: I12ba3010c324ce784a1bc1ab2f422575490761a1 Change-Id: I12ba3010c324ce784a1bc1ab2f422575490761a1 (cherry picked from commit 2d1c749b68cb9402f29fa42397eec730a0edfc2b) --- .../com/android/bluetooth/mcp/McpService.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/android/app/src/com/android/bluetooth/mcp/McpService.java b/android/app/src/com/android/bluetooth/mcp/McpService.java index dca1c1dad6a..bfccf7e3be0 100644 --- a/android/app/src/com/android/bluetooth/mcp/McpService.java +++ b/android/app/src/com/android/bluetooth/mcp/McpService.java @@ -18,6 +18,7 @@ package com.android.bluetooth.mcp; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothMcpServiceManager; import android.content.AttributionSource; import android.os.Handler; @@ -27,6 +28,7 @@ import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.ProfileService; +import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -199,10 +201,34 @@ public class McpService extends ProfileService { } public int getDeviceAuthorization(BluetoothDevice device) { - // TODO: For now just reject authorization for other than LeAudio device already authorized - // except for PTS. Consider intent based authorization mechanism for non-LeAudio devices. - return mDeviceAuthorizations.getOrDefault(device, Utils.isPtsTestMode() - ? BluetoothDevice.ACCESS_ALLOWED : BluetoothDevice.ACCESS_UNKNOWN); + /* Media control is allowed for + * 1. in PTS mode + * 2. authorized devices + * 3. Any LeAudio devices which are allowed to connect + */ + if (mDeviceAuthorizations.getOrDefault(device, Utils.isPtsTestMode() + ? BluetoothDevice.ACCESS_ALLOWED : BluetoothDevice.ACCESS_UNKNOWN) + == BluetoothDevice.ACCESS_ALLOWED) { + return BluetoothDevice.ACCESS_ALLOWED; + } + + LeAudioService leAudioService = LeAudioService.getLeAudioService(); + if (leAudioService == null) { + Log.e(TAG, "MCS access not permited. LeAudioService not available"); + return BluetoothDevice.ACCESS_UNKNOWN; + } + + if (leAudioService.getConnectionPolicy(device) + > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { + if (DBG) { + Log.d(TAG, "MCS authorization allowed based on supported LeAudio service"); + } + setDeviceAuthorized(device, true); + return BluetoothDevice.ACCESS_ALLOWED; + } + + Log.e(TAG, "MCS access not permited"); + return BluetoothDevice.ACCESS_UNKNOWN; } @GuardedBy("mLock") -- GitLab From 8576076ef10f5965ce86ed3d515ec16f8f62960d Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Fri, 9 Sep 2022 18:41:39 +0000 Subject: [PATCH 031/667] BluetoothMetrics: Bypass counter metrics caching for profile connection failures to get timestamp Test: atest BluetoothInstrumentationTests Bug: 193268714 Change-Id: I0bdbf89ddda03f27acda1f7f6d3112eb40393996 (cherry picked from commit 6f3ab6670abb56d4dc7bf2dc192ad995d8486f2a) Merged-In: I0bdbf89ddda03f27acda1f7f6d3112eb40393996 --- .../bluetooth/btservice/MetricsLogger.java | 15 ++++++-- .../btservice/MetricsLoggerTest.java | 33 ++++++++--------- system/gd/metrics/counter_metrics.cc | 15 ++++++-- system/gd/metrics/counter_metrics.h | 4 +-- system/gd/metrics/counter_metrics_unittest.cc | 35 ++++++++++--------- 5 files changed, 61 insertions(+), 41 deletions(-) diff --git a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java index 12a4f8107c0..c3f5b65acb0 100644 --- a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java +++ b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java @@ -97,7 +97,7 @@ public class MetricsLogger { return true; } - public boolean count(int key, long count) { + public boolean cacheCount(int key, long count) { if (!mInitialized) { Log.w(TAG, "MetricsLogger isn't initialized"); return false; @@ -167,9 +167,18 @@ public class MetricsLogger { getDrainIntent()); } - protected void writeCounter(int key, long count) { + public boolean count(int key, long count) { + if (!mInitialized) { + Log.w(TAG, "MetricsLogger isn't initialized"); + return false; + } + if (count <= 0) { + Log.w(TAG, "count is not larger than 0. count: " + count + " key: " + key); + return false; + } BluetoothStatsLog.write( BluetoothStatsLog.BLUETOOTH_CODE_PATH_COUNTER, key, count); + return true; } protected void drainBufferedCounters() { @@ -177,7 +186,7 @@ public class MetricsLogger { synchronized (mLock) { // send mCounters to statsd for (int key : mCounters.keySet()) { - writeCounter(key, mCounters.get(key)); + count(key, mCounters.get(key)); } mCounters.clear(); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java index 9566ef9cf28..851e998ac41 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java @@ -51,8 +51,9 @@ public class MetricsLoggerTest { public HashMap mTestableCounters = new HashMap<>(); @Override - protected void writeCounter(int key, long count) { + public boolean count(int key, long count) { mTestableCounters.put(key, count); + return true; } @Override @@ -141,18 +142,18 @@ public class MetricsLoggerTest { @Test public void testAddAndSendCountersNormalCases() { mTestableMetricsLogger.init(mMockAdapterService); - mTestableMetricsLogger.count(1, 10); - mTestableMetricsLogger.count(1, 10); - mTestableMetricsLogger.count(2, 5); + mTestableMetricsLogger.cacheCount(1, 10); + mTestableMetricsLogger.cacheCount(1, 10); + mTestableMetricsLogger.cacheCount(2, 5); mTestableMetricsLogger.drainBufferedCounters(); Assert.assertEquals(20L, mTestableMetricsLogger.mTestableCounters.get(1).longValue()); Assert.assertEquals(5L, mTestableMetricsLogger.mTestableCounters.get(2).longValue()); - mTestableMetricsLogger.count(1, 3); - mTestableMetricsLogger.count(2, 5); - mTestableMetricsLogger.count(2, 5); - mTestableMetricsLogger.count(3, 1); + mTestableMetricsLogger.cacheCount(1, 3); + mTestableMetricsLogger.cacheCount(2, 5); + mTestableMetricsLogger.cacheCount(2, 5); + mTestableMetricsLogger.cacheCount(3, 1); mTestableMetricsLogger.drainBufferedCounters(); Assert.assertEquals( 3L, mTestableMetricsLogger.mTestableCounters.get(1).longValue()); @@ -166,10 +167,10 @@ public class MetricsLoggerTest { public void testAddAndSendCountersCornerCases() { mTestableMetricsLogger.init(mMockAdapterService); Assert.assertTrue(mTestableMetricsLogger.isInitialized()); - mTestableMetricsLogger.count(1, -1); - mTestableMetricsLogger.count(3, 0); - mTestableMetricsLogger.count(2, 10); - mTestableMetricsLogger.count(2, Long.MAX_VALUE - 8L); + mTestableMetricsLogger.cacheCount(1, -1); + mTestableMetricsLogger.cacheCount(3, 0); + mTestableMetricsLogger.cacheCount(2, 10); + mTestableMetricsLogger.cacheCount(2, Long.MAX_VALUE - 8L); mTestableMetricsLogger.drainBufferedCounters(); Assert.assertFalse(mTestableMetricsLogger.mTestableCounters.containsKey(1)); @@ -181,9 +182,9 @@ public class MetricsLoggerTest { @Test public void testMetricsLoggerClose() { mTestableMetricsLogger.init(mMockAdapterService); - mTestableMetricsLogger.count(1, 1); - mTestableMetricsLogger.count(2, 10); - mTestableMetricsLogger.count(2, Long.MAX_VALUE); + mTestableMetricsLogger.cacheCount(1, 1); + mTestableMetricsLogger.cacheCount(2, 10); + mTestableMetricsLogger.cacheCount(2, Long.MAX_VALUE); mTestableMetricsLogger.close(); Assert.assertEquals( @@ -194,7 +195,7 @@ public class MetricsLoggerTest { @Test public void testMetricsLoggerNotInit() { - Assert.assertFalse(mTestableMetricsLogger.count(1, 1)); + Assert.assertFalse(mTestableMetricsLogger.cacheCount(1, 1)); mTestableMetricsLogger.drainBufferedCounters(); Assert.assertFalse(mTestableMetricsLogger.mTestableCounters.containsKey(1)); Assert.assertFalse(mTestableMetricsLogger.close()); diff --git a/system/gd/metrics/counter_metrics.cc b/system/gd/metrics/counter_metrics.cc index 4474b3923bb..820990eb361 100644 --- a/system/gd/metrics/counter_metrics.cc +++ b/system/gd/metrics/counter_metrics.cc @@ -49,7 +49,7 @@ void CounterMetrics::Stop() { LOG_INFO("Counter metrics canceled"); } -bool CounterMetrics::Count(int32_t key, int64_t count) { +bool CounterMetrics::CacheCount(int32_t key, int64_t count) { if (!IsInitialized()) { LOG_WARN("Counter metrics isn't initialized"); return false; @@ -73,8 +73,17 @@ bool CounterMetrics::Count(int32_t key, int64_t count) { return true; } -void CounterMetrics::WriteCounter(int32_t key, int64_t count) { +bool CounterMetrics::Count(int32_t key, int64_t count) { + if (!IsInitialized()) { + LOG_WARN("Counter metrics isn't initialized"); + return false; + } + if (count <= 0) { + LOG_WARN("count is not larger than 0. count: %s, key: %d", std::to_string(count).c_str(), key); + return false; + } os::LogMetricBluetoothCodePathCounterMetrics(key, count); + return true; } void CounterMetrics::DrainBufferedCounters() { @@ -85,7 +94,7 @@ void CounterMetrics::DrainBufferedCounters() { std::lock_guard lock(mutex_); LOG_INFO("Draining buffered counters"); for (auto const& pair : counters_) { - WriteCounter(pair.first, pair.second); + Count(pair.first, pair.second); } counters_.clear(); } diff --git a/system/gd/metrics/counter_metrics.h b/system/gd/metrics/counter_metrics.h index c21e9bedf9c..8148e63e6d7 100644 --- a/system/gd/metrics/counter_metrics.h +++ b/system/gd/metrics/counter_metrics.h @@ -25,7 +25,8 @@ namespace metrics { class CounterMetrics : public bluetooth::Module { public: - bool Count(int32_t key, int64_t value); + bool CacheCount(int32_t key, int64_t value); + virtual bool Count(int32_t key, int64_t count); void Stop() override; static const ModuleFactory Factory; @@ -36,7 +37,6 @@ class CounterMetrics : public bluetooth::Module { return std::string("BluetoothCounterMetrics"); } void DrainBufferedCounters(); - virtual void WriteCounter(int32_t key, int64_t count); virtual bool IsInitialized() { return initialized_; } diff --git a/system/gd/metrics/counter_metrics_unittest.cc b/system/gd/metrics/counter_metrics_unittest.cc index d9d89a29766..f09a6d9e906 100644 --- a/system/gd/metrics/counter_metrics_unittest.cc +++ b/system/gd/metrics/counter_metrics_unittest.cc @@ -33,8 +33,9 @@ class CounterMetricsTest : public ::testing::Test { } std::unordered_map test_counters_; private: - void WriteCounter(int32_t key, int64_t count) override { + bool Count(int32_t key, int64_t count) override { test_counters_[key] = count; + return true; } bool IsInitialized() override { return true; @@ -44,26 +45,26 @@ class CounterMetricsTest : public ::testing::Test { }; TEST_F(CounterMetricsTest, normal_case) { - ASSERT_TRUE(testable_counter_metrics_.Count(1, 2)); - ASSERT_TRUE(testable_counter_metrics_.Count(1, 3)); - ASSERT_TRUE(testable_counter_metrics_.Count(2, 4)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(1, 2)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(1, 3)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(2, 4)); testable_counter_metrics_.DrainBuffer(); ASSERT_EQ(testable_counter_metrics_.test_counters_[1], 5); ASSERT_EQ(testable_counter_metrics_.test_counters_[2], 4); } TEST_F(CounterMetricsTest, multiple_drain) { - ASSERT_TRUE(testable_counter_metrics_.Count(1, 2)); - ASSERT_TRUE(testable_counter_metrics_.Count(1, 3)); - ASSERT_TRUE(testable_counter_metrics_.Count(2, 4)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(1, 2)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(1, 3)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(2, 4)); testable_counter_metrics_.DrainBuffer(); ASSERT_EQ(testable_counter_metrics_.test_counters_[1], 5); ASSERT_EQ(testable_counter_metrics_.test_counters_[2], 4); testable_counter_metrics_.test_counters_.clear(); - ASSERT_TRUE(testable_counter_metrics_.Count(1, 20)); - ASSERT_TRUE(testable_counter_metrics_.Count(1, 30)); - ASSERT_TRUE(testable_counter_metrics_.Count(2, 40)); - ASSERT_TRUE(testable_counter_metrics_.Count(3, 100)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(1, 20)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(1, 30)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(2, 40)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(3, 100)); testable_counter_metrics_.DrainBuffer(); ASSERT_EQ(testable_counter_metrics_.test_counters_[1], 50); ASSERT_EQ(testable_counter_metrics_.test_counters_[2], 40); @@ -71,17 +72,17 @@ TEST_F(CounterMetricsTest, multiple_drain) { } TEST_F(CounterMetricsTest, overflow) { - ASSERT_TRUE(testable_counter_metrics_.Count(1, LLONG_MAX)); - ASSERT_FALSE(testable_counter_metrics_.Count(1, 1)); - ASSERT_FALSE(testable_counter_metrics_.Count(1, 2)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(1, LLONG_MAX)); + ASSERT_FALSE(testable_counter_metrics_.CacheCount(1, 1)); + ASSERT_FALSE(testable_counter_metrics_.CacheCount(1, 2)); testable_counter_metrics_.DrainBuffer(); ASSERT_EQ(testable_counter_metrics_.test_counters_[1], LLONG_MAX); } TEST_F(CounterMetricsTest, non_positive) { - ASSERT_TRUE(testable_counter_metrics_.Count(1, 5)); - ASSERT_FALSE(testable_counter_metrics_.Count(1, 0)); - ASSERT_FALSE(testable_counter_metrics_.Count(1, -1)); + ASSERT_TRUE(testable_counter_metrics_.CacheCount(1, 5)); + ASSERT_FALSE(testable_counter_metrics_.CacheCount(1, 0)); + ASSERT_FALSE(testable_counter_metrics_.CacheCount(1, -1)); testable_counter_metrics_.DrainBuffer(); ASSERT_EQ(testable_counter_metrics_.test_counters_[1], 5); } -- GitLab From 120952d0586c166d0022e3a4b3f22ff4299e68a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ko=C5=82odziejczyk?= Date: Mon, 8 Aug 2022 21:32:51 +0000 Subject: [PATCH 032/667] le_audio: Disable ASEs after 3sec from suspend request Patch adds possibility to disable streaming ASEs after defined (default 3 second) time. This also extends a little bit release timeout after suspend request to seperate those two operations on ASEs. Bug: 240145681 Tag: #feature Sponsor: jpawlowski@ Test: atest bluetooth_le_audio_test Test: atest bluetooth_le_audio_client_test Merged-In: Ic4775ed392dcc6a99a73204834ff903789c0b784 Change-Id: Ic4775ed392dcc6a99a73204834ff903789c0b784 (cherry picked from commit 0c40c9818ec7138ea572b898dc319950697a163b) --- system/bta/le_audio/client.cc | 23 ++++++++++++++++++- system/bta/le_audio/le_audio_client_test.cc | 3 +++ system/conf/bt_stack.conf | 3 +++ system/internal_include/stack_config.h | 1 + system/main/stack_config.cc | 7 ++++++ .../avrcp_device_fuzz/avrcp_device_fuzz.cc | 3 ++- .../profile/avrcp/tests/avrcp_device_test.cc | 3 ++- system/stack/test/btm/stack_btm_test.cc | 3 +++ system/stack/test/stack_smp_test.cc | 3 +++ system/test/common/stack_config.cc | 3 +++ 10 files changed, 49 insertions(+), 3 deletions(-) diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index 23a4f05a2be..7467ca9ec2f 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -240,7 +240,8 @@ class LeAudioClientImpl : public LeAudioClient { lc3_decoder_right(nullptr), audio_source_instance_(nullptr), audio_sink_instance_(nullptr), - suspend_timeout_(alarm_new("LeAudioSuspendTimeout")) { + suspend_timeout_(alarm_new("LeAudioSuspendTimeout")), + disable_timer_(alarm_new("LeAudioDisableTimer")) { LeAudioGroupStateMachine::Initialize(state_machine_callbacks_); groupStateMachine_ = LeAudioGroupStateMachine::Get(); @@ -2993,11 +2994,29 @@ class LeAudioClientImpl : public LeAudioClient { return; } + if (stack_config_get_interface() + ->get_pts_le_audio_disable_ases_before_stopping()) { + LOG_INFO("Stream disable_timer_ started"); + if (alarm_is_scheduled(disable_timer_)) alarm_cancel(disable_timer_); + + alarm_set_on_mloop( + disable_timer_, kAudioDisableTimeoutMs, + [](void* data) { + if (instance) instance->GroupSuspend(PTR_TO_INT(data)); + }, + INT_TO_PTR(active_group_id_)); + } + /* Group should tie in time to get requested status */ uint64_t timeoutMs = kAudioSuspentKeepIsoAliveTimeoutMs; timeoutMs = osi_property_get_int32(kAudioSuspentKeepIsoAliveTimeoutMsProp, timeoutMs); + if (stack_config_get_interface() + ->get_pts_le_audio_disable_ases_before_stopping()) { + timeoutMs += kAudioDisableTimeoutMs; + } + LOG_DEBUG("Stream suspend_timeout_ started: %d ms", static_cast(timeoutMs)); if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_); @@ -3946,9 +3965,11 @@ class LeAudioClientImpl : public LeAudioClient { const void* audio_source_instance_; const void* audio_sink_instance_; static constexpr uint64_t kAudioSuspentKeepIsoAliveTimeoutMs = 5000; + static constexpr uint64_t kAudioDisableTimeoutMs = 3000; static constexpr char kAudioSuspentKeepIsoAliveTimeoutMsProp[] = "persist.bluetooth.leaudio.audio.suspend.timeoutms"; alarm_t* suspend_timeout_; + alarm_t* disable_timer_; static constexpr uint64_t kDeviceAttachDelayMs = 500; std::vector cached_channel_data_; diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc index 36f25aeb3f1..0507e682156 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc @@ -148,6 +148,7 @@ bool get_pts_connect_eatt_before_encryption(void) { return false; } bool get_pts_unencrypt_broadcast(void) { return false; } bool get_pts_eatt_peripheral_collision_support(void) { return false; } bool get_pts_force_le_audio_multiple_contexts_metadata(void) { return false; } +bool get_pts_le_audio_disable_ases_before_stopping(void) { return false; } config_t* get_all(void) { return nullptr; } stack_config_t mock_stack_config{ @@ -169,6 +170,8 @@ stack_config_t mock_stack_config{ get_pts_eatt_peripheral_collision_support, .get_pts_force_le_audio_multiple_contexts_metadata = get_pts_force_le_audio_multiple_contexts_metadata, + .get_pts_le_audio_disable_ases_before_stopping = + get_pts_le_audio_disable_ases_before_stopping, .get_all = get_all, }; const stack_config_t* stack_config_get_interface(void) { diff --git a/system/conf/bt_stack.conf b/system/conf/bt_stack.conf index 84e1a99b6f5..f82351bb631 100644 --- a/system/conf/bt_stack.conf +++ b/system/conf/bt_stack.conf @@ -97,6 +97,9 @@ TRC_HID_DEV=2 # Use EATT for all services #PTS_UseEattForAllServices=true +# Suspend stream after some timeout in LE Audio client module +#PTS_LeAudioSuspendStreaming=true + # Force to update metadata with multiple CCIDs #PTS_ForceLeAudioMultipleContextsMetadata=true diff --git a/system/internal_include/stack_config.h b/system/internal_include/stack_config.h index 080d40183a0..efaec3f700e 100644 --- a/system/internal_include/stack_config.h +++ b/system/internal_include/stack_config.h @@ -47,6 +47,7 @@ typedef struct { int (*get_pts_l2cap_ecoc_send_num_of_sdu)(void); bool (*get_pts_l2cap_ecoc_reconfigure)(void); const std::string* (*get_pts_broadcast_audio_config_options)(void); + bool (*get_pts_le_audio_disable_ases_before_stopping)(void); config_t* (*get_all)(void); } stack_config_t; diff --git a/system/main/stack_config.cc b/system/main/stack_config.cc index 58994735c00..5c2921f1fb6 100644 --- a/system/main/stack_config.cc +++ b/system/main/stack_config.cc @@ -51,6 +51,7 @@ const char* PTS_L2CAP_ECOC_SEND_NUM_OF_SDU = "PTS_L2capEcocSendNumOfSdu"; const char* PTS_L2CAP_ECOC_RECONFIGURE = "PTS_L2capEcocReconfigure"; const char* PTS_BROADCAST_AUDIO_CONFIG_OPTION = "PTS_BroadcastAudioConfigOption"; +const char* PTS_LE_AUDIO_SUSPEND_STREAMING = "PTS_LeAudioSuspendStreaming"; static std::unique_ptr config; } // namespace @@ -202,6 +203,11 @@ static const std::string* get_pts_broadcast_audio_config_options(void) { PTS_BROADCAST_AUDIO_CONFIG_OPTION, NULL); } +static bool get_pts_le_audio_disable_ases_before_stopping(void) { + return config_get_bool(*config, CONFIG_DEFAULT_SECTION, + PTS_LE_AUDIO_SUSPEND_STREAMING, false); +} + static config_t* get_all(void) { return config.get(); } const stack_config_t interface = { @@ -226,6 +232,7 @@ const stack_config_t interface = { get_pts_l2cap_ecoc_send_num_of_sdu, get_pts_l2cap_ecoc_reconfigure, get_pts_broadcast_audio_config_options, + get_pts_le_audio_disable_ases_before_stopping, get_all}; const stack_config_t* stack_config_get_interface(void) { return &interface; } diff --git a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc index 42e9ba61ef8..bca1f6f9ba0 100644 --- a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc +++ b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc @@ -64,7 +64,8 @@ const stack_config_t interface = {nullptr, get_pts_avrcp_test, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr}; + nullptr, nullptr, + nullptr}; void Callback(uint8_t, bool, std::unique_ptr<::bluetooth::PacketBuilder>) {} diff --git a/system/profile/avrcp/tests/avrcp_device_test.cc b/system/profile/avrcp/tests/avrcp_device_test.cc index f59330b287a..e8058aa3786 100644 --- a/system/profile/avrcp/tests/avrcp_device_test.cc +++ b/system/profile/avrcp/tests/avrcp_device_test.cc @@ -60,7 +60,8 @@ const stack_config_t interface = {nullptr, get_pts_avrcp_test, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr}; + nullptr, nullptr, + nullptr}; // TODO (apanicke): All the tests below are just basic positive unit tests. // Add more tests to increase code coverage. diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc index 150012eee98..b9285e643a3 100644 --- a/system/stack/test/btm/stack_btm_test.cc +++ b/system/stack/test/btm/stack_btm_test.cc @@ -88,6 +88,7 @@ bool get_pts_l2cap_ecoc_reconfigure(void) { return false; } const std::string* get_pts_broadcast_audio_config_options(void) { return &kBroadcastAudioConfigOptions; } +bool get_pts_le_audio_disable_ases_before_stopping(void) { return false; } config_t* get_all(void) { return nullptr; } const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; } @@ -119,6 +120,8 @@ stack_config_t mock_stack_config{ .get_pts_l2cap_ecoc_reconfigure = get_pts_l2cap_ecoc_reconfigure, .get_pts_broadcast_audio_config_options = get_pts_broadcast_audio_config_options, + .get_pts_le_audio_disable_ases_before_stopping = + get_pts_le_audio_disable_ases_before_stopping, .get_all = get_all, }; const stack_config_t* stack_config_get_interface(void) { diff --git a/system/stack/test/stack_smp_test.cc b/system/stack/test/stack_smp_test.cc index d0c7c0fabc9..85a16f97976 100644 --- a/system/stack/test/stack_smp_test.cc +++ b/system/stack/test/stack_smp_test.cc @@ -63,6 +63,7 @@ bool get_pts_l2cap_ecoc_reconfigure(void) { return false; } const std::string* get_pts_broadcast_audio_config_options(void) { return &kBroadcastAudioConfigOptions; } +bool get_pts_le_audio_disable_ases_before_stopping(void) { return false; } config_t* get_all(void) { return nullptr; } const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; } @@ -95,6 +96,8 @@ stack_config_t mock_stack_config{ .get_pts_l2cap_ecoc_reconfigure = get_pts_l2cap_ecoc_reconfigure, .get_pts_broadcast_audio_config_options = get_pts_broadcast_audio_config_options, + .get_pts_le_audio_disable_ases_before_stopping = + get_pts_le_audio_disable_ases_before_stopping, .get_all = get_all, }; const stack_config_t* stack_config_get_interface(void) { diff --git a/system/test/common/stack_config.cc b/system/test/common/stack_config.cc index 300f0d64425..bb783f9f64f 100644 --- a/system/test/common/stack_config.cc +++ b/system/test/common/stack_config.cc @@ -48,6 +48,7 @@ bool get_pts_l2cap_ecoc_reconfigure(void) { return false; } const std::string* get_pts_broadcast_audio_config_options(void) { return &kBroadcastAudioConfigOptions; } +bool get_pts_le_audio_disable_ases_before_stopping(void) { return false; } struct config_t; config_t* get_all(void) { return nullptr; } struct packet_fragmenter_t; @@ -82,6 +83,8 @@ stack_config_t mock_stack_config{ .get_pts_l2cap_ecoc_reconfigure = get_pts_l2cap_ecoc_reconfigure, .get_pts_broadcast_audio_config_options = get_pts_broadcast_audio_config_options, + .get_pts_le_audio_disable_ases_before_stopping = + get_pts_le_audio_disable_ases_before_stopping, .get_all = get_all, }; -- GitLab From 275d903460c2a29ce23621086dcfed60a37b99d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ko=C5=82odziejczyk?= Date: Wed, 27 Jul 2022 13:25:36 +0000 Subject: [PATCH 033/667] le_audio: Replace temporary context policy with direction for group Patch replaces temporary context policy with group direction for LE Audio devices. Bug: 238143026 Tag: #feature Test: atest BluetoothInstrumentationTests Merged-In: Id36312ac3cb9743a6409a358564fcd93ac3655bb Change-Id: Id36312ac3cb9743a6409a358564fcd93ac3655bb (cherry picked from commit 0b6a4a6603f3ffde5beaf02f549b138ba2591268) --- .../bluetooth/le_audio/LeAudioService.java | 111 +++++------------- 1 file changed, 31 insertions(+), 80 deletions(-) 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 ed9776881be..ebad1ca1dc6 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -92,6 +92,11 @@ public class LeAudioService extends ProfileService { private static final int MAX_LE_AUDIO_STATE_MACHINES = 10; private static LeAudioService sLeAudioService; + /** + * Indicates group audio support for none direction + */ + private static final int AUDIO_DIRECTION_NONE = 0x00; + /** * Indicates group audio support for input direction */ @@ -102,11 +107,6 @@ public class LeAudioService extends ProfileService { */ private static final int AUDIO_DIRECTION_OUTPUT_BIT = 0x02; - /* - * Indicates no active contexts - */ - private static final int ACTIVE_CONTEXTS_NONE = 0; - private AdapterService mAdapterService; private DatabaseManager mDatabaseManager; private HandlerThread mStateMachinesThread; @@ -137,14 +137,14 @@ public class LeAudioService extends ProfileService { LeAudioGroupDescriptor() { mIsConnected = false; mIsActive = false; - mActiveContexts = ACTIVE_CONTEXTS_NONE; + mDirection = AUDIO_DIRECTION_NONE; mCodecStatus = null; mLostLeadDeviceWhileStreaming = null; } public Boolean mIsConnected; public Boolean mIsActive; - public Integer mActiveContexts; + public Integer mDirection; public BluetoothLeAudioCodecStatus mCodecStatus; /* This can be non empty only for the streaming time */ BluetoothDevice mLostLeadDeviceWhileStreaming; @@ -161,21 +161,6 @@ public class LeAudioService extends ProfileService { private final Map mDeviceGroupIdMap = new ConcurrentHashMap<>(); private final Map mDeviceAudioLocationMap = new ConcurrentHashMap<>(); - private final int mContextSupportingInputAudio = BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL - | BluetoothLeAudio.CONTEXT_TYPE_VOICE_ASSISTANTS; - - private final int mContextSupportingOutputAudio = BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL - | BluetoothLeAudio.CONTEXT_TYPE_MEDIA - | BluetoothLeAudio.CONTEXT_TYPE_GAME - | BluetoothLeAudio.CONTEXT_TYPE_INSTRUCTIONAL - | BluetoothLeAudio.CONTEXT_TYPE_VOICE_ASSISTANTS - | BluetoothLeAudio.CONTEXT_TYPE_LIVE - | BluetoothLeAudio.CONTEXT_TYPE_SOUND_EFFECTS - | BluetoothLeAudio.CONTEXT_TYPE_NOTIFICATIONS - | BluetoothLeAudio.CONTEXT_TYPE_RINGTONE - | BluetoothLeAudio.CONTEXT_TYPE_ALERTS - | BluetoothLeAudio.CONTEXT_TYPE_EMERGENCY_ALARM; - private BroadcastReceiver mBondStateChangedReceiver; private BroadcastReceiver mConnectionStateChangedReceiver; private BroadcastReceiver mMuteStateChangedReceiver; @@ -320,8 +305,8 @@ public class LeAudioService extends ProfileService { Integer group_id = entry.getKey(); if (descriptor.mIsActive) { descriptor.mIsActive = false; - updateActiveDevices(group_id, descriptor.mActiveContexts, - ACTIVE_CONTEXTS_NONE, descriptor.mIsActive); + updateActiveDevices(group_id, descriptor.mDirection, AUDIO_DIRECTION_NONE, + descriptor.mIsActive); break; } } @@ -624,32 +609,6 @@ public class LeAudioService extends ProfileService { return result; } - /** - * Get supported group audio direction from available context. - * - * @param activeContexts bitset of active context to be matched with possible audio direction - * support. - * @return matched possible audio direction support masked bitset - * {@link #AUDIO_DIRECTION_INPUT_BIT} if input audio is supported - * {@link #AUDIO_DIRECTION_OUTPUT_BIT} if output audio is supported - */ - private Integer getAudioDirectionsFromActiveContextsMap(Integer activeContexts) { - Integer supportedAudioDirections = 0; - - if (((activeContexts & mContextSupportingInputAudio) != 0) - || (Utils.isPtsTestMode() - && (activeContexts - & (BluetoothLeAudio.CONTEXT_TYPE_RINGTONE - | BluetoothLeAudio.CONTEXT_TYPE_MEDIA)) != 0)) { - supportedAudioDirections |= AUDIO_DIRECTION_INPUT_BIT; - } - if ((activeContexts & mContextSupportingOutputAudio) != 0) { - supportedAudioDirections |= AUDIO_DIRECTION_OUTPUT_BIT; - } - - return supportedAudioDirections; - } - private Integer getActiveGroupId() { synchronized (mGroupLock) { for (Map.Entry entry : mGroupDescriptors.entrySet()) { @@ -801,12 +760,7 @@ public class LeAudioService extends ProfileService { } private boolean updateActiveInDevice(BluetoothDevice device, Integer groupId, - Integer oldActiveContexts, Integer newActiveContexts) { - Integer oldSupportedAudioDirections = - getAudioDirectionsFromActiveContextsMap(oldActiveContexts); - Integer newSupportedAudioDirections = - getAudioDirectionsFromActiveContextsMap(newActiveContexts); - + Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections) { boolean oldSupportedByDeviceInput = (oldSupportedAudioDirections & AUDIO_DIRECTION_INPUT_BIT) != 0; boolean newSupportedByDeviceInput = (newSupportedAudioDirections @@ -866,12 +820,7 @@ public class LeAudioService extends ProfileService { } private boolean updateActiveOutDevice(BluetoothDevice device, Integer groupId, - Integer oldActiveContexts, Integer newActiveContexts) { - Integer oldSupportedAudioDirections = - getAudioDirectionsFromActiveContextsMap(oldActiveContexts); - Integer newSupportedAudioDirections = - getAudioDirectionsFromActiveContextsMap(newActiveContexts); - + Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections) { boolean oldSupportedByDeviceOutput = (oldSupportedAudioDirections & AUDIO_DIRECTION_OUTPUT_BIT) != 0; boolean newSupportedByDeviceOutput = (newSupportedAudioDirections @@ -941,13 +890,13 @@ public class LeAudioService extends ProfileService { /** * Report the active devices change to the active device manager and the media framework. * @param groupId id of group which devices should be updated - * @param newActiveContexts new active contexts for group of devices - * @param oldActiveContexts old active contexts for group of devices + * @param newSupportedAudioDirections new supported audio directions for group of devices + * @param oldSupportedAudioDirections old supported audio directions for group of devices * @param isActive if there is new active group * @return true if group is active after change false otherwise. */ - private boolean updateActiveDevices(Integer groupId, Integer oldActiveContexts, - Integer newActiveContexts, boolean isActive) { + private boolean updateActiveDevices(Integer groupId, Integer oldSupportedAudioDirections, + Integer newSupportedAudioDirections, boolean isActive) { BluetoothDevice device = null; if (isActive) { @@ -955,9 +904,11 @@ public class LeAudioService extends ProfileService { } boolean outReplaced = - updateActiveOutDevice(device, groupId, oldActiveContexts, newActiveContexts); + updateActiveOutDevice(device, groupId, oldSupportedAudioDirections, + newSupportedAudioDirections); boolean inReplaced = - updateActiveInDevice(device, groupId, oldActiveContexts, newActiveContexts); + updateActiveInDevice(device, groupId, oldSupportedAudioDirections, + newSupportedAudioDirections); if (outReplaced || inReplaced) { Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); @@ -1149,8 +1100,8 @@ public class LeAudioService extends ProfileService { return; } - descriptor.mIsActive = updateActiveDevices(groupId, - ACTIVE_CONTEXTS_NONE, descriptor.mActiveContexts, true); + descriptor.mIsActive = updateActiveDevices(groupId, AUDIO_DIRECTION_NONE, + descriptor.mDirection, true); if (descriptor.mIsActive) { notifyGroupStatusChanged(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); @@ -1167,8 +1118,8 @@ public class LeAudioService extends ProfileService { } descriptor.mIsActive = false; - updateActiveDevices(groupId, descriptor.mActiveContexts, - ACTIVE_CONTEXTS_NONE, descriptor.mIsActive); + updateActiveDevices(groupId, descriptor.mDirection, AUDIO_DIRECTION_NONE, + descriptor.mIsActive); /* Clear lost devices */ if (DBG) Log.d(TAG, "Clear for group: " + groupId); clearLostDevicesWhileStreaming(descriptor); @@ -1342,13 +1293,13 @@ public class LeAudioService extends ProfileService { if (descriptor != null) { if (descriptor.mIsActive) { descriptor.mIsActive = - updateActiveDevices(groupId, descriptor.mActiveContexts, - available_contexts, descriptor.mIsActive); + updateActiveDevices(groupId, descriptor.mDirection, direction, + descriptor.mIsActive); if (!descriptor.mIsActive) { notifyGroupStatusChanged(groupId, BluetoothLeAudio.GROUP_STATUS_INACTIVE); } } - descriptor.mActiveContexts = available_contexts; + descriptor.mDirection = direction; } else { Log.e(TAG, "no descriptors for group: " + groupId); } @@ -1669,8 +1620,8 @@ public class LeAudioService extends ProfileService { descriptor.mIsActive = false; /* Update audio framework */ updateActiveDevices(myGroupId, - descriptor.mActiveContexts, - descriptor.mActiveContexts, + descriptor.mDirection, + descriptor.mDirection, descriptor.mIsActive); return; } @@ -1678,8 +1629,8 @@ public class LeAudioService extends ProfileService { if (descriptor.mIsActive) { updateActiveDevices(myGroupId, - descriptor.mActiveContexts, - descriptor.mActiveContexts, + descriptor.mDirection, + descriptor.mDirection, descriptor.mIsActive); } } @@ -2803,7 +2754,7 @@ public class LeAudioService extends ProfileService { ProfileService.println(sb, " Group: " + groupId); ProfileService.println(sb, " isActive: " + descriptor.mIsActive); ProfileService.println(sb, " isConnected: " + descriptor.mIsConnected); - ProfileService.println(sb, " mActiveContexts: " + descriptor.mActiveContexts); + ProfileService.println(sb, " mDirection: " + descriptor.mDirection); ProfileService.println(sb, " group lead: " + getConnectedGroupLeadDevice(groupId)); ProfileService.println(sb, " first device: " + getFirstDeviceFromGroup(groupId)); ProfileService.println(sb, " lost lead device: " -- GitLab From 71dd27b52bfee0a52285a274223694b5dde39794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ko=C5=82odziejczyk?= Date: Fri, 30 Sep 2022 13:47:00 +0000 Subject: [PATCH 034/667] le_audio: Deactivate ASEs while whole set is being lost While putting back earbuds to case, ACL for all connected is dropped. Current flow deactivates ASEs while going through release ASE procedure. In case of loosing all devices and no releasing procedure, all fake active ases should be deactivated. They will be reactivated again when connected. Bug: 247823655 Tag: #feature Sponsor: jpawlowski@ Test: atest bluetooth_le_audio_test Test: atest bluetooth_le_audio_client_test Merged-In: I222aa4cae5d71487270199bc35c156c494fd99fb Change-Id: I222aa4cae5d71487270199bc35c156c494fd99fb (cherry picked from commit f2110bd15098f01710b1ccaf13f53543bbdcdff5) --- system/bta/le_audio/state_machine.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc index 734a45341f0..20ae741a049 100644 --- a/system/bta/le_audio/state_machine.cc +++ b/system/bta/le_audio/state_machine.cc @@ -617,6 +617,9 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { return; } + /* mark ASEs as not used. */ + leAudioDevice->DeactivateAllAses(); + /* If group is in Idle there is nothing to do here */ if ((group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) && (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) { @@ -625,9 +628,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { return; } - /* mark ASEs as not used. */ - leAudioDevice->DeactivateAllAses(); - LOG_DEBUG( " device: %s, group connected: %d, all active ase disconnected:: %d", leAudioDevice->address_.ToString().c_str(), -- GitLab From 2b1444090b8258c5a1cb9c21b3cc3ee9fd5792ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Wed, 5 Oct 2022 22:06:06 +0000 Subject: [PATCH 035/667] leaudio: Fix for handling multiple LeAudio headsets Make sure to get proper CIG ID when handling CisEstablished event Bug: 241883130 Test: atest BluetoothInstrumentationTests Test: manual: connect two LeAudio headsets and make a phone call Tag: #feature Merged-In: Ic945638c6c3a9e0e87a868bc26d5f6a3590a51ff Change-Id: Ic945638c6c3a9e0e87a868bc26d5f6a3590a51ff (cherry picked from commit 45952e2595b349f78eeeb17155e34328f675a054) --- system/bta/le_audio/client.cc | 18 +++++++++--------- system/bta/le_audio/devices.cc | 9 +++++++-- system/bta/le_audio/devices.h | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index 23a4f05a2be..b9103ee7fb7 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -3625,8 +3625,8 @@ class LeAudioClientImpl : public LeAudioClient { static_cast( data); - LeAudioDevice* leAudioDevice = - leAudioDevices_.FindByCisConnHdl(event->cis_conn_hdl); + LeAudioDevice* leAudioDevice = leAudioDevices_.FindByCisConnHdl( + event->cig_id, event->cis_conn_hdl); if (!leAudioDevice) { LOG(ERROR) << __func__ << ", no bonded Le Audio Device with CIS: " << +event->cis_conn_hdl; @@ -3650,8 +3650,8 @@ class LeAudioClientImpl : public LeAudioClient { static_cast( data); - LeAudioDevice* leAudioDevice = - leAudioDevices_.FindByCisConnHdl(event->cis_conn_hdl); + LeAudioDevice* leAudioDevice = leAudioDevices_.FindByCisConnHdl( + event->cig_id, event->cis_conn_hdl); if (!leAudioDevice) { LOG(ERROR) << __func__ << ", no bonded Le Audio Device with CIS: " << +event->cis_conn_hdl; @@ -3670,9 +3670,9 @@ class LeAudioClientImpl : public LeAudioClient { } void IsoSetupIsoDataPathCb(uint8_t status, uint16_t conn_handle, - uint8_t /* cig_id */) { + uint8_t cig_id) { LeAudioDevice* leAudioDevice = - leAudioDevices_.FindByCisConnHdl(conn_handle); + leAudioDevices_.FindByCisConnHdl(cig_id, conn_handle); LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_); instance->groupStateMachine_->ProcessHciNotifSetupIsoDataPath( @@ -3680,9 +3680,9 @@ class LeAudioClientImpl : public LeAudioClient { } void IsoRemoveIsoDataPathCb(uint8_t status, uint16_t conn_handle, - uint8_t /* cig_id */) { + uint8_t cig_id) { LeAudioDevice* leAudioDevice = - leAudioDevices_.FindByCisConnHdl(conn_handle); + leAudioDevices_.FindByCisConnHdl(cig_id, conn_handle); LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_); instance->groupStateMachine_->ProcessHciNotifRemoveIsoDataPath( @@ -3695,7 +3695,7 @@ class LeAudioClientImpl : public LeAudioClient { uint32_t retransmittedPackets, uint32_t crcErrorPackets, uint32_t rxUnreceivedPackets, uint32_t duplicatePackets) { LeAudioDevice* leAudioDevice = - leAudioDevices_.FindByCisConnHdl(conn_handle); + leAudioDevices_.FindByCisConnHdl(cig_id, conn_handle); if (!leAudioDevice) { LOG(WARNING) << __func__ << ", device under connection handle: " << loghex(conn_handle) diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc index 4e4283d8dd4..76c34e2cff8 100644 --- a/system/bta/le_audio/devices.cc +++ b/system/bta/le_audio/devices.cc @@ -2589,13 +2589,18 @@ LeAudioDevice* LeAudioDevices::FindByConnId(uint16_t conn_id) { return (iter == leAudioDevices_.end()) ? nullptr : iter->get(); } -LeAudioDevice* LeAudioDevices::FindByCisConnHdl(const uint16_t conn_hdl) { +LeAudioDevice* LeAudioDevices::FindByCisConnHdl(uint8_t cig_id, + uint16_t conn_hdl) { auto iter = std::find_if(leAudioDevices_.begin(), leAudioDevices_.end(), - [&conn_hdl](auto& d) { + [&conn_hdl, &cig_id](auto& d) { LeAudioDevice* dev; BidirectAsesPair ases; dev = d.get(); + if (dev->group_id_ != cig_id) { + return false; + } + ases = dev->GetAsesByCisConnHdl(conn_hdl); if (ases.sink || ases.source) return true; diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h index cd5a0fe8a3a..90fa87e1c5a 100644 --- a/system/bta/le_audio/devices.h +++ b/system/bta/le_audio/devices.h @@ -177,7 +177,7 @@ class LeAudioDevices { LeAudioDevice* FindByAddress(const RawAddress& address); std::shared_ptr GetByAddress(const RawAddress& address); LeAudioDevice* FindByConnId(uint16_t conn_id); - LeAudioDevice* FindByCisConnHdl(const uint16_t conn_hdl); + LeAudioDevice* FindByCisConnHdl(uint8_t cig_id, uint16_t conn_hdl); size_t Size(void); void Dump(int fd, int group_id); void Cleanup(void); -- GitLab From 2d1b99456e1b7f99a04b04f563eae488ca35642c Mon Sep 17 00:00:00 2001 From: Chris Manton Date: Tue, 20 Sep 2022 12:26:14 -0700 Subject: [PATCH 036/667] test: log capture add fd sync Bug: 244092860 Test: atest bluetooth_test_gd_unit bluetooth_test_gd Tag: #refactor BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines Ignore-AOSP-First: Cherry-pick Merged-In: I5c60363de865463a6a441e8c5e622713c25496b2 Change-Id: I5c60363de865463a6a441e8c5e622713c25496b2 --- system/gd/common/testing/log_capture.cc | 168 ++++++++++++++++++++++++ system/gd/common/testing/log_capture.h | 55 ++++++++ 2 files changed, 223 insertions(+) create mode 100644 system/gd/common/testing/log_capture.cc create mode 100644 system/gd/common/testing/log_capture.h diff --git a/system/gd/common/testing/log_capture.cc b/system/gd/common/testing/log_capture.cc new file mode 100644 index 00000000000..52a2830e5a0 --- /dev/null +++ b/system/gd/common/testing/log_capture.cc @@ -0,0 +1,168 @@ +/* + * 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. + */ + +#include "common/testing/log_capture.h" + +#include +#include +#include + +#include +#include +#include + +#include "os/log.h" + +namespace { +constexpr char kTempFilename[] = "/tmp/bt_gtest_log_capture-XXXXXX"; +constexpr size_t kTempFilenameMaxSize = 64; +constexpr size_t kBufferSize = 4096; +constexpr int kStandardErrorFd = STDERR_FILENO; +} // namespace + +namespace bluetooth { +namespace testing { + +LogCapture::LogCapture() { + fd_ = create_backing_store(); + if (fd_ == -1) { + LOG_ERROR("Unable to create backing storage : %s", strerror(errno)); + return; + } + if (!set_non_blocking(fd_)) { + LOG_ERROR("Unable to set socket non-blocking : %s", strerror(errno)); + return; + } + original_stderr_fd_ = fcntl(kStandardErrorFd, F_DUPFD_CLOEXEC); + if (original_stderr_fd_ == -1) { + LOG_ERROR("Unable to save original fd : %s", strerror(errno)); + return; + } + if (dup3(fd_, kStandardErrorFd, O_CLOEXEC) == -1) { + LOG_ERROR("Unable to duplicate stderr fd : %s", strerror(errno)); + return; + } +} + +LogCapture::~LogCapture() { + Rewind()->Flush(); + clean_up(); +} + +LogCapture* LogCapture::Rewind() { + if (fd_ != -1) { + if (lseek(fd_, 0, SEEK_SET) != 0) { + LOG_ERROR("Unable to rewind log capture : %s", strerror(errno)); + } + } + return this; +} + +bool LogCapture::Find(std::string to_find) { + std::string str = this->Read(); + return str.find(to_find) != std::string::npos; +} + +void LogCapture::Flush() { + if (fd_ != -1 && original_stderr_fd_ != -1) { + ssize_t sz{-1}; + do { + char buf[kBufferSize]; + sz = read(fd_, buf, sizeof(buf)); + if (sz > 0) { + write(original_stderr_fd_, buf, sz); + } + } while (sz == kBufferSize); + } +} + +void LogCapture::Sync() { + if (fd_ != -1) { + fsync(fd_); + } +} + +void LogCapture::Reset() { + if (fd_ != -1) { + if (ftruncate(fd_, 0UL) == -1) { + LOG_ERROR("Unable to truncate backing storage : %s", strerror(errno)); + } + this->Rewind(); + } +} + +std::string LogCapture::Read() { + if (fd_ == -1) { + return std::string(); + } + std::ostringstream oss; + ssize_t sz{-1}; + do { + char buf[kBufferSize]; + sz = read(fd_, buf, sizeof(buf)); + if (sz > 0) { + oss << buf; + } + } while (sz == kBufferSize); + return oss.str(); +} + +size_t LogCapture::Size() const { + size_t size{0UL}; + struct stat statbuf; + if (fd_ != -1 && fstat(fd_, &statbuf) != -1) { + size = statbuf.st_size; + } + return size; +} + +int LogCapture::create_backing_store() const { + char backing_store_filename[kTempFilenameMaxSize]; + strncpy(backing_store_filename, kTempFilename, kTempFilenameMaxSize); + int fd = mkstemp(backing_store_filename); + if (fd != -1) { + unlink(backing_store_filename); + } + return fd; +} + +bool LogCapture::set_non_blocking(int fd) const { + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + LOG_ERROR("Unable to get file descriptor flags : %s", strerror(errno)); + return false; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + LOG_ERROR("Unable to set file descriptor flags : %s", strerror(errno)); + return false; + } + return true; +} + +void LogCapture::clean_up() { + if (original_stderr_fd_ != -1) { + if (dup3(original_stderr_fd_, kStandardErrorFd, O_CLOEXEC) != kStandardErrorFd) { + LOG_ERROR("Unable to restore original fd : %s", strerror(errno)); + } + } + if (fd_ != -1) { + close(fd_); + fd_ = -1; + } +} + +} // namespace testing +} // namespace bluetooth diff --git a/system/gd/common/testing/log_capture.h b/system/gd/common/testing/log_capture.h new file mode 100644 index 00000000000..a141385f63e --- /dev/null +++ b/system/gd/common/testing/log_capture.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#include +#include + +namespace bluetooth { +namespace testing { + +class LogCapture { + public: + LogCapture(); + ~LogCapture(); + + // Rewind file pointer to start of log + // Returns a |this| pointer for chaining. See |Find| + LogCapture* Rewind(); + // Searches from filepointer to end of file for |to_find| string + // Returns true if found, false otherwise + bool Find(std::string to_find); + // Reads and returns the entirety of the backing store into a string + std::string Read(); + // Flushes contents of log capture back to |stderr| + void Flush(); + // Synchronize buffer contents to file descriptor + void Sync(); + // Returns the backing store size in bytes + size_t Size() const; + // Truncates and resets the file pointer discarding all logs up to this point + void Reset(); + + private: + int create_backing_store() const; + bool set_non_blocking(int fd) const; + void clean_up(); + + int fd_{-1}; + int original_stderr_fd_{-1}; +}; + +} // namespace testing +} // namespace bluetooth -- GitLab From 47f7dcb37389f2507ac57e77845b40a897b406f3 Mon Sep 17 00:00:00 2001 From: Qasim Javed Date: Thu, 15 Sep 2022 09:31:28 -0700 Subject: [PATCH 037/667] Wait for a string to appear in logs. Currently, we have to wait for arbitrary amount of time before we check the log for a particular string. Instead of that, add functionality to wait for a provided string to appear in the logs. When an fd is dup?()'ed, the oldfd and the newfd share the offset. So when the offset is changed on the oldfd using lseek, that change in offset is applied to the newfd as well. In the context of this change, it means that when we Rewind(), that also changes where the logs will be written in the backing store. It can cause problems if we are Rewind()'ing while logs are being written. This change keeps the dup'ed fd since its needed to create a backing store. However, it creates adds a new fd which uses the open system call to get a new fd to the underlying backing store file. Since this is a separate fd, an lseek on this fd will not result in a change in offset on the newfd and oldfd provided to dup3 system call. Bug: 246955322 Tag: #gd-refactor Test: atest bluetooth_test_gd_unit Ignore-AOSP-First: Cherry-pick Merged-In: Ib2fcfeef82d3419f4c02e1815b907b0b6ac499a9 Change-Id: Ib2fcfeef82d3419f4c02e1815b907b0b6ac499a9 --- system/gd/common/testing/log_capture.cc | 38 ++++- system/gd/common/testing/log_capture.h | 6 +- system/gd/common/testing/log_capture_test.cc | 149 +++++++++++++++++++ 3 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 system/gd/common/testing/log_capture_test.cc diff --git a/system/gd/common/testing/log_capture.cc b/system/gd/common/testing/log_capture.cc index 52a2830e5a0..4d034b43cbd 100644 --- a/system/gd/common/testing/log_capture.cc +++ b/system/gd/common/testing/log_capture.cc @@ -37,12 +37,12 @@ namespace bluetooth { namespace testing { LogCapture::LogCapture() { - fd_ = create_backing_store(); - if (fd_ == -1) { + std::tie(dup_fd_, fd_) = create_backing_store(); + if (dup_fd_ == -1 || fd_ == -1) { LOG_ERROR("Unable to create backing storage : %s", strerror(errno)); return; } - if (!set_non_blocking(fd_)) { + if (!set_non_blocking(dup_fd_)) { LOG_ERROR("Unable to set socket non-blocking : %s", strerror(errno)); return; } @@ -51,7 +51,7 @@ LogCapture::LogCapture() { LOG_ERROR("Unable to save original fd : %s", strerror(errno)); return; } - if (dup3(fd_, kStandardErrorFd, O_CLOEXEC) == -1) { + if (dup3(dup_fd_, kStandardErrorFd, O_CLOEXEC) == -1) { LOG_ERROR("Unable to duplicate stderr fd : %s", strerror(errno)); return; } @@ -101,6 +101,12 @@ void LogCapture::Reset() { LOG_ERROR("Unable to truncate backing storage : %s", strerror(errno)); } this->Rewind(); + // The only time we rewind the dup()'ed fd is during Reset() + if (dup_fd_ != -1) { + if (lseek(dup_fd_, 0, SEEK_SET) != 0) { + LOG_ERROR("Unable to rewind log capture : %s", strerror(errno)); + } + } } } @@ -129,14 +135,26 @@ size_t LogCapture::Size() const { return size; } -int LogCapture::create_backing_store() const { +void LogCapture::WaitUntilLogContains(std::promise* promise, std::string text) { + std::async([this, promise, text]() { + bool found = false; + do { + found = this->Rewind()->Find(text); + } while (!found); + promise->set_value(); + }); + promise->get_future().wait(); +} + +std::pair LogCapture::create_backing_store() const { char backing_store_filename[kTempFilenameMaxSize]; strncpy(backing_store_filename, kTempFilename, kTempFilenameMaxSize); - int fd = mkstemp(backing_store_filename); - if (fd != -1) { + int dup_fd = mkstemp(backing_store_filename); + int fd = open(backing_store_filename, O_RDWR); + if (dup_fd != -1) { unlink(backing_store_filename); } - return fd; + return std::make_pair(dup_fd, fd); } bool LogCapture::set_non_blocking(int fd) const { @@ -158,6 +176,10 @@ void LogCapture::clean_up() { LOG_ERROR("Unable to restore original fd : %s", strerror(errno)); } } + if (dup_fd_ != -1) { + close(dup_fd_); + dup_fd_ = -1; + } if (fd_ != -1) { close(fd_); fd_ = -1; diff --git a/system/gd/common/testing/log_capture.h b/system/gd/common/testing/log_capture.h index a141385f63e..e08c0fa9e10 100644 --- a/system/gd/common/testing/log_capture.h +++ b/system/gd/common/testing/log_capture.h @@ -15,6 +15,7 @@ */ #include +#include #include namespace bluetooth { @@ -41,12 +42,15 @@ class LogCapture { size_t Size() const; // Truncates and resets the file pointer discarding all logs up to this point void Reset(); + // Wait until the provided string shows up in the logs + void WaitUntilLogContains(std::promise* promise, std::string text); private: - int create_backing_store() const; + std::pair create_backing_store() const; bool set_non_blocking(int fd) const; void clean_up(); + int dup_fd_{-1}; int fd_{-1}; int original_stderr_fd_{-1}; }; diff --git a/system/gd/common/testing/log_capture_test.cc b/system/gd/common/testing/log_capture_test.cc new file mode 100644 index 00000000000..333128b4239 --- /dev/null +++ b/system/gd/common/testing/log_capture_test.cc @@ -0,0 +1,149 @@ +/* + * 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. + */ + +#include "log_capture.h" + +#include + +#include +#include +#include + +#include "common/init_flags.h" +#include "os/log.h" + +namespace { +const char* test_flags[] = { + "INIT_logging_debug_enabled_for_all=true", + nullptr, +}; + +constexpr char kEmptyLine[] = ""; +constexpr char kLogError[] = "LOG_ERROR"; +constexpr char kLogWarn[] = "LOG_WARN"; +constexpr char kLogInfo[] = "LOG_INFO"; +constexpr char kLogDebug[] = "LOG_DEBUG"; +constexpr char kLogVerbose[] = "LOG_VERBOSE"; + +} // namespace + +namespace bluetooth { +namespace testing { + +class LogCaptureTest : public ::testing::Test { + protected: + void SetUp() override {} + + void TearDown() override {} + + // The line number is part of the log output and must be factored out + size_t CalibrateOneLine(const char* log_line) { + LOG_INFO("%s", log_line); + return strlen(log_line); + } +}; + +TEST_F(LogCaptureTest, no_output) { + std::unique_ptr log_capture = std::make_unique(); + + ASSERT_TRUE(log_capture->Size() == 0); +} + +TEST_F(LogCaptureTest, truncate) { + std::unique_ptr log_capture = std::make_unique(); + + CalibrateOneLine(kLogError); + size_t size = log_capture->Size(); + ASSERT_TRUE(size > 0); + + log_capture->Reset(); + ASSERT_EQ(0UL, log_capture->Size()); + + CalibrateOneLine(kLogError); + ASSERT_EQ(size, log_capture->Size()); +} + +TEST_F(LogCaptureTest, log_size) { + std::unique_ptr log_capture = std::make_unique(); + + CalibrateOneLine(kEmptyLine); + size_t empty_line_size = log_capture->Size(); + log_capture->Reset(); + + std::vector log_lines = { + kLogError, + kLogWarn, + kLogInfo, + }; + + size_t msg_size{0}; + for (auto& log_line : log_lines) { + msg_size += CalibrateOneLine(log_line.c_str()); + } + + ASSERT_EQ(empty_line_size * log_lines.size() + msg_size, log_capture->Size()); + + ASSERT_TRUE(log_capture->Rewind()->Find(kLogError)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogWarn)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogInfo)); +} + +TEST_F(LogCaptureTest, typical) { + std::unique_ptr log_capture = std::make_unique(); + + LOG_ERROR("%s", kLogError); + LOG_WARN("%s", kLogWarn); + LOG_INFO("%s", kLogInfo); + LOG_DEBUG("%s", kLogDebug); + LOG_VERBOSE("%s", kLogVerbose); + + ASSERT_TRUE(log_capture->Rewind()->Find(kLogError)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogWarn)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogInfo)); + ASSERT_FALSE(log_capture->Rewind()->Find(kLogDebug)); + ASSERT_FALSE(log_capture->Rewind()->Find(kLogVerbose)); +} + +TEST_F(LogCaptureTest, with_logging_debug_enabled_for_all) { + bluetooth::common::InitFlags::Load(test_flags); + std::unique_ptr log_capture = std::make_unique(); + + LOG_ERROR("%s", kLogError); + LOG_WARN("%s", kLogWarn); + LOG_INFO("%s", kLogInfo); + LOG_DEBUG("%s", kLogDebug); + LOG_VERBOSE("%s", kLogVerbose); + + ASSERT_TRUE(log_capture->Rewind()->Find(kLogError)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogWarn)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogInfo)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogDebug)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogVerbose)); + bluetooth::common::InitFlags::Load(nullptr); +} + +TEST_F(LogCaptureTest, wait_until_log_contains) { + bluetooth::common::InitFlags::Load(test_flags); + std::unique_ptr log_capture = std::make_unique(); + + LOG_DEBUG("%s", kLogDebug); + std::promise promise; + log_capture->WaitUntilLogContains(&promise, kLogDebug); + bluetooth::common::InitFlags::Load(nullptr); +} + +} // namespace testing +} // namespace bluetooth -- GitLab From bc8fa74af171aba1d04647247b883381156fad8f Mon Sep 17 00:00:00 2001 From: Chris Manton Date: Wed, 5 Oct 2022 18:51:41 -0700 Subject: [PATCH 038/667] Split log_capture into android and host targets Bug: 251320004 Test: gd/cert/run Tag: #refactor BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines Ignore-AOSP-First: Cherry-pick Merged-In: I762978c5db35b4c182a60693df9777c24cf84e3a Change-Id: I762978c5db35b4c182a60693df9777c24cf84e3a --- system/gd/Android.bp | 4 + system/gd/common/testing/Android.bp | 36 +++++ .../gd/common/testing/android/log_capture.cc | 84 ++++++++++ .../testing/android/log_capture_test.cc | 41 +++++ .../common/testing/{ => host}/log_capture.cc | 0 .../common/testing/host/log_capture_test.cc | 150 ++++++++++++++++++ 6 files changed, 315 insertions(+) create mode 100644 system/gd/common/testing/Android.bp create mode 100644 system/gd/common/testing/android/log_capture.cc create mode 100644 system/gd/common/testing/android/log_capture_test.cc rename system/gd/common/testing/{ => host}/log_capture.cc (100%) create mode 100644 system/gd/common/testing/host/log_capture_test.cc diff --git a/system/gd/Android.bp b/system/gd/Android.bp index 7589c5f7800..27d4135204c 100644 --- a/system/gd/Android.bp +++ b/system/gd/Android.bp @@ -351,12 +351,16 @@ cc_test { srcs: [ ":BluetoothHalTestSources_hci_host", ":BluetoothOsTestSources_host", + ":BluetoothHostTestingLogCapture", + ":BluetoothHostTestingLogCaptureTest", ], }, android: { srcs: [ ":BluetoothHalTestSources_hci_android_hidl", ":BluetoothOsTestSources_android", + ":BluetoothAndroidTestingLogCapture", + ":BluetoothAndroidTestingLogCaptureTest", ], static_libs: [ "android.system.suspend.control-V1-ndk", diff --git a/system/gd/common/testing/Android.bp b/system/gd/common/testing/Android.bp new file mode 100644 index 00000000000..f72aee547c3 --- /dev/null +++ b/system/gd/common/testing/Android.bp @@ -0,0 +1,36 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_bt_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_bt_license"], +} + +filegroup { + name: "BluetoothAndroidTestingLogCapture", + srcs: [ + "android/log_capture.cc", + ], +} + +filegroup { + name: "BluetoothAndroidTestingLogCaptureTest", + srcs: [ + "android/log_capture_test.cc", + ], +} + +filegroup { + name: "BluetoothHostTestingLogCapture", + srcs: [ + "host/log_capture.cc", + ], +} + +filegroup { + name: "BluetoothHostTestingLogCaptureTest", + srcs: [ + "host/log_capture_test.cc", + ], +} diff --git a/system/gd/common/testing/android/log_capture.cc b/system/gd/common/testing/android/log_capture.cc new file mode 100644 index 00000000000..174bc3dd32a --- /dev/null +++ b/system/gd/common/testing/android/log_capture.cc @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#include "common/testing/log_capture.h" + +#include +#include +#include + +#include +#include +#include + +#include "os/log.h" + +namespace bluetooth { +namespace testing { + +LogCapture::LogCapture() { + LOG_INFO( + "Log capture disabled for android build dup_fd:%d fd:%d original_stderr_fd:%d", + dup_fd_, + fd_, + original_stderr_fd_); +} + +LogCapture::~LogCapture() {} + +LogCapture* LogCapture::Rewind() { + return this; +} + +bool LogCapture::Find(std::string to_find) { + // For |atest| assume all log captures succeed + return true; +} + +void LogCapture::Flush() {} + +void LogCapture::Sync() {} + +void LogCapture::Reset() {} + +std::string LogCapture::Read() { + return std::string(); +} + +size_t LogCapture::Size() const { + size_t size{0UL}; + return size; +} + +void LogCapture::WaitUntilLogContains(std::promise* promise, std::string text) { + std::async([promise, text]() { promise->set_value(); }); + promise->get_future().wait(); +} + +std::pair LogCapture::create_backing_store() const { + int dup_fd = -1; + int fd = -1; + return std::make_pair(dup_fd, fd); +} + +bool LogCapture::set_non_blocking(int fd) const { + return true; +} + +void LogCapture::clean_up() {} + +} // namespace testing +} // namespace bluetooth diff --git a/system/gd/common/testing/android/log_capture_test.cc b/system/gd/common/testing/android/log_capture_test.cc new file mode 100644 index 00000000000..a85eac0ac14 --- /dev/null +++ b/system/gd/common/testing/android/log_capture_test.cc @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#include "../log_capture.h" + +#include + +#include +#include +#include + +#include "common/init_flags.h" +#include "os/log.h" + +namespace bluetooth { +namespace testing { + +class LogCaptureTest : public ::testing::Test { + protected: + void SetUp() override {} + + void TearDown() override {} +}; + +TEST_F(LogCaptureTest, not_working_over_atest) {} + +} // namespace testing +} // namespace bluetooth diff --git a/system/gd/common/testing/log_capture.cc b/system/gd/common/testing/host/log_capture.cc similarity index 100% rename from system/gd/common/testing/log_capture.cc rename to system/gd/common/testing/host/log_capture.cc diff --git a/system/gd/common/testing/host/log_capture_test.cc b/system/gd/common/testing/host/log_capture_test.cc new file mode 100644 index 00000000000..cbd6a1cc23d --- /dev/null +++ b/system/gd/common/testing/host/log_capture_test.cc @@ -0,0 +1,150 @@ +/* + * 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. + */ + +#include "../log_capture.h" + +#include + +#include +#include +#include + +#include "common/init_flags.h" +#include "os/log.h" + +namespace { +const char* test_flags[] = { + "INIT_logging_debug_enabled_for_all=true", + nullptr, +}; + +constexpr char kEmptyLine[] = ""; +constexpr char kLogError[] = "LOG_ERROR"; +constexpr char kLogWarn[] = "LOG_WARN"; +constexpr char kLogInfo[] = "LOG_INFO"; +constexpr char kLogDebug[] = "LOG_DEBUG"; +constexpr char kLogVerbose[] = "LOG_VERBOSE"; + +} // namespace + +namespace bluetooth { +namespace testing { + +class LogCaptureTest : public ::testing::Test { + protected: + void SetUp() override {} + + void TearDown() override {} + + // The line number is part of the log output and must be factored out + size_t CalibrateOneLine(const char* log_line) { + LOG_INFO("%s", log_line); + return strlen(log_line); + } +}; + +TEST_F(LogCaptureTest, no_output) { + std::unique_ptr log_capture = std::make_unique(); + + ASSERT_TRUE(log_capture->Size() == 0); +} + +TEST_F(LogCaptureTest, truncate) { + std::unique_ptr log_capture = std::make_unique(); + + CalibrateOneLine(kLogError); + size_t size = log_capture->Size(); + ASSERT_TRUE(size > 0); + + log_capture->Reset(); + ASSERT_EQ(0UL, log_capture->Size()); + + CalibrateOneLine(kLogError); + ASSERT_EQ(size, log_capture->Size()); +} + +TEST_F(LogCaptureTest, log_size) { + std::unique_ptr log_capture = std::make_unique(); + + CalibrateOneLine(kEmptyLine); + size_t empty_line_size = log_capture->Size(); + log_capture->Reset(); + + std::vector log_lines = { + kLogError, + kLogWarn, + kLogInfo, + }; + + size_t msg_size{0}; + for (auto& log_line : log_lines) { + msg_size += CalibrateOneLine(log_line.c_str()); + } + + ASSERT_EQ(empty_line_size * log_lines.size() + msg_size, log_capture->Size()); + + ASSERT_TRUE(log_capture->Rewind()->Find(kLogError)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogWarn)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogInfo)); +} + +TEST_F(LogCaptureTest, typical) { + bluetooth::common::InitFlags::Load(nullptr); + std::unique_ptr log_capture = std::make_unique(); + + LOG_ERROR("%s", kLogError); + LOG_WARN("%s", kLogWarn); + LOG_INFO("%s", kLogInfo); + LOG_DEBUG("%s", kLogDebug); + LOG_VERBOSE("%s", kLogVerbose); + + ASSERT_TRUE(log_capture->Rewind()->Find(kLogError)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogWarn)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogInfo)); + ASSERT_FALSE(log_capture->Rewind()->Find(kLogDebug)); + ASSERT_FALSE(log_capture->Rewind()->Find(kLogVerbose)); +} + +TEST_F(LogCaptureTest, with_logging_debug_enabled_for_all) { + bluetooth::common::InitFlags::Load(test_flags); + std::unique_ptr log_capture = std::make_unique(); + + LOG_ERROR("%s", kLogError); + LOG_WARN("%s", kLogWarn); + LOG_INFO("%s", kLogInfo); + LOG_DEBUG("%s", kLogDebug); + LOG_VERBOSE("%s", kLogVerbose); + + ASSERT_TRUE(log_capture->Rewind()->Find(kLogError)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogWarn)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogInfo)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogDebug)); + ASSERT_TRUE(log_capture->Rewind()->Find(kLogVerbose)); + bluetooth::common::InitFlags::Load(nullptr); +} + +TEST_F(LogCaptureTest, wait_until_log_contains) { + bluetooth::common::InitFlags::Load(test_flags); + std::unique_ptr log_capture = std::make_unique(); + + LOG_DEBUG("%s", kLogDebug); + std::promise promise; + log_capture->WaitUntilLogContains(&promise, kLogDebug); + bluetooth::common::InitFlags::Load(nullptr); +} + +} // namespace testing +} // namespace bluetooth -- GitLab From 8e8729681784dd4f5b4d35cfc3733e184c15a192 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 9 Oct 2022 10:57:56 -0700 Subject: [PATCH 039/667] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ic67e0c408e7e88e60292d55a24b01c86574a1815 --- android/app/res/values-as/strings_sap.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/res/values-as/strings_sap.xml b/android/app/res/values-as/strings_sap.xml index f886e47fa17..f18138bb0b1 100644 --- a/android/app/res/values-as/strings_sap.xml +++ b/android/app/res/values-as/strings_sap.xml @@ -1,8 +1,8 @@ - "ব্লুটুথৰ ছিম ব্যৱহাৰ" - "ব্লুটুথৰ ছিম ব্যৱহাৰ" + "ব্লুটুথৰ ছিম এক্সেছ" + "ব্লুটুথৰ ছিম এক্সেছ" "সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টক অনুৰোধ কৰিবনে?" "সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টৰ অপেক্ষা কৰি থকা হৈছে" "বিচ্ছিন্ন কৰক" -- GitLab From 5f3362200fbe0ccc98348aa8fb9d71fe4e5e28e6 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 9 Oct 2022 10:58:14 -0700 Subject: [PATCH 040/667] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I344188c1cb935e1af245f0a30c95c3e780f4a205 --- android/app/res/values-as/strings_sap.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/res/values-as/strings_sap.xml b/android/app/res/values-as/strings_sap.xml index f886e47fa17..f18138bb0b1 100644 --- a/android/app/res/values-as/strings_sap.xml +++ b/android/app/res/values-as/strings_sap.xml @@ -1,8 +1,8 @@ - "ব্লুটুথৰ ছিম ব্যৱহাৰ" - "ব্লুটুথৰ ছিম ব্যৱহাৰ" + "ব্লুটুথৰ ছিম এক্সেছ" + "ব্লুটুথৰ ছিম এক্সেছ" "সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টক অনুৰোধ কৰিবনে?" "সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টৰ অপেক্ষা কৰি থকা হৈছে" "বিচ্ছিন্ন কৰক" -- GitLab From f36be4b1dd925134dcc883507903363d2ef3274d Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 9 Oct 2022 10:59:25 -0700 Subject: [PATCH 041/667] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I005e7554c70fa1471a2d7a2654fc8ce5c697a565 --- android/app/res/values-as/strings.xml | 4 ++-- android/app/res/values-de/strings.xml | 4 ++-- android/app/res/values-fr-rCA/strings.xml | 4 ++-- android/app/res/values-pt-rPT/strings.xml | 2 +- android/app/res/values-te/strings.xml | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/res/values-as/strings.xml b/android/app/res/values-as/strings.xml index d76fae61cf1..39eeb14c769 100644 --- a/android/app/res/values-as/strings.xml +++ b/android/app/res/values-as/strings.xml @@ -16,7 +16,7 @@ - "ডাউনল’ড মেনেজাৰ ব্যৱহাৰ কৰিব পাৰে।" + "ডাউনল’ড মেনেজাৰ এক্সেছ কৰিব পাৰে।" "এপটোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।" "ব্লুটুথ ডিভাইচ এক্সেছ কৰাৰ স্বীকৃতি দিয়ে।" "এপ্‌টোক এটা ব্লুটুথ ডিভাইচ অস্থায়ীৰূপে স্বীকাৰ কৰাৰ অনুমতি দিয়ে যিয়ে ডিভাইচটোক ব্যৱহাৰকাৰীৰ নিশ্চিতিকৰণৰ অবিহনেই ইয়ালৈ ফাইল পঠিওৱাৰ অনুমতি দিয়ে।" @@ -118,7 +118,7 @@ "এতিয়া প্লে’ হৈ আছে" "ছেভ কৰক" "বাতিল কৰক" - "ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ অনুমতি দিবই লাগিব।" + "ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ এক্সেছ দিবই লাগিব।" "বাকী থকা শ্লটবোৰ:" "এপ্লিকেশ্বন আইকন" "ব্লুটুথৰ জৰিয়তে বাৰ্তা শ্বেয়াৰ কৰাৰ ছেটিং" diff --git a/android/app/res/values-de/strings.xml b/android/app/res/values-de/strings.xml index 6f2e2dbf26a..0b0339ba898 100644 --- a/android/app/res/values-de/strings.xml +++ b/android/app/res/values-de/strings.xml @@ -80,9 +80,9 @@ "Die Datei wird empfangen. Überprüfe den Fortschritt in der Benachrichtigungskonsole." "Die Datei kann nicht empfangen werden." "Der Empfang der Datei von \"%1$s\" wurde angehalten." - "Datei wird an \"%1$s\" gesendet..." + "Datei wird an „%1$s“ gesendet..." "%1$s Dateien werden an \"%2$s\" gesendet." - "Die Übertragung der Datei an \"%1$s\" wurde abgebrochen" + "Die Übertragung der Datei an „%1$s“ wurde abgebrochen" "Auf dem USB-Speicher ist nicht genügend Platz, um die Datei zu speichern." "Auf der SD-Karte ist nicht genügend Platz, um die Datei zu speichern." "Erforderlicher Speicherplatz: %1$s" diff --git a/android/app/res/values-fr-rCA/strings.xml b/android/app/res/values-fr-rCA/strings.xml index 4d4abd514ac..5531ec3f3ee 100644 --- a/android/app/res/values-fr-rCA/strings.xml +++ b/android/app/res/values-fr-rCA/strings.xml @@ -80,9 +80,9 @@ "La réception du fichier va commencer. La progression va s\'afficher dans le panneau de notification." "Impossible de recevoir le fichier." "Réception du fichier de \"%1$s\" interrompue" - "Envoi du fichier à \"%1$s\"" + "Envoi du fichier à « %1$s »" "Envoi de %1$s fichiers à \"%2$s\"" - "Envoi du fichier à \"%1$s\" interrompu" + "Envoi du fichier à « %1$s » interrompu" "Espace insuffisant sur la mémoire de stockage USB pour l\'enregistrement du fichier." "Espace insuffisant sur la carte SD pour l\'enregistrement du fichier." "Espace requis : %1$s" diff --git a/android/app/res/values-pt-rPT/strings.xml b/android/app/res/values-pt-rPT/strings.xml index 204acda30e0..478cf114073 100644 --- a/android/app/res/values-pt-rPT/strings.xml +++ b/android/app/res/values-pt-rPT/strings.xml @@ -86,7 +86,7 @@ "Não existe espaço suficiente na memória USB para guardar o ficheiro." "Não existe espaço suficiente no cartão SD para guardar o ficheiro." "Espaço necessário: %1$s" - "Existem demasiados pedidos em processamento. Tente novamente mais tarde." + "Existem demasiados pedidos em processamento. Tente mais tarde." "Ainda não foi iniciada a transferência do ficheiro." "Transferência do ficheiro em curso." "Transferência de ficheiros concluída com êxito." diff --git a/android/app/res/values-te/strings.xml b/android/app/res/values-te/strings.xml index b2a52128b51..98d1626b4bc 100644 --- a/android/app/res/values-te/strings.xml +++ b/android/app/res/values-te/strings.xml @@ -48,14 +48,14 @@ "ఫైల్ బదిలీ" "వీరి నుండి: \"%1$s\"" "ఫైల్: %1$s" - "ఫైల్ పరిమాణం: %1$s" + "ఫైల్ సైజ్‌: %1$s" "ఫైల్‌ను స్వీకరిస్తోంది…" "ఆపివేయి" "దాచు" "దీని నుండి" "ఫైల్ పేరు" - "పరిమాణం" + "సైజ్‌" "ఫైల్ స్వీకరించబడలేదు" "ఫైల్: %1$s" "కారణం: %1$s" -- GitLab From 99635465110395ce3fb145bb8a584d8f403a9002 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 9 Oct 2022 10:59:50 -0700 Subject: [PATCH 042/667] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Idc6413d4bca512daeeaccd9009fcaa4dcce80fad --- android/app/res/values-ar/strings.xml | 2 +- android/app/res/values-as/strings.xml | 4 ++-- android/app/res/values-de/strings.xml | 4 ++-- android/app/res/values-eu/strings.xml | 12 ++++++------ android/app/res/values-fr-rCA/strings.xml | 4 ++-- android/app/res/values-pt-rPT/strings.xml | 2 +- android/app/res/values-te/strings.xml | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/android/app/res/values-ar/strings.xml b/android/app/res/values-ar/strings.xml index 87b7ca6abca..423bd412542 100644 --- a/android/app/res/values-ar/strings.xml +++ b/android/app/res/values-ar/strings.xml @@ -115,7 +115,7 @@ "فتح" "محو من القائمة" "محو" - "التعرّف التلقائي على الموسيقى" + "قيد التشغيل الآن" "حفظ" "إلغاء" "حدد الحسابات التي تريد مشاركتها عبر البلوتوث. لا يزال يتعين عليك قبول أي دخول إلى الحسابات أثناء الاتصال." diff --git a/android/app/res/values-as/strings.xml b/android/app/res/values-as/strings.xml index 4ecc5fbee33..847be126713 100644 --- a/android/app/res/values-as/strings.xml +++ b/android/app/res/values-as/strings.xml @@ -16,7 +16,7 @@ - "ডাউনল’ড মেনেজাৰ ব্যৱহাৰ কৰিব পাৰে।" + "ডাউনল’ড মেনেজাৰ এক্সেছ কৰিব পাৰে।" "এপটোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।" "ব্লুটুথ ডিভাইচ এক্সেছ কৰাৰ স্বীকৃতি দিয়ে।" "এপ্‌টোক এটা ব্লুটুথ ডিভাইচ অস্থায়ীৰূপে স্বীকাৰ কৰাৰ অনুমতি দিয়ে যিয়ে ডিভাইচটোক ব্যৱহাৰকাৰীৰ নিশ্চিতিকৰণৰ অবিহনেই ইয়ালৈ ফাইল পঠিওৱাৰ অনুমতি দিয়ে।" @@ -118,7 +118,7 @@ "এতিয়া প্লে’ হৈ আছে" "ছেভ কৰক" "বাতিল কৰক" - "ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ অনুমতি দিবই লাগিব।" + "ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ এক্সেছ দিবই লাগিব।" "বাকী থকা শ্লটবোৰ:" "এপ্লিকেশ্বন আইকন" "ব্লুটুথৰ জৰিয়তে বাৰ্তা শ্বেয়াৰ কৰাৰ ছেটিং" diff --git a/android/app/res/values-de/strings.xml b/android/app/res/values-de/strings.xml index 719dd9532b0..f432967d9ed 100644 --- a/android/app/res/values-de/strings.xml +++ b/android/app/res/values-de/strings.xml @@ -80,9 +80,9 @@ "Die Datei wird empfangen. Überprüfe den Fortschritt in der Benachrichtigungskonsole." "Die Datei kann nicht empfangen werden." "Der Empfang der Datei von \"%1$s\" wurde angehalten." - "Datei wird an \"%1$s\" gesendet..." + "Datei wird an „%1$s“ gesendet..." "%1$s Dateien werden an \"%2$s\" gesendet." - "Die Übertragung der Datei an \"%1$s\" wurde abgebrochen" + "Die Übertragung der Datei an „%1$s“ wurde abgebrochen" "Auf dem USB-Speicher ist nicht genügend Platz, um die Datei zu speichern." "Auf der SD-Karte ist nicht genügend Platz, um die Datei zu speichern." "Erforderlicher Speicherplatz: %1$s" diff --git a/android/app/res/values-eu/strings.xml b/android/app/res/values-eu/strings.xml index d48e5a06f35..149917e5336 100644 --- a/android/app/res/values-eu/strings.xml +++ b/android/app/res/values-eu/strings.xml @@ -20,14 +20,14 @@ "Bluetooth bidezko partekatzeen kudeatzailea atzitzea eta fitxategiak transferitzeko erabiltzeko baimena ematen die aplikazioei." "Ezarri Bluetooth bidezko gailuak onartutakoen zerrendan." "Bluetooth bidezko gailu bat aldi baterako onartutakoen zerrendan ezartzeko baimena ematen die aplikazioei, gailu honetara fitxategiak bidaltzeko baimena izan dezan, baina gailu honen erabiltzaileari berrespena eskatu beharrik gabe." - "Bluetooth-a" + "Bluetootha" "Identifikatu ezin den gailua" "Ezezaguna" "Hegaldi modua" - "Ezin duzu erabili Bluetooth-a Hegaldi moduan." + "Ezin duzu erabili Bluetootha Hegaldi moduan." - "Bluetooth-zerbitzuak erabiltzeko, Bluetooth-a aktibatu behar duzu." - "Bluetooth-a aktibatu nahi duzu?" + "Bluetooth-zerbitzuak erabiltzeko, Bluetootha aktibatu behar duzu." + "Bluetootha aktibatu nahi duzu?" "Utzi" "Aktibatu" "Fitxategi-transferentzia" @@ -76,7 +76,7 @@ "Ez dago fitxategirik" "Ez dago horrelako fitxategirik. \n" "Itxaron…" - "Bluetooth-a aktibatzen…" + "Bluetootha aktibatzen…" "Fitxategia jasoko da. Egoera kontrolatzeko, joan Jakinarazpenen panelera." "Ezin da fitxategia jaso." "\"%1$s\" igorlearen fitxategia jasotzeari utzi zaio" @@ -127,5 +127,5 @@ "Deskonektatu da Bluetooth bidezko audioa" "Bluetooth bidezko audioa" "Ezin dira transferitu 4 GB baino gehiagoko fitxategiak" - "Konektatu Bluetooth-era" + "Konektatu Bluetoothera" diff --git a/android/app/res/values-fr-rCA/strings.xml b/android/app/res/values-fr-rCA/strings.xml index 5e10989fa30..1bc3d20bd4e 100644 --- a/android/app/res/values-fr-rCA/strings.xml +++ b/android/app/res/values-fr-rCA/strings.xml @@ -80,9 +80,9 @@ "La réception du fichier va commencer. La progression va s\'afficher dans le panneau de notification." "Impossible de recevoir le fichier." "Réception du fichier de \"%1$s\" interrompue" - "Envoi du fichier à \"%1$s\"" + "Envoi du fichier à « %1$s »" "Envoi de %1$s fichiers à \"%2$s\"" - "Envoi du fichier à \"%1$s\" interrompu" + "Envoi du fichier à « %1$s » interrompu" "Espace insuffisant sur la mémoire de stockage USB pour l\'enregistrement du fichier." "Espace insuffisant sur la carte SD pour l\'enregistrement du fichier." "Espace requis : %1$s" diff --git a/android/app/res/values-pt-rPT/strings.xml b/android/app/res/values-pt-rPT/strings.xml index c2c09c3013d..524a95b455b 100644 --- a/android/app/res/values-pt-rPT/strings.xml +++ b/android/app/res/values-pt-rPT/strings.xml @@ -86,7 +86,7 @@ "Não existe espaço suficiente na memória USB para guardar o ficheiro." "Não existe espaço suficiente no cartão SD para guardar o ficheiro." "Espaço necessário: %1$s" - "Existem demasiados pedidos em processamento. Tente novamente mais tarde." + "Existem demasiados pedidos em processamento. Tente mais tarde." "Ainda não foi iniciada a transferência do ficheiro." "Transferência do ficheiro em curso." "Transferência de ficheiros concluída com êxito." diff --git a/android/app/res/values-te/strings.xml b/android/app/res/values-te/strings.xml index bdca35f5e85..6e8024ee61f 100644 --- a/android/app/res/values-te/strings.xml +++ b/android/app/res/values-te/strings.xml @@ -48,14 +48,14 @@ "ఫైల్ బదిలీ" "వీరి నుండి: \"%1$s\"" "ఫైల్: %1$s" - "ఫైల్ పరిమాణం: %1$s" + "ఫైల్ సైజ్‌: %1$s" "ఫైల్‌ను స్వీకరిస్తోంది…" "ఆపివేయి" "దాచు" "దీని నుండి" "ఫైల్ పేరు" - "పరిమాణం" + "సైజ్‌" "ఫైల్ స్వీకరించబడలేదు" "ఫైల్: %1$s" "కారణం: %1$s" -- GitLab From 3031d60a5f604a6a8c8b99352c130795ac4e5381 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 9 Oct 2022 11:00:56 -0700 Subject: [PATCH 043/667] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I64c6659cc3c3f1657323c659f2187d6c083cb195 --- android/app/res/values-eu/test_strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/res/values-eu/test_strings.xml b/android/app/res/values-eu/test_strings.xml index e6016491941..4c501ab19e1 100644 --- a/android/app/res/values-eu/test_strings.xml +++ b/android/app/res/values-eu/test_strings.xml @@ -1,7 +1,7 @@ - "Bluetooth-a" + "Bluetootha" "Sartu erregistroa" "Berretsi erregistroa" "ACK erregistroa" -- GitLab From 6e45892add9814fe9b916127f12e44184ccc0f18 Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Fri, 16 Sep 2022 09:02:54 +0000 Subject: [PATCH 044/667] Add AtPhonebookTest Bug: 237467631 Test: atest AtPhonebookTest Change-Id: Ib8d761a03aea81c7d3766033261b8a78e87e5f87 (cherry picked from commit 4545604bd41ad1e6968d372d12b6fca0913dc907) --- .../android/bluetooth/hfp/AtPhonebook.java | 4 +- .../bluetooth/hfp/AtPhonebookTest.java | 177 ++++++++++++++++++ 2 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/hfp/AtPhonebookTest.java diff --git a/android/app/src/com/android/bluetooth/hfp/AtPhonebook.java b/android/app/src/com/android/bluetooth/hfp/AtPhonebook.java index 12f1c4c40eb..56c9989aa1d 100644 --- a/android/app/src/com/android/bluetooth/hfp/AtPhonebook.java +++ b/android/app/src/com/android/bluetooth/hfp/AtPhonebook.java @@ -36,6 +36,7 @@ import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.util.DevicePolicyUtils; import com.android.bluetooth.util.GsmAlphabet; +import com.android.internal.annotations.VisibleForTesting; import java.util.HashMap; @@ -632,7 +633,8 @@ public class AtPhonebook { * @return {@link BluetoothDevice#ACCESS_UNKNOWN}, {@link BluetoothDevice#ACCESS_ALLOWED} or * {@link BluetoothDevice#ACCESS_REJECTED}. */ - private int checkAccessPermission(BluetoothDevice remoteDevice) { + @VisibleForTesting + int checkAccessPermission(BluetoothDevice remoteDevice) { log("checkAccessPermission"); int permission = remoteDevice.getPhonebookAccessPermission(); diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/AtPhonebookTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/AtPhonebookTest.java new file mode 100644 index 00000000000..40606c8e6a3 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/AtPhonebookTest.java @@ -0,0 +1,177 @@ +/* + * 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. + */ + +package com.android.bluetooth.hfp; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.TestUtils; +import com.android.bluetooth.btservice.AdapterService; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class AtPhonebookTest { + private static final String INVALID_COMMAND = "invalid_command"; + private Context mTargetContext; + private BluetoothAdapter mAdapter; + private BluetoothDevice mTestDevice; + + @Mock + private AdapterService mAdapterService; + private HeadsetNativeInterface mNativeInterface; + private AtPhonebook mAtPhonebook; + + @Before + public void setUp() throws Exception { + mTargetContext = InstrumentationRegistry.getTargetContext(); + MockitoAnnotations.initMocks(this); + TestUtils.setAdapterService(mAdapterService); + + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); + // Spy on native interface + mNativeInterface = spy(HeadsetNativeInterface.getInstance()); + mAtPhonebook = new AtPhonebook(mTargetContext, mNativeInterface); + } + + @After + public void tearDown() throws Exception { + TestUtils.clearAdapterService(mAdapterService); + } + + @Test + public void checkAccessPermission_returnsCorrectPermission() { + assertThat(mAtPhonebook.checkAccessPermission(mTestDevice)).isEqualTo( + BluetoothDevice.ACCESS_UNKNOWN); + } + + @Test + public void getAndSetCheckingAccessPermission_setCorrectly() { + mAtPhonebook.setCheckingAccessPermission(true); + assertThat(mAtPhonebook.getCheckingAccessPermission()).isTrue(); + } + + @Test + public void handleCscsCommand() { + mAtPhonebook.handleCscsCommand(INVALID_COMMAND, AtPhonebook.TYPE_READ, mTestDevice); + verify(mNativeInterface).atResponseString(mTestDevice, + "+CSCS: \"" + "UTF-8" + "\""); + + mAtPhonebook.handleCscsCommand(INVALID_COMMAND, AtPhonebook.TYPE_TEST, mTestDevice); + verify(mNativeInterface).atResponseString(mTestDevice, + "+CSCS: (\"UTF-8\",\"IRA\",\"GSM\")"); + + mAtPhonebook.handleCscsCommand(INVALID_COMMAND, AtPhonebook.TYPE_SET, mTestDevice); + verify(mNativeInterface, atLeastOnce()).atResponseCode(mTestDevice, + HeadsetHalConstants.AT_RESPONSE_ERROR, -1); + + mAtPhonebook.handleCscsCommand("command=GSM", AtPhonebook.TYPE_SET, mTestDevice); + verify(mNativeInterface, atLeastOnce()).atResponseCode(mTestDevice, + HeadsetHalConstants.AT_RESPONSE_OK, -1); + + mAtPhonebook.handleCscsCommand("command=ERR", AtPhonebook.TYPE_SET, mTestDevice); + verify(mNativeInterface).atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, + BluetoothCmeError.OPERATION_NOT_SUPPORTED); + + mAtPhonebook.handleCscsCommand(INVALID_COMMAND, AtPhonebook.TYPE_UNKNOWN, mTestDevice); + verify(mNativeInterface).atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, + BluetoothCmeError.TEXT_HAS_INVALID_CHARS); + } + + @Test + public void handleCpbsCommand() { + mAtPhonebook.handleCpbsCommand(INVALID_COMMAND, AtPhonebook.TYPE_READ, mTestDevice); + verify(mNativeInterface).atResponseString(mTestDevice, + "+CPBS: \"" + "ME" + "\"," + 0 + "," + 256); + + mAtPhonebook.handleCpbsCommand(INVALID_COMMAND, AtPhonebook.TYPE_TEST, mTestDevice); + verify(mNativeInterface).atResponseString(mTestDevice, + "+CPBS: (\"ME\",\"SM\",\"DC\",\"RC\",\"MC\")"); + + mAtPhonebook.handleCpbsCommand(INVALID_COMMAND, AtPhonebook.TYPE_SET, mTestDevice); + verify(mNativeInterface).atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, + BluetoothCmeError.OPERATION_NOT_SUPPORTED); + + mAtPhonebook.handleCpbsCommand("command=ERR", AtPhonebook.TYPE_SET, mTestDevice); + verify(mNativeInterface).atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, + BluetoothCmeError.OPERATION_NOT_ALLOWED); + + mAtPhonebook.handleCpbsCommand("command=SM", AtPhonebook.TYPE_SET, mTestDevice); + verify(mNativeInterface, atLeastOnce()).atResponseCode(mTestDevice, + HeadsetHalConstants.AT_RESPONSE_OK, -1); + + mAtPhonebook.handleCpbsCommand(INVALID_COMMAND, AtPhonebook.TYPE_UNKNOWN, mTestDevice); + verify(mNativeInterface).atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, + BluetoothCmeError.TEXT_HAS_INVALID_CHARS); + } + + @Test + public void handleCpbrCommand() { + mAtPhonebook.handleCpbrCommand(INVALID_COMMAND, AtPhonebook.TYPE_TEST, mTestDevice); + verify(mNativeInterface).atResponseString(mTestDevice, "+CPBR: (1-" + 1 + "),30,30"); + verify(mNativeInterface).atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_OK, + -1); + + mAtPhonebook.handleCpbrCommand(INVALID_COMMAND, AtPhonebook.TYPE_SET, mTestDevice); + verify(mNativeInterface).atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, + -1); + + mAtPhonebook.handleCpbrCommand("command=ERR", AtPhonebook.TYPE_SET, mTestDevice); + verify(mNativeInterface).atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, + BluetoothCmeError.TEXT_HAS_INVALID_CHARS); + + mAtPhonebook.handleCpbrCommand("command=123,123", AtPhonebook.TYPE_SET, mTestDevice); + assertThat(mAtPhonebook.getCheckingAccessPermission()).isTrue(); + + mAtPhonebook.handleCpbrCommand(INVALID_COMMAND, AtPhonebook.TYPE_UNKNOWN, mTestDevice); + verify(mNativeInterface, atLeastOnce()).atResponseCode(mTestDevice, + HeadsetHalConstants.AT_RESPONSE_ERROR, BluetoothCmeError.TEXT_HAS_INVALID_CHARS); + } + + @Test + public void processCpbrCommand() { + mAtPhonebook.handleCpbsCommand("command=SM", AtPhonebook.TYPE_SET, mTestDevice); + assertThat(mAtPhonebook.processCpbrCommand(mTestDevice)).isEqualTo( + HeadsetHalConstants.AT_RESPONSE_OK); + + mAtPhonebook.handleCpbsCommand("command=ME", AtPhonebook.TYPE_SET, mTestDevice); + assertThat(mAtPhonebook.processCpbrCommand(mTestDevice)).isEqualTo( + HeadsetHalConstants.AT_RESPONSE_OK); + } + + @Test + public void resetAtState() { + mAtPhonebook.resetAtState(); + assertThat(mAtPhonebook.getCheckingAccessPermission()).isFalse(); + } +} \ No newline at end of file -- GitLab From 7b55edce18f41ca9b939645fa25bbade9500254e Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Fri, 30 Sep 2022 09:16:23 +0000 Subject: [PATCH 045/667] Add BluetoothMapFolderElementTest Bug: 237467631 Test: atest BluetoothMapFolderElementTest Change-Id: I50e889666a8dcc7d4ca02f6ae04bce703c1b919b (cherry picked from commit 5a87f3e4516b6f48c65181e9a0281d18c3f68ea8) --- .../map/BluetoothMapFolderElement.java | 2 +- .../bluetooth/map/BluetoothMapObexServer.java | 4 +- .../map/BluetoothMapFolderElementTest.java | 184 ++++++++++++++++++ 3 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapFolderElementTest.java diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapFolderElement.java b/android/app/src/com/android/bluetooth/map/BluetoothMapFolderElement.java index bc327268d30..de8525f2ca3 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapFolderElement.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapFolderElement.java @@ -58,7 +58,7 @@ public class BluetoothMapFolderElement implements Comparable(); } - public void setIngore(boolean ignore) { + public void setIgnore(boolean ignore) { mIgnore = ignore; } diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java index 0c770626953..911fc915545 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java @@ -1163,7 +1163,7 @@ public class BluetoothMapObexServer extends ServerRequestHandler { // If messageHandle or convoId filtering ignore folder Log.v(TAG, "sendMessageListingRsp: ignore folder "); folderToList = mCurrentFolder.getRoot(); - folderToList.setIngore(true); + folderToList.setIgnore(true); } else { folderToList = getFolderElementFromName(folderName); if (folderToList == null) { @@ -1207,7 +1207,7 @@ public class BluetoothMapObexServer extends ServerRequestHandler { outAppParams.setMessageListingSize(listSize); op.noBodyHeader(); } - folderToList.setIngore(false); + folderToList.setIgnore(false); // Build the application parameter header // let the peer know if there are unread messages in the list if (hasUnread) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapFolderElementTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapFolderElementTest.java new file mode 100644 index 00000000000..d6f7ff9d8b2 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapFolderElementTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bluetooth.map; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class BluetoothMapFolderElementTest { + private static final boolean TEST_HAS_SMS_MMS_CONTENT = true; + private static final boolean TEST_HAS_IM_CONTENT = true; + private static final boolean TEST_HAS_EMAIL_CONTENT = true; + private static final boolean TEST_IGNORE = true; + + private static final String TEST_SMS_MMS_FOLDER_NAME = "smsmms"; + private static final String TEST_IM_FOLDER_NAME = "im"; + private static final String TEST_EMAIL_FOLDER_NAME = "email"; + private static final String TEST_TELECOM_FOLDER_NAME = "telecom"; + private static final String TEST_MSG_FOLDER_NAME = "msg"; + private static final String TEST_PLACEHOLDER_FOLDER_NAME = "placeholder"; + + private static final long TEST_ROOT_FOLDER_ID = 1; + private static final long TEST_PARENT_FOLDER_ID = 2; + private static final long TEST_FOLDER_ID = 3; + private static final long TEST_IM_FOLDER_ID = 4; + private static final long TEST_EMAIL_FOLDER_ID = 5; + private static final long TEST_PLACEHOLDER_ID = 6; + + private static final String TEST_FOLDER_NAME = "test"; + private static final String TEST_PARENT_FOLDER_NAME = "parent"; + private static final String TEST_ROOT_FOLDER_NAME = "root"; + + private final BluetoothMapFolderElement mRootFolderElement = + new BluetoothMapFolderElement(TEST_ROOT_FOLDER_NAME, null); + + private BluetoothMapFolderElement mParentFolderElement; + private BluetoothMapFolderElement mTestFolderElement; + + + @Before + public void setUp() throws Exception { + mRootFolderElement.setFolderId(TEST_ROOT_FOLDER_ID); + mRootFolderElement.addFolder(TEST_PARENT_FOLDER_NAME); + + mParentFolderElement = mRootFolderElement.getSubFolder(TEST_PARENT_FOLDER_NAME); + mParentFolderElement.setFolderId(TEST_PARENT_FOLDER_ID); + mParentFolderElement.addFolder(TEST_FOLDER_NAME); + + mTestFolderElement = mParentFolderElement.getSubFolder(TEST_FOLDER_NAME); + mTestFolderElement.setFolderId(TEST_FOLDER_ID); + mTestFolderElement.setIgnore(TEST_IGNORE); + mTestFolderElement.setHasSmsMmsContent(TEST_HAS_SMS_MMS_CONTENT); + mTestFolderElement.setHasEmailContent(TEST_HAS_EMAIL_CONTENT); + mTestFolderElement.setHasImContent(TEST_HAS_IM_CONTENT); + } + + + @Test + public void getters() { + assertThat(mTestFolderElement.shouldIgnore()).isEqualTo(TEST_IGNORE); + assertThat(mTestFolderElement.getFolderId()).isEqualTo(TEST_FOLDER_ID); + assertThat(mTestFolderElement.hasSmsMmsContent()).isEqualTo(TEST_HAS_SMS_MMS_CONTENT); + assertThat(mTestFolderElement.hasEmailContent()).isEqualTo(TEST_HAS_EMAIL_CONTENT); + assertThat(mTestFolderElement.hasImContent()).isEqualTo(TEST_HAS_IM_CONTENT); + } + + @Test + public void getFullPath() { + assertThat(mTestFolderElement.getFullPath()).isEqualTo( + String.format("%s/%s", TEST_PARENT_FOLDER_NAME, TEST_FOLDER_NAME)); + } + + @Test + public void getRoot() { + assertThat(mTestFolderElement.getRoot()).isEqualTo(mRootFolderElement); + } + + @Test + public void addFolders() { + mTestFolderElement.addSmsMmsFolder(TEST_SMS_MMS_FOLDER_NAME); + mTestFolderElement.addImFolder(TEST_IM_FOLDER_NAME, TEST_IM_FOLDER_ID); + mTestFolderElement.addEmailFolder(TEST_EMAIL_FOLDER_NAME, TEST_EMAIL_FOLDER_ID); + + assertThat(mTestFolderElement.getSubFolder(TEST_SMS_MMS_FOLDER_NAME).getName()).isEqualTo( + TEST_SMS_MMS_FOLDER_NAME); + assertThat(mTestFolderElement.getSubFolder(TEST_IM_FOLDER_NAME).getName()).isEqualTo( + TEST_IM_FOLDER_NAME); + assertThat(mTestFolderElement.getSubFolder(TEST_EMAIL_FOLDER_NAME).getName()).isEqualTo( + TEST_EMAIL_FOLDER_NAME); + + mTestFolderElement.addFolder(TEST_SMS_MMS_FOLDER_NAME); + assertThat(mTestFolderElement.getSubFolderCount()).isEqualTo(3); + } + + @Test + public void getFolderById() { + assertThat(mTestFolderElement.getFolderById(TEST_FOLDER_ID)).isEqualTo(mTestFolderElement); + assertThat(mRootFolderElement.getFolderById(TEST_ROOT_FOLDER_ID)).isEqualTo( + mRootFolderElement); + assertThat(BluetoothMapFolderElement.getFolderById(TEST_FOLDER_ID, null)).isNull(); + assertThat(BluetoothMapFolderElement.getFolderById(TEST_PLACEHOLDER_ID, + mTestFolderElement)).isNull(); + } + + @Test + public void getFolderByName() { + mRootFolderElement.addFolder(TEST_TELECOM_FOLDER_NAME); + mRootFolderElement.getSubFolder(TEST_TELECOM_FOLDER_NAME).addFolder(TEST_MSG_FOLDER_NAME); + BluetoothMapFolderElement placeholderFolderElement = mRootFolderElement.getSubFolder( + TEST_TELECOM_FOLDER_NAME).getSubFolder(TEST_MSG_FOLDER_NAME).addFolder( + TEST_PLACEHOLDER_FOLDER_NAME); + assertThat(mRootFolderElement.getFolderByName(TEST_PLACEHOLDER_FOLDER_NAME)).isNull(); + placeholderFolderElement.setFolderId(TEST_PLACEHOLDER_ID); + assertThat(mRootFolderElement.getFolderByName(TEST_PLACEHOLDER_FOLDER_NAME)).isEqualTo( + placeholderFolderElement); + } + + @Test + public void compareTo_withNull_returnsOne() { + assertThat(mTestFolderElement.compareTo(null)).isEqualTo(1); + } + + @Test + public void compareTo_withDifferentName_returnsCharacterDifference() { + assertThat(mTestFolderElement.compareTo(mParentFolderElement)).isEqualTo(4); + } + + @Test + public void compareTo_withSameSubFolders_returnsZero() { + BluetoothMapFolderElement folderElementWithSameSubFolders = + new BluetoothMapFolderElement(TEST_FOLDER_NAME, mParentFolderElement); + + mTestFolderElement.addSmsMmsFolder(TEST_SMS_MMS_FOLDER_NAME); + folderElementWithSameSubFolders.addSmsMmsFolder(TEST_SMS_MMS_FOLDER_NAME); + assertThat(mTestFolderElement.compareTo(folderElementWithSameSubFolders)).isEqualTo(0); + } + + @Test + public void compareTo_withDifferentSubFoldersSize_returnsSizeDifference() { + BluetoothMapFolderElement folderElementWithDifferentSubFoldersSize = + new BluetoothMapFolderElement(TEST_FOLDER_NAME, mParentFolderElement); + + mTestFolderElement.addSmsMmsFolder(TEST_SMS_MMS_FOLDER_NAME); + folderElementWithDifferentSubFoldersSize.addSmsMmsFolder(TEST_SMS_MMS_FOLDER_NAME); + folderElementWithDifferentSubFoldersSize.addImFolder(TEST_IM_FOLDER_NAME, + TEST_IM_FOLDER_ID); + assertThat( + mTestFolderElement.compareTo(folderElementWithDifferentSubFoldersSize)).isEqualTo( + -1); + } + + @Test + public void compareTo_withDifferentSubFolderTree_returnsCompareToRecursively() { + BluetoothMapFolderElement folderElementWithDifferentSubFoldersTree = + new BluetoothMapFolderElement(TEST_FOLDER_NAME, mParentFolderElement); + + mTestFolderElement.addSmsMmsFolder(TEST_SMS_MMS_FOLDER_NAME); + folderElementWithDifferentSubFoldersTree.addSmsMmsFolder(TEST_SMS_MMS_FOLDER_NAME); + folderElementWithDifferentSubFoldersTree.getSubFolder(TEST_SMS_MMS_FOLDER_NAME).addFolder( + TEST_PLACEHOLDER_FOLDER_NAME); + assertThat( + mTestFolderElement.compareTo(folderElementWithDifferentSubFoldersTree)).isEqualTo( + -1); + } +} -- GitLab From 6117493fed1f939e03f249dc582b9af696218e79 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Fri, 16 Sep 2022 14:24:07 +0900 Subject: [PATCH 046/667] Add PbapClientConnectionHandlerTest Bug: 237467631 Test: atest PbapClientConnectionHandlerTest (Need to enable pbapclient profile before test) Change-Id: Ie0e893b34be608ffe1f2086058444086c910e21f (cherry picked from commit e2fa87bcf57d1097813ee40adaae1084680989ee) Merged-In: Ie0e893b34be608ffe1f2086058444086c910e21f --- .../PbapClientConnectionHandler.java | 63 +++-- .../PbapClientConnectionHandlerTest.java | 252 ++++++++++++++++++ 2 files changed, 286 insertions(+), 29 deletions(-) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandlerTest.java diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java index 70621d9797e..ce5ccc9ab18 100644 --- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java +++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java @@ -17,7 +17,6 @@ package com.android.bluetooth.pbapclient; import android.accounts.Account; import android.accounts.AccountManager; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; @@ -32,6 +31,7 @@ import android.util.Log; import com.android.bluetooth.BluetoothObexTransport; import com.android.bluetooth.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.obex.ClientSession; import com.android.obex.HeaderSet; import com.android.obex.ResponseCodes; @@ -102,7 +102,9 @@ class PbapClientConnectionHandler extends Handler { private static final long PBAP_REQUESTED_FIELDS = PBAP_FILTER_VERSION | PBAP_FILTER_FN | PBAP_FILTER_N | PBAP_FILTER_PHOTO | PBAP_FILTER_ADR | PBAP_FILTER_EMAIL | PBAP_FILTER_TEL | PBAP_FILTER_NICKNAME; - private static final int L2CAP_INVALID_PSM = -1; + + @VisibleForTesting + static final int L2CAP_INVALID_PSM = -1; public static final String PB_PATH = "telecom/pb.vcf"; public static final String FAV_PATH = "telecom/fav.vcf"; @@ -126,7 +128,6 @@ class PbapClientConnectionHandler extends Handler { private Account mAccount; private AccountManager mAccountManager; private BluetoothSocket mSocket; - private final BluetoothAdapter mAdapter; private final BluetoothDevice mDevice; // PSE SDP Record for current device. private SdpPseRecord mPseRec = null; @@ -136,19 +137,6 @@ class PbapClientConnectionHandler extends Handler { private final PbapClientStateMachine mPbapClientStateMachine; private boolean mAccountCreated; - PbapClientConnectionHandler(Looper looper, Context context, PbapClientStateMachine stateMachine, - BluetoothDevice device) { - super(looper); - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mDevice = device; - mContext = context; - mPbapClientStateMachine = stateMachine; - mAuth = new BluetoothPbapObexAuthenticator(this); - mAccountManager = AccountManager.get(mPbapClientStateMachine.getContext()); - mAccount = - new Account(mDevice.getAddress(), mContext.getString(R.string.pbap_account_type)); - } - /** * Constructs PCEConnectionHandler object * @@ -156,7 +144,6 @@ class PbapClientConnectionHandler extends Handler { */ PbapClientConnectionHandler(Builder pceHandlerbuild) { super(pceHandlerbuild.mLooper); - mAdapter = BluetoothAdapter.getDefaultAdapter(); mDevice = pceHandlerbuild.mDevice; mContext = pceHandlerbuild.mContext; mPbapClientStateMachine = pceHandlerbuild.mClientStateMachine; @@ -252,8 +239,8 @@ class PbapClientConnectionHandler extends Handler { if (DBG) { Log.d(TAG, "Completing Disconnect"); } - removeAccount(mAccount); - removeCallLog(mAccount); + removeAccount(); + removeCallLog(); mPbapClientStateMachine.sendMessage(PbapClientStateMachine.MSG_CONNECTION_CLOSED); break; @@ -286,9 +273,20 @@ class PbapClientConnectionHandler extends Handler { return; } + @VisibleForTesting + synchronized void setPseRecord(SdpPseRecord record) { + mPseRec = record; + } + + @VisibleForTesting + synchronized BluetoothSocket getSocket() { + return mSocket; + } + /* Utilize SDP, if available, to create a socket connection over L2CAP, RFCOMM specified * channel, or RFCOMM default channel. */ - private synchronized boolean connectSocket() { + @VisibleForTesting + synchronized boolean connectSocket() { try { /* Use BluetoothSocket to connect */ if (mPseRec == null) { @@ -318,7 +316,8 @@ class PbapClientConnectionHandler extends Handler { /* Connect an OBEX session over the already connected socket. First establish an OBEX Transport * abstraction, then establish a Bluetooth Authenticator, and finally issue the connect call */ - private boolean connectObexSession() { + @VisibleForTesting + boolean connectObexSession() { boolean connectionSuccessful = false; try { @@ -357,13 +356,13 @@ class PbapClientConnectionHandler extends Handler { // Will get NPE if a null mSocket is passed to BluetoothObexTransport. // mSocket can be set to null if an abort() --> closeSocket() was called between // the calls to connectSocket() and connectObexSession(). - Log.w(TAG, "CONNECT Failure " + e.toString()); + Log.w(TAG, "CONNECT Failure ", e); closeSocket(); } return connectionSuccessful; } - public void abort() { + void abort() { // Perform forced cleanup, it is ok if the handler throws an exception this will free the // handler to complete what it is doing and finish with cleanup. closeSocket(); @@ -385,6 +384,7 @@ class PbapClientConnectionHandler extends Handler { } } + @VisibleForTesting void downloadContacts(String path) { try { PhonebookPullRequest processor = @@ -438,6 +438,7 @@ class PbapClientConnectionHandler extends Handler { } } + @VisibleForTesting void downloadCallLog(String path, HashMap callCounter) { try { BluetoothPbapRequestPullPhoneBook request = @@ -453,7 +454,8 @@ class PbapClientConnectionHandler extends Handler { } } - private boolean addAccount(Account account) { + @VisibleForTesting + boolean addAccount(Account account) { if (mAccountManager.addAccountExplicitly(account, null, null)) { if (DBG) { Log.d(TAG, "Added account " + mAccount); @@ -463,17 +465,19 @@ class PbapClientConnectionHandler extends Handler { return false; } - private void removeAccount(Account account) { - if (mAccountManager.removeAccountExplicitly(account)) { + @VisibleForTesting + void removeAccount() { + if (mAccountManager.removeAccountExplicitly(mAccount)) { if (DBG) { - Log.d(TAG, "Removed account " + account); + Log.d(TAG, "Removed account " + mAccount); } } else { Log.e(TAG, "Failed to remove account " + mAccount); } } - private void removeCallLog(Account account) { + @VisibleForTesting + void removeCallLog() { try { // need to check call table is exist ? if (mContext.getContentResolver() == null) { @@ -489,7 +493,8 @@ class PbapClientConnectionHandler extends Handler { } } - private boolean isRepositorySupported(int mask) { + @VisibleForTesting + boolean isRepositorySupported(int mask) { if (mPseRec == null) { if (VDBG) Log.v(TAG, "No PBAP Server SDP Record"); return false; diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandlerTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandlerTest.java new file mode 100644 index 00000000000..dc83c978a46 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandlerTest.java @@ -0,0 +1,252 @@ +/* + * 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. + */ + +package com.android.bluetooth.pbapclient; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.accounts.Account; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.SdpPseRecord; +import android.content.ContentResolver; +import android.content.Context; +import android.content.ContextWrapper; +import android.os.HandlerThread; +import android.os.Looper; +import android.util.Log; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ServiceTestRule; +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.TestUtils; +import com.android.bluetooth.btservice.AdapterService; +import com.android.bluetooth.btservice.storage.DatabaseManager; + +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashMap; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PbapClientConnectionHandlerTest { + + private static final String TAG = "ConnHandlerTest"; + private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00"; + + private HandlerThread mThread; + private Looper mLooper; + private Context mTargetContext; + private BluetoothDevice mRemoteDevice; + + @Rule + public final ServiceTestRule mServiceRule = new ServiceTestRule(); + + @Mock + private AdapterService mAdapterService; + + @Mock + private DatabaseManager mDatabaseManager; + + private BluetoothAdapter mAdapter; + + private PbapClientService mService; + + private PbapClientStateMachine mStateMachine; + + private PbapClientConnectionHandler mHandler; + + @Before + public void setUp() throws Exception { + mTargetContext = spy(new ContextWrapper( + InstrumentationRegistry.getInstrumentation().getTargetContext())); + Assume.assumeTrue("Ignore test when PbapClientService is not enabled", + PbapClientService.isEnabled()); + MockitoAnnotations.initMocks(this); + TestUtils.setAdapterService(mAdapterService); + doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); + doReturn(true, false).when(mAdapterService) + .isStartedProfile(anyString()); + TestUtils.startService(mServiceRule, PbapClientService.class); + mService = PbapClientService.getPbapClientService(); + assertThat(mService).isNotNull(); + + mAdapter = BluetoothAdapter.getDefaultAdapter(); + + mThread = new HandlerThread("test_handler_thread"); + mThread.start(); + mLooper = mThread.getLooper(); + mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS); + + mStateMachine = new PbapClientStateMachine(mService, mRemoteDevice); + mHandler = new PbapClientConnectionHandler.Builder() + .setLooper(mLooper) + .setClientSM(mStateMachine) + .setContext(mTargetContext) + .setRemoteDevice(mRemoteDevice) + .build(); + } + + @After + public void tearDown() throws Exception { + if (!PbapClientService.isEnabled()) { + return; + } + TestUtils.stopService(mServiceRule, PbapClientService.class); + mService = PbapClientService.getPbapClientService(); + assertThat(mService).isNull(); + TestUtils.clearAdapterService(mAdapterService); + mLooper.quit(); + } + + @Test + public void connectSocket_whenBluetoothIsNotEnabled_returnsFalse() { + assertThat(mHandler.connectSocket()).isFalse(); + } + + @Test + public void connectSocket_whenBluetoothIsNotEnabled_returnsFalse_withInvalidL2capPsm() { + SdpPseRecord record = mock(SdpPseRecord.class); + mHandler.setPseRecord(record); + + when(record.getL2capPsm()).thenReturn(PbapClientConnectionHandler.L2CAP_INVALID_PSM); + assertThat(mHandler.connectSocket()).isFalse(); + } + + @Test + public void connectSocket_whenBluetoothIsNotEnabled_returnsFalse_withValidL2capPsm() { + SdpPseRecord record = mock(SdpPseRecord.class); + mHandler.setPseRecord(record); + + when(record.getL2capPsm()).thenReturn(1); // Valid PSM ranges 1 to 30; + assertThat(mHandler.connectSocket()).isFalse(); + } + + // TODO: Add connectObexSession_returnsTrue + + @Test + public void connectObexSession_returnsFalse_withoutConnectingSocket() { + assertThat(mHandler.connectObexSession()).isFalse(); + } + + @Test + public void abort() { + SdpPseRecord record = mock(SdpPseRecord.class); + when(record.getL2capPsm()).thenReturn(1); // Valid PSM ranges 1 to 30; + mHandler.setPseRecord(record); + mHandler.connectSocket(); // Workaround for setting mSocket as non-null value + assertThat(mHandler.getSocket()).isNotNull(); + + mHandler.abort(); + + assertThat(mThread.isInterrupted()).isTrue(); + assertThat(mHandler.getSocket()).isNull(); + } + + @Test + public void downloadContacts() { + final String path = PbapClientConnectionHandler.PB_PATH; + + try { + mHandler.downloadContacts(path); + } catch (Exception e) { + Log.e(TAG, "Exception happened.", e); + assertWithMessage("Exception should not be thrown!").fail(); + } + } + + @Test + public void downloadCallLog() { + final String path = PbapClientConnectionHandler.ICH_PATH; + final HashMap callCounter = new HashMap<>(); + + try { + mHandler.downloadCallLog(path, callCounter); + } catch (Exception e) { + Log.e(TAG, "Exception happened.", e); + assertWithMessage("Exception should not be thrown!").fail(); + } + } + + @Test + public void addAccount() { + try { + mHandler.addAccount(mock(Account.class)); + } catch (Exception e) { + Log.e(TAG, "Exception happened.", e); + assertWithMessage("Exception should not be thrown!").fail(); + } + } + + @Test + public void removeAccount() { + try { + mHandler.removeAccount(); + } catch (Exception e) { + Log.e(TAG, "Exception happened.", e); + assertWithMessage("Exception should not be thrown!").fail(); + } + } + + @Test + public void removeCallLog() { + try { + ContentResolver res = mock(ContentResolver.class); + when(mTargetContext.getContentResolver()).thenReturn(res); + mHandler.removeCallLog(); + + when(mTargetContext.getContentResolver()).thenReturn(null); + mHandler.removeCallLog(); + } catch (Exception e) { + Log.e(TAG, "Exception happened.", e); + assertWithMessage("Exception should not be thrown!").fail(); + } + } + + @Test + public void isRepositorySupported_withoutSettingPseRecord_returnsFalse() { + mHandler.setPseRecord(null); + final int mask = 0x11; + + assertThat(mHandler.isRepositorySupported(mask)).isFalse(); + } + + @Test + public void isRepositorySupported_withSettingPseRecord() { + SdpPseRecord record = mock(SdpPseRecord.class); + when(record.getSupportedRepositories()).thenReturn(1); + mHandler.setPseRecord(record); + final int mask = 0x11; + + assertThat(mHandler.isRepositorySupported(mask)).isTrue(); + } +} -- GitLab From d681f5644dff1f620e252cab162becf700136606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Mon, 10 Oct 2022 21:41:47 +0000 Subject: [PATCH 047/667] leaudio: Don't adjust metadata when choosing configuration Le audio code maintain configuration_context_type_ and metadata_context_types_. The configuration_context_type_ is choosen based on the metadata_context_types_, which contains a list of all the mixed streams. This patch fixes that. Bug: 251803434 Test: atest BluetoothInstrumentationTests Test manual: Media stream + notification - make sure configuratin is not changed Tag: #feature Merged-In: Icc5b134abe39170882705a0f0bb261eb300eaf76 Change-Id: Icc5b134abe39170882705a0f0bb261eb300eaf76 (cherry picked from commit 871373875aad649a997229eb2b07a7b2b1981867) --- system/bta/le_audio/client.cc | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index e1fe5f902bf..4ee9f7fcb95 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -3337,22 +3337,20 @@ class LeAudioClientImpl : public LeAudioClient { return LeAudioContextType::UNSPECIFIED; } - auto adjusted_contexts = adjustMetadataContexts(available_contexts); - using T = std::underlying_type::type; /* Mini policy. Voice is prio 1, game prio 2, media is prio 3 */ - if ((adjusted_contexts & + if ((available_contexts & AudioContexts(static_cast(LeAudioContextType::CONVERSATIONAL))) .any()) return LeAudioContextType::CONVERSATIONAL; - if ((adjusted_contexts & + if ((available_contexts & AudioContexts(static_cast(LeAudioContextType::GAME))) .any()) return LeAudioContextType::GAME; - if ((adjusted_contexts & + if ((available_contexts & AudioContexts(static_cast(LeAudioContextType::RINGTONE))) .any()) { if (!in_call_) { @@ -3361,7 +3359,7 @@ class LeAudioClientImpl : public LeAudioClient { return LeAudioContextType::RINGTONE; } - if ((adjusted_contexts & + if ((available_contexts & AudioContexts(static_cast(LeAudioContextType::MEDIA))) .any()) return LeAudioContextType::MEDIA; @@ -3369,8 +3367,8 @@ class LeAudioClientImpl : public LeAudioClient { /*TODO do something smarter here */ /* Get context for the first non-zero bit */ uint16_t context_type = 0b1; - while (adjusted_contexts != 0b1) { - adjusted_contexts = adjusted_contexts >> 1; + while (available_contexts != 0b1) { + available_contexts = available_contexts >> 1; context_type = context_type << 1; } @@ -3428,7 +3426,7 @@ class LeAudioClientImpl : public LeAudioClient { (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); if (audio_receiver_state_ == AudioState::STARTED) { - /* If the receiver is starte. Take into account current context type */ + /* If the receiver is started. Take into account current context type */ metadata_context_types_ = adjustMetadataContexts(metadata_context_types_); } else { metadata_context_types_ = 0; @@ -3478,12 +3476,12 @@ class LeAudioClientImpl : public LeAudioClient { if (new_configuration_context == configuration_context_type_) { LOG_INFO("Context did not changed."); - return; - } - configuration_context_type_ = new_configuration_context; - if (StopStreamIfNeeded(group, new_configuration_context)) { - return; + } else { + configuration_context_type_ = new_configuration_context; + if (StopStreamIfNeeded(group, new_configuration_context)) { + return; + } } if (is_group_streaming) { -- GitLab From af6c4fd15559288ea0139baf3617d105154e5658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Tue, 11 Oct 2022 17:01:06 +0000 Subject: [PATCH 048/667] leaudio: Fix crash when data path is removed while ACL is disconnected Fixes race condition. Bug: 253034131 Test: atest BluetoothInstrumentationTests Test: manual - initiate disconnection of one earbud Tag: #feature Merged-In: I49709207790104ffa03e419205e7bda9c7802241 Change-Id: I49709207790104ffa03e419205e7bda9c7802241 (cherry picked from commit 69fbad85a45261c7c7c1ee757f2fcb07478c399a) --- system/bta/le_audio/client.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index 4ee9f7fcb95..9f6ff28a4c2 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -3690,6 +3690,12 @@ class LeAudioClientImpl : public LeAudioClient { uint8_t cig_id) { LeAudioDevice* leAudioDevice = leAudioDevices_.FindByCisConnHdl(cig_id, conn_handle); + /* In case device has been disconnected before data path was setup */ + if (!leAudioDevice) { + LOG_WARN("Device for CIG %d and using cis_handle 0x%04x is disconnected.", + cig_id, conn_handle); + return; + } LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_); instance->groupStateMachine_->ProcessHciNotifSetupIsoDataPath( @@ -3700,6 +3706,17 @@ class LeAudioClientImpl : public LeAudioClient { uint8_t cig_id) { LeAudioDevice* leAudioDevice = leAudioDevices_.FindByCisConnHdl(cig_id, conn_handle); + + /* If CIS has been disconnected just before ACL being disconnected by the + * remote device, leAudioDevice might be already cleared i.e. has no + * information about conn_handle, when the data path remove compete arrives. + */ + if (!leAudioDevice) { + LOG_WARN("Device for CIG %d and using cis_handle 0x%04x is disconnected.", + cig_id, conn_handle); + return; + } + LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_); instance->groupStateMachine_->ProcessHciNotifRemoveIsoDataPath( -- GitLab From a806e4e87c28a98fc6ad39cc9b78c47134e5249d Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Wed, 28 Sep 2022 08:58:34 +0000 Subject: [PATCH 049/667] Add BluetoothMapConvoListingElementTest Bug: 237467631 Test: atest BluetoothMapConvoListingElementTest Change-Id: I99bbcd8801ef05d9e3de4dfd3387b2bf90db011b (cherry picked from commit 983d2abea3d88b071c133de9dd68bf7833a34a6f) --- .../BluetoothMapConvoListingElementTest.java | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java new file mode 100644 index 00000000000..84b0d7fac24 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java @@ -0,0 +1,192 @@ +/* + * 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. + */ + +package com.android.bluetooth.map; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.SignedLongLong; +import com.android.bluetooth.map.BluetoothMapUtils.TYPE; +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; +import org.xmlpull.v1.XmlSerializer; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class BluetoothMapConvoListingElementTest { + private static final long TEST_ID = 1111; + private static final String TEST_NAME = "test_name"; + private static final long TEST_LAST_ACTIVITY = 0; + private static final boolean TEST_READ = true; + private static final boolean TEST_REPORT_READ = true; + private static final long TEST_VERSION_COUNTER = 0; + private static final int TEST_CURSOR_INDEX = 1; + private static final TYPE TEST_TYPE = TYPE.EMAIL; + private static final String TEST_SUMMARY = "test_summary"; + private static final String TEST_SMS_MMS_CONTACTS = "test_sms_mms_contacts"; + + private final BluetoothMapConvoContactElement TEST_CONTACT_ELEMENT_ONE = + new BluetoothMapConvoContactElement("test_uci_one", "test_name_one", + "test_display_name_one", "test_presence_status_one", 2, TEST_LAST_ACTIVITY, 2, + 1, "1111"); + + private final BluetoothMapConvoContactElement TEST_CONTACT_ELEMENT_TWO = + new BluetoothMapConvoContactElement("test_uci_two", "test_name_two", + "test_display_name_two", "test_presence_status_two", 1, TEST_LAST_ACTIVITY, 1, + 2, "1112"); + + private final List TEST_CONTACTS = new ArrayList<>( + Arrays.asList(TEST_CONTACT_ELEMENT_ONE, TEST_CONTACT_ELEMENT_TWO)); + + private final SignedLongLong signedLongLong = new SignedLongLong(TEST_ID, 0); + + private BluetoothMapConvoListingElement mListingElement; + + @Before + public void setUp() throws Exception { + mListingElement = new BluetoothMapConvoListingElement(); + + mListingElement.setCursorIndex(TEST_CURSOR_INDEX); + mListingElement.setVersionCounter(TEST_VERSION_COUNTER); + mListingElement.setName(TEST_NAME); + mListingElement.setType(TEST_TYPE); + mListingElement.setContacts(TEST_CONTACTS); + mListingElement.setLastActivity(TEST_LAST_ACTIVITY); + mListingElement.setRead(TEST_READ, TEST_REPORT_READ); + mListingElement.setConvoId(0, TEST_ID); + mListingElement.setSummary(TEST_SUMMARY); + mListingElement.setSmsMmsContacts(TEST_SMS_MMS_CONTACTS); + } + + @Test + public void getters() throws Exception { + assertThat(mListingElement.getCursorIndex()).isEqualTo(TEST_CURSOR_INDEX); + assertThat(mListingElement.getVersionCounter()).isEqualTo(TEST_VERSION_COUNTER); + assertThat(mListingElement.getName()).isEqualTo(TEST_NAME); + assertThat(mListingElement.getType()).isEqualTo(TEST_TYPE); + assertThat(mListingElement.getContacts()).isEqualTo(TEST_CONTACTS); + assertThat(mListingElement.getLastActivity()).isEqualTo(TEST_LAST_ACTIVITY); + assertThat(mListingElement.getRead()).isEqualTo("READ"); + assertThat(mListingElement.getReadBool()).isEqualTo(TEST_READ); + assertThat(mListingElement.getConvoId()).isEqualTo(signedLongLong.toHexString()); + assertThat(mListingElement.getCpConvoId()).isEqualTo( + signedLongLong.getLeastSignificantBits()); + assertThat(mListingElement.getFullSummary()).isEqualTo(TEST_SUMMARY); + assertThat(mListingElement.getSmsMmsContacts()).isEqualTo(TEST_SMS_MMS_CONTACTS); + } + + @Test + public void incrementVersionCounter() { + mListingElement.incrementVersionCounter(); + assertThat(mListingElement.getVersionCounter()).isEqualTo(TEST_VERSION_COUNTER + 1); + } + + @Test + public void removeContactWithObject() { + mListingElement.removeContact(TEST_CONTACT_ELEMENT_TWO); + assertThat(mListingElement.getContacts().size()).isEqualTo(1); + } + + @Test + public void removeContactWithIndex() { + mListingElement.removeContact(1); + assertThat(mListingElement.getContacts().size()).isEqualTo(1); + } + + @Test + public void encodeToXml_thenDecodeToInstance_returnsCorrectly() throws Exception { + final XmlSerializer serializer = new FastXmlSerializer(); + final StringWriter writer = new StringWriter(); + + serializer.setOutput(writer); + serializer.startDocument("UTF-8", true); + mListingElement.encode(serializer); + serializer.endDocument(); + + final XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance(); + parserFactory.setNamespaceAware(true); + final XmlPullParser parser; + parser = parserFactory.newPullParser(); + + parser.setInput(new StringReader(writer.toString())); + parser.next(); + + BluetoothMapConvoListingElement listingElementFromXml = + BluetoothMapConvoListingElement.createFromXml(parser); + + assertThat(listingElementFromXml.getVersionCounter()).isEqualTo(0); + assertThat(listingElementFromXml.getName()).isEqualTo(TEST_NAME); + assertThat(listingElementFromXml.getContacts()).isEqualTo(TEST_CONTACTS); + assertThat(listingElementFromXml.getLastActivity()).isEqualTo(TEST_LAST_ACTIVITY); + assertThat(listingElementFromXml.getRead()).isEqualTo("UNREAD"); + assertThat(listingElementFromXml.getConvoId()).isEqualTo(signedLongLong.toHexString()); + assertThat(listingElementFromXml.getFullSummary().trim()).isEqualTo(TEST_SUMMARY); + } + + @Test + public void equalsWithSameValues_returnsTrue() { + BluetoothMapConvoListingElement listingElement = new BluetoothMapConvoListingElement(); + listingElement.setName(TEST_NAME); + listingElement.setContacts(TEST_CONTACTS); + listingElement.setLastActivity(TEST_LAST_ACTIVITY); + listingElement.setRead(TEST_READ, TEST_REPORT_READ); + + BluetoothMapConvoListingElement listingElementEqual = new BluetoothMapConvoListingElement(); + listingElementEqual.setName(TEST_NAME); + listingElementEqual.setContacts(TEST_CONTACTS); + listingElementEqual.setLastActivity(TEST_LAST_ACTIVITY); + listingElementEqual.setRead(TEST_READ, TEST_REPORT_READ); + + assertThat(listingElement).isEqualTo(listingElementEqual); + } + + @Test + public void equalsWithDifferentRead_returnsFalse() { + BluetoothMapConvoListingElement + listingElement = new BluetoothMapConvoListingElement(); + + BluetoothMapConvoListingElement listingElementWithDifferentRead = + new BluetoothMapConvoListingElement(); + listingElementWithDifferentRead.setRead(TEST_READ, TEST_REPORT_READ); + + assertThat(listingElement).isNotEqualTo(listingElementWithDifferentRead); + } + + @Test + public void compareToWithSameValues_returnsZero() { + BluetoothMapConvoListingElement + listingElement = new BluetoothMapConvoListingElement(); + listingElement.setLastActivity(TEST_LAST_ACTIVITY); + + BluetoothMapConvoListingElement listingElementSameLastActivity = + new BluetoothMapConvoListingElement(); + listingElementSameLastActivity.setLastActivity(TEST_LAST_ACTIVITY); + + assertThat(listingElement.compareTo(listingElementSameLastActivity)).isEqualTo(0); + } +} \ No newline at end of file -- GitLab From 4d432ca2b535b0b1710306bcb07ec88c8d72eea5 Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Thu, 6 Oct 2022 08:35:58 +0000 Subject: [PATCH 050/667] Add BluetoothMapConvoListingTest Bug: 237467631 Test: atest BluetoothMapConvoListingTest Change-Id: I35139613693311e11b8af344c94036bd81919478 (cherry picked from commit 983150c682e94ddb23c9fc4cdb948dbad385d96d) --- .../map/BluetoothMapConvoListingTest.java | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java new file mode 100644 index 00000000000..432506b35c9 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java @@ -0,0 +1,179 @@ +/* + * 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. + */ + +package com.android.bluetooth.map; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.SignedLongLong; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +@RunWith(AndroidJUnit4.class) +public class BluetoothMapConvoListingTest { + private static final long TEST_LAST_ACTIVITY_EARLIEST = 0; + private static final long TEST_LAST_ACTIVITY_MIDDLE = 1; + private static final long TEST_LAST_ACTIVITY_LATEST = 2; + private static final boolean TEST_READ = true; + private static final boolean TEST_REPORT_READ = true; + + private BluetoothMapConvoListingElement mListingElementEarliestWithReadFalse; + private BluetoothMapConvoListingElement mListingElementMiddleWithReadFalse; + private BluetoothMapConvoListingElement mListingElementLatestWithReadTrue; + private BluetoothMapConvoListing mListing; + + @Before + public void setUp() { + mListingElementEarliestWithReadFalse = new BluetoothMapConvoListingElement(); + mListingElementEarliestWithReadFalse.setLastActivity(TEST_LAST_ACTIVITY_EARLIEST); + + mListingElementMiddleWithReadFalse = new BluetoothMapConvoListingElement(); + mListingElementMiddleWithReadFalse.setLastActivity(TEST_LAST_ACTIVITY_MIDDLE); + + mListingElementLatestWithReadTrue = new BluetoothMapConvoListingElement(); + mListingElementLatestWithReadTrue.setLastActivity(TEST_LAST_ACTIVITY_LATEST); + mListingElementLatestWithReadTrue.setRead(TEST_READ, TEST_REPORT_READ); + + mListing = new BluetoothMapConvoListing(); + mListing.add(mListingElementEarliestWithReadFalse); + mListing.add(mListingElementMiddleWithReadFalse); + mListing.add(mListingElementLatestWithReadTrue); + } + + @Test + public void addElement() { + final BluetoothMapConvoListing listing = new BluetoothMapConvoListing(); + assertThat(listing.getCount()).isEqualTo(0); + listing.add(mListingElementLatestWithReadTrue); + assertThat(listing.getCount()).isEqualTo(1); + assertThat(listing.hasUnread()).isEqualTo(true); + } + + @Test + public void segment_whenCountIsLessThanOne_returnsOffsetToEnd() { + mListing.segment(0, 1); + assertThat(mListing.getList().size()).isEqualTo(2); + } + + @Test + public void segment_whenOffsetIsBiggerThanSize_returnsEmptyList() { + mListing.segment(1, 4); + assertThat(mListing.getList().size()).isEqualTo(0); + } + + @Test + public void segment_whenOffsetCountCombinationIsValid_returnsCorrectly() { + mListing.segment(1, 1); + assertThat(mListing.getList().size()).isEqualTo(1); + } + + @Test + public void sort() { + // BluetoothMapConvoListingElements are sorted according to their mLastActivity values + mListing.sort(); + assertThat(mListing.getList().get(0).getLastActivity()).isEqualTo( + TEST_LAST_ACTIVITY_LATEST); + } + + @Test + public void equals_withSameObject_returnsTrue() { + assertThat(mListing.equals(mListing)).isEqualTo(true); + } + + @Test + public void equals_withNull_returnsFalse() { + assertThat(mListing.equals(null)).isEqualTo(false); + } + + @Test + public void equals_withDifferentClass_returnsFalse() { + assertThat(mListing.equals(mListingElementEarliestWithReadFalse)).isEqualTo(false); + } + + @Test + public void equals_withDifferentRead_returnsFalse() { + final BluetoothMapConvoListing listingWithDifferentRead = new BluetoothMapConvoListing(); + assertThat(mListing.equals(listingWithDifferentRead)).isEqualTo(false); + } + + @Test + public void equals_whenNullComparedWithNonNullList_returnsFalse() { + final BluetoothMapConvoListing listingWithNullList = new BluetoothMapConvoListing(); + final BluetoothMapConvoListing listingWithNonNullList = new BluetoothMapConvoListing(); + listingWithNonNullList.add(mListingElementEarliestWithReadFalse); + + assertThat(listingWithNullList.equals(listingWithNonNullList)).isEqualTo(false); + } + + @Test + public void equals_whenNonNullListsAreDifferent_returnsFalse() { + final BluetoothMapConvoListing listingWithListSizeOne = new BluetoothMapConvoListing(); + listingWithListSizeOne.add(mListingElementEarliestWithReadFalse); + + final BluetoothMapConvoListing listingWithListSizeTwo = new BluetoothMapConvoListing(); + listingWithListSizeTwo.add(mListingElementEarliestWithReadFalse); + listingWithListSizeTwo.add(mListingElementMiddleWithReadFalse); + + assertThat(listingWithListSizeOne.equals(listingWithListSizeTwo)).isEqualTo(false); + } + + @Test + public void equals_whenNonNullListsAreTheSame_returnsTrue() { + final BluetoothMapConvoListing listing = new BluetoothMapConvoListing(); + final BluetoothMapConvoListing listingEqual = new BluetoothMapConvoListing(); + listing.add(mListingElementEarliestWithReadFalse); + listingEqual.add(mListingElementEarliestWithReadFalse); + assertThat(listing.equals(listingEqual)).isEqualTo(true); + } + + @Test + public void encodeToXml_thenAppendFromXml() throws Exception { + final BluetoothMapConvoListing listingToAppend = new BluetoothMapConvoListing(); + final BluetoothMapConvoListingElement listingElementToAppendOne = + new BluetoothMapConvoListingElement(); + final BluetoothMapConvoListingElement listingElementToAppendTwo = + new BluetoothMapConvoListingElement(); + + final long testIdOne = 1111; + final long testIdTwo = 1112; + + final SignedLongLong signedLongLongIdOne = new SignedLongLong(testIdOne, 0); + final SignedLongLong signedLongLongIdTwo = new SignedLongLong(testIdTwo, 0); + + listingElementToAppendOne.setConvoId(0, testIdOne); + listingElementToAppendTwo.setConvoId(0, testIdTwo); + + listingToAppend.add(listingElementToAppendOne); + listingToAppend.add(listingElementToAppendTwo); + + final InputStream listingStream = new ByteArrayInputStream(listingToAppend.encode()); + + BluetoothMapConvoListing listing = new BluetoothMapConvoListing(); + listing.appendFromXml(listingStream); + assertThat(listing.getList().size()).isEqualTo(2); + assertThat(listing.getList().get(0).getConvoId()).isEqualTo( + signedLongLongIdOne.toHexString()); + assertThat(listing.getList().get(1).getConvoId()).isEqualTo( + signedLongLongIdTwo.toHexString()); + } +} \ No newline at end of file -- GitLab From 8965b12122e10fb6eb985a70b67ece66cf42fa06 Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Thu, 6 Oct 2022 04:29:48 +0000 Subject: [PATCH 051/667] Add BluetoothMapMessageListingElementTest Bug: 237467631 Test: atest BluetoothMapMessageListingElementTest Change-Id: I14f7f8cf05a20a501b433dbf386f7e7410b16d3e (cherry picked from commit bea63b6c81419b0045abb55b6133fba9c43bb2fb) --- ...BluetoothMapMessageListingElementTest.java | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingElementTest.java diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingElementTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingElementTest.java new file mode 100644 index 00000000000..a918f3257e3 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingElementTest.java @@ -0,0 +1,231 @@ +/* + * 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. + */ + +package com.android.bluetooth.map; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.map.BluetoothMapUtils.TYPE; +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; +import org.xmlpull.v1.XmlSerializer; + +import java.io.StringReader; +import java.io.StringWriter; +import java.text.SimpleDateFormat; + +@RunWith(AndroidJUnit4.class) +public class BluetoothMapMessageListingElementTest { + private static final long TEST_CP_HANDLE = 1; + private static final String TEST_SUBJECT = "test_subject"; + private static final long TEST_DATE_TIME = 2; + private static final String TEST_SENDER_NAME = "test_sender_name"; + private static final String TEST_SENDER_ADDRESSING = "test_sender_addressing"; + private static final String TEST_REPLY_TO_ADDRESSING = "test_reply_to_addressing"; + private static final String TEST_RECIPIENT_NAME = "test_recipient_name"; + private static final String TEST_RECIPIENT_ADDRESSING = "test_recipient_addressing"; + private static final TYPE TEST_TYPE = TYPE.EMAIL; + private static final boolean TEST_MSG_TYPE_APP_PARAM_SET = true; + private static final int TEST_SIZE = 0; + private static final String TEST_TEXT = "test_text"; + private static final String TEST_RECEPTION_STATUS = "test_reception_status"; + private static final String TEST_DELIVERY_STATUS = "test_delivery_status"; + private static final int TEST_ATTACHMENT_SIZE = 0; + private static final String TEST_PRIORITY = "test_priority"; + private static final boolean TEST_READ = true; + private static final String TEST_SENT = "test_sent"; + private static final String TEST_PROTECT = "test_protect"; + private static final String TEST_FOLDER_TYPE = "test_folder_type"; + private static final long TEST_THREAD_ID = 1; + private static final String TEST_THREAD_NAME = "test_thread_name"; + private static final String TEST_ATTACHMENT_MIME_TYPES = "test_attachment_mime_types"; + private static final boolean TEST_REPORT_READ = true; + private static final int TEST_CURSOR_INDEX = 1; + + private final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); + + private BluetoothMapMessageListingElement mMessageListingElement; + + @Before + public void setUp() throws Exception { + mMessageListingElement = new BluetoothMapMessageListingElement(); + + mMessageListingElement.setHandle(TEST_CP_HANDLE); + mMessageListingElement.setSubject(TEST_SUBJECT); + mMessageListingElement.setDateTime(TEST_DATE_TIME); + mMessageListingElement.setSenderName(TEST_SENDER_NAME); + mMessageListingElement.setSenderAddressing(TEST_SENDER_ADDRESSING); + mMessageListingElement.setReplytoAddressing(TEST_REPLY_TO_ADDRESSING); + mMessageListingElement.setRecipientName(TEST_RECIPIENT_NAME); + mMessageListingElement.setRecipientAddressing(TEST_RECIPIENT_ADDRESSING); + mMessageListingElement.setType(TEST_TYPE, TEST_MSG_TYPE_APP_PARAM_SET); + mMessageListingElement.setSize(TEST_SIZE); + mMessageListingElement.setText(TEST_TEXT); + mMessageListingElement.setReceptionStatus(TEST_RECEPTION_STATUS); + mMessageListingElement.setDeliveryStatus(TEST_DELIVERY_STATUS); + mMessageListingElement.setAttachmentSize(TEST_ATTACHMENT_SIZE); + mMessageListingElement.setPriority(TEST_PRIORITY); + mMessageListingElement.setRead(TEST_READ, TEST_REPORT_READ); + mMessageListingElement.setSent(TEST_SENT); + mMessageListingElement.setProtect(TEST_PROTECT); + mMessageListingElement.setFolderType(TEST_FOLDER_TYPE); + mMessageListingElement.setThreadId(TEST_THREAD_ID, TEST_TYPE); + mMessageListingElement.setThreadName(TEST_THREAD_NAME); + mMessageListingElement.setAttachmentMimeTypes(TEST_ATTACHMENT_MIME_TYPES); + mMessageListingElement.setCursorIndex(TEST_CURSOR_INDEX); + } + + @Test + public void getters() { + assertThat(mMessageListingElement.getHandle()).isEqualTo(TEST_CP_HANDLE); + assertThat(mMessageListingElement.getSubject()).isEqualTo(TEST_SUBJECT); + assertThat(mMessageListingElement.getDateTime()).isEqualTo(TEST_DATE_TIME); + assertThat(mMessageListingElement.getDateTimeString()).isEqualTo( + format.format(TEST_DATE_TIME)); + assertThat(mMessageListingElement.getSenderName()).isEqualTo(TEST_SENDER_NAME); + assertThat(mMessageListingElement.getSenderAddressing()).isEqualTo(TEST_SENDER_ADDRESSING); + assertThat(mMessageListingElement.getReplyToAddressing()).isEqualTo( + TEST_REPLY_TO_ADDRESSING); + assertThat(mMessageListingElement.getRecipientName()).isEqualTo(TEST_RECIPIENT_NAME); + assertThat(mMessageListingElement.getRecipientAddressing()).isEqualTo( + TEST_RECIPIENT_ADDRESSING); + assertThat(mMessageListingElement.getType()).isEqualTo(TEST_TYPE); + assertThat(mMessageListingElement.getSize()).isEqualTo(TEST_SIZE); + assertThat(mMessageListingElement.getText()).isEqualTo(TEST_TEXT); + assertThat(mMessageListingElement.getReceptionStatus()).isEqualTo(TEST_RECEPTION_STATUS); + assertThat(mMessageListingElement.getDeliveryStatus()).isEqualTo(TEST_DELIVERY_STATUS); + assertThat(mMessageListingElement.getAttachmentSize()).isEqualTo(TEST_ATTACHMENT_SIZE); + assertThat(mMessageListingElement.getPriority()).isEqualTo(TEST_PRIORITY); + assertThat(mMessageListingElement.getRead()).isEqualTo("yes"); + assertThat(mMessageListingElement.getReadBool()).isEqualTo(TEST_READ); + assertThat(mMessageListingElement.getSent()).isEqualTo(TEST_SENT); + assertThat(mMessageListingElement.getProtect()).isEqualTo(TEST_PROTECT); + assertThat(mMessageListingElement.getFolderType()).isEqualTo(TEST_FOLDER_TYPE); + assertThat(mMessageListingElement.getThreadName()).isEqualTo(TEST_THREAD_NAME); + assertThat(mMessageListingElement.getAttachmentMimeTypes()).isEqualTo( + TEST_ATTACHMENT_MIME_TYPES); + assertThat(mMessageListingElement.getCursorIndex()).isEqualTo(TEST_CURSOR_INDEX); + } + + @Test + public void encode() throws Exception { + mMessageListingElement.setSubject(null); + + final XmlSerializer serializer = new FastXmlSerializer(); + final StringWriter writer = new StringWriter(); + + serializer.setOutput(writer); + serializer.startDocument("UTF-8", true); + mMessageListingElement.encode(serializer, true); + serializer.endDocument(); + + final XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance(); + parserFactory.setNamespaceAware(true); + final XmlPullParser parser; + parser = parserFactory.newPullParser(); + + parser.setInput(new StringReader(writer.toString())); + parser.next(); + + int count = parser.getAttributeCount(); + assertThat(count).isEqualTo(21); + + for (int i = 0; i < count; i++) { + String attributeName = parser.getAttributeName(i).trim(); + String attributeValue = parser.getAttributeValue(i); + if (attributeName.equalsIgnoreCase("handle")) { + assertThat(attributeValue).isEqualTo( + BluetoothMapUtils.getMapHandle(TEST_CP_HANDLE, TEST_TYPE)); + } else if (attributeName.equalsIgnoreCase("datetime")) { + assertThat(attributeValue).isEqualTo( + BluetoothMapUtils.getDateTimeString(TEST_DATE_TIME)); + } else if (attributeName.equalsIgnoreCase("sender_name")) { + assertThat(attributeValue).isEqualTo( + BluetoothMapUtils.stripInvalidChars(TEST_SENDER_NAME)); + } else if (attributeName.equalsIgnoreCase("sender_addressing")) { + assertThat(attributeValue).isEqualTo(TEST_SENDER_ADDRESSING); + } else if (attributeName.equalsIgnoreCase("replyto_addressing")) { + assertThat(attributeValue).isEqualTo(TEST_REPLY_TO_ADDRESSING); + } else if (attributeName.equalsIgnoreCase("recipient_name")) { + assertThat(attributeValue).isEqualTo(TEST_RECIPIENT_NAME); + } else if (attributeName.equalsIgnoreCase("recipient_addressing")) { + assertThat(attributeValue).isEqualTo(TEST_RECIPIENT_ADDRESSING); + } else if (attributeName.equalsIgnoreCase("type")) { + assertThat(attributeValue).isEqualTo(TEST_TYPE.name()); + } else if (attributeName.equalsIgnoreCase("size")) { + assertThat(attributeValue).isEqualTo(Integer.toString(TEST_SIZE)); + } else if (attributeName.equalsIgnoreCase("text")) { + assertThat(attributeValue).isEqualTo(TEST_TEXT); + } else if (attributeName.equalsIgnoreCase("reception_status")) { + assertThat(attributeValue).isEqualTo(TEST_RECEPTION_STATUS); + } else if (attributeName.equalsIgnoreCase("delivery_status")) { + assertThat(attributeValue).isEqualTo(TEST_DELIVERY_STATUS); + } else if (attributeName.equalsIgnoreCase("attachment_size")) { + assertThat(attributeValue).isEqualTo(Integer.toString(TEST_ATTACHMENT_SIZE)); + } else if (attributeName.equalsIgnoreCase("attachment_mime_types")) { + assertThat(attributeValue).isEqualTo(TEST_ATTACHMENT_MIME_TYPES); + } else if (attributeName.equalsIgnoreCase("priority")) { + assertThat(attributeValue).isEqualTo(TEST_PRIORITY); + } else if (attributeName.equalsIgnoreCase("read")) { + assertThat(attributeValue).isEqualTo(mMessageListingElement.getRead()); + } else if (attributeName.equalsIgnoreCase("sent")) { + assertThat(attributeValue).isEqualTo(TEST_SENT); + } else if (attributeName.equalsIgnoreCase("protected")) { + assertThat(attributeValue).isEqualTo(TEST_PROTECT); + } else if (attributeName.equalsIgnoreCase("conversation_id")) { + assertThat(attributeValue).isEqualTo( + BluetoothMapUtils.getMapConvoHandle(TEST_THREAD_ID, TEST_TYPE)); + } else if (attributeName.equalsIgnoreCase("conversation_name")) { + assertThat(attributeValue).isEqualTo(TEST_THREAD_NAME); + } else if (attributeName.equalsIgnoreCase("folder_type")) { + assertThat(attributeValue).isEqualTo(TEST_FOLDER_TYPE); + } else { + throw new Exception("Test fails with unknown XML attribute"); + } + } + } + + @Test + public void compareTo_withLaterDateTime_ReturnsOne() { + BluetoothMapMessageListingElement elementWithLaterDateTime = + new BluetoothMapMessageListingElement(); + elementWithLaterDateTime.setDateTime(TEST_DATE_TIME + 1); + assertThat(mMessageListingElement.compareTo(elementWithLaterDateTime)).isEqualTo(1); + } + + @Test + public void compareTo_withFasterDateTime_ReturnsNegativeOne() { + BluetoothMapMessageListingElement elementWithFasterDateTime = + new BluetoothMapMessageListingElement(); + elementWithFasterDateTime.setDateTime(TEST_DATE_TIME - 1); + assertThat(mMessageListingElement.compareTo(elementWithFasterDateTime)).isEqualTo(-1); + } + + @Test + public void compareTo_withEqualDateTime_ReturnsZero() { + BluetoothMapMessageListingElement elementWithEqualDateTime = + new BluetoothMapMessageListingElement(); + elementWithEqualDateTime.setDateTime(TEST_DATE_TIME); + assertThat(mMessageListingElement.compareTo(elementWithEqualDateTime)).isEqualTo(0); + } +} \ No newline at end of file -- GitLab From ed8388dc8bf00f9da0599e247a8aff7e91bad90f Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Fri, 7 Oct 2022 08:53:40 +0000 Subject: [PATCH 052/667] Add BluetoothMapbMessageVCardTest Bug: 237467631 Test: atest BluetoothMapbMessageVCardTest Change-Id: Ic831b8b535b36eef8c2439c0867b222d09d5f5ad (cherry picked from commit 820904f53e1dde7e93324d7fd89beea04195d596) --- .../bluetooth/map/BluetoothMapbMessage.java | 4 +- .../map/BluetoothMapbMessageVCardTest.java | 119 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageVCardTest.java diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapbMessage.java b/android/app/src/com/android/bluetooth/map/BluetoothMapbMessage.java index e222aa9c707..3ed5c60d273 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapbMessage.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapbMessage.java @@ -19,6 +19,7 @@ import android.telephony.PhoneNumberUtils; import android.util.Log; import com.android.bluetooth.map.BluetoothMapUtils.TYPE; +import com.android.internal.annotations.VisibleForTesting; import java.io.ByteArrayOutputStream; import java.io.File; @@ -323,7 +324,8 @@ public abstract class BluetoothMapbMessage { ; - private static class BMsgReader { + @VisibleForTesting + static class BMsgReader { InputStream mInStream; BMsgReader(InputStream is) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageVCardTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageVCardTest.java new file mode 100644 index 00000000000..8f108f57d21 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageVCardTest.java @@ -0,0 +1,119 @@ +/* + * 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. + */ + +package com.android.bluetooth.map; + +import static com.google.common.truth.Truth.assertThat; + +import android.telephony.PhoneNumberUtils; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.map.BluetoothMapbMessage.BMsgReader; +import com.android.bluetooth.map.BluetoothMapbMessage.VCard; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +@RunWith(AndroidJUnit4.class) +public class BluetoothMapbMessageVCardTest { + private static final String TEST_NAME = "test_name"; + private static final String TEST_FORMATTED_NAME = "test_formatted_name"; + private static final String TEST_FIRST_PHONE_NUMBER = "111-1111-1111"; + private static final String[] TEST_PHONE_NUMBERS = + new String[]{TEST_FIRST_PHONE_NUMBER, "222-2222-2222"}; + private static final String TEST_FIRST_EMAIL = "testFirst@email.com"; + private static final String[] TEST_EMAIL_ADDRESSES = + new String[]{TEST_FIRST_EMAIL, "testSecond@email.com"}; + private static final String TEST_FIRST_BT_UCI = "test_first_bt_uci"; + private static final String[] TEST_BT_UCIS = + new String[]{TEST_FIRST_BT_UCI, "test_second_bt_uci"}; + private static final String TEST_FIRST_BT_UID = "1111"; + private static final String[] TEST_BT_UIDS = new String[]{TEST_FIRST_BT_UID, "1112"}; + private static final int TEST_ENV_LEVEL = 1; + + @Test + public void constructor_forVersionTwoPointOne() { + VCard vcard = new VCard(TEST_NAME, TEST_PHONE_NUMBERS, TEST_EMAIL_ADDRESSES); + assertThat(vcard.getName()).isEqualTo(TEST_NAME); + assertThat(vcard.getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(vcard.getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + } + + @Test + public void constructor_forVersionTwoPointOne_withEnvLevel() { + VCard vcard = new VCard(TEST_NAME, TEST_PHONE_NUMBERS, TEST_EMAIL_ADDRESSES, + TEST_ENV_LEVEL); + assertThat(vcard.getName()).isEqualTo(TEST_NAME); + assertThat(vcard.getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(vcard.getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + assertThat(vcard.getEnvLevel()).isEqualTo(TEST_ENV_LEVEL); + } + + @Test + public void constructor_forVersionThree() { + VCard vcard = new VCard(TEST_NAME, TEST_FORMATTED_NAME, TEST_PHONE_NUMBERS, + TEST_EMAIL_ADDRESSES, TEST_ENV_LEVEL); + assertThat(vcard.getName()).isEqualTo(TEST_NAME); + assertThat(vcard.getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(vcard.getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + assertThat(vcard.getEnvLevel()).isEqualTo(TEST_ENV_LEVEL); + } + + @Test + public void constructor_forVersionThree_withUcis() { + VCard vcard = new VCard(TEST_NAME, TEST_FORMATTED_NAME, TEST_PHONE_NUMBERS, + TEST_EMAIL_ADDRESSES, TEST_BT_UIDS, TEST_BT_UCIS); + assertThat(vcard.getName()).isEqualTo(TEST_NAME); + assertThat(vcard.getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(vcard.getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + assertThat(vcard.getFirstBtUci()).isEqualTo(TEST_FIRST_BT_UCI); + } + + @Test + public void getters_withInitWithNulls_returnsCorrectly() { + VCard vcard = new VCard(null, null, null); + assertThat(vcard.getName()).isEqualTo(""); + assertThat(vcard.getFirstPhoneNumber()).isNull(); + assertThat(vcard.getFirstEmail()).isNull(); + assertThat(vcard.getFirstBtUci()).isNull(); + assertThat(vcard.getFirstBtUid()).isNull(); + } + + @Test + public void encodeToStringBuilder_thenParseBackToVCard_returnsCorrectly() { + VCard vcardOriginal = new VCard(TEST_NAME, TEST_FORMATTED_NAME, TEST_PHONE_NUMBERS, + TEST_EMAIL_ADDRESSES, TEST_BT_UIDS, TEST_BT_UCIS); + StringBuilder stringBuilder = new StringBuilder(); + vcardOriginal.encode(stringBuilder); + InputStream inputStream = new ByteArrayInputStream(stringBuilder.toString().getBytes()); + + VCard vcardParsed = VCard.parseVcard(new BMsgReader(inputStream), TEST_ENV_LEVEL); + + assertThat(vcardParsed.getName()).isEqualTo(TEST_NAME); + assertThat(vcardParsed.getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(vcardParsed.getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + assertThat(vcardParsed.getEnvLevel()).isEqualTo(TEST_ENV_LEVEL); + } +} \ No newline at end of file -- GitLab From 572a11b3b7c427e1269d02596f4c80c80772a8ef Mon Sep 17 00:00:00 2001 From: Chienyuan Date: Wed, 6 Jul 2022 17:25:09 +0800 Subject: [PATCH 053/667] Check paused client by switch Bug: 238400713 Test: gd/cert/run Tag: #refactor Ignore-AOSP-First: Cherry-pick Merged-In: Ia2d031f1b949448c58afe1942d574ecf616dc0f2 Change-Id: Ia2d031f1b949448c58afe1942d574ecf616dc0f2 --- system/gd/hci/le_address_manager.cc | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/system/gd/hci/le_address_manager.cc b/system/gd/hci/le_address_manager.cc index f05050df4ea..af20e154d2c 100644 --- a/system/gd/hci/le_address_manager.cc +++ b/system/gd/hci/le_address_manager.cc @@ -223,9 +223,15 @@ AddressWithType LeAddressManager::GetAnotherAddress() { void LeAddressManager::pause_registered_clients() { for (auto& client : registered_clients_) { - if (client.second != ClientState::PAUSED && client.second != ClientState::WAITING_FOR_PAUSE) { - client.second = ClientState::WAITING_FOR_PAUSE; - client.first->OnPause(); + switch (client.second) { + case ClientState::PAUSED: + case ClientState::WAITING_FOR_PAUSE: + break; + case WAITING_FOR_RESUME: + case RESUMED: + client.second = ClientState::WAITING_FOR_PAUSE; + client.first->OnPause(); + break; } } } @@ -241,14 +247,19 @@ void LeAddressManager::ack_pause(LeAddressManagerCallback* callback) { } registered_clients_.find(callback)->second = ClientState::PAUSED; for (auto client : registered_clients_) { - if (client.second != ClientState::PAUSED) { - // make sure all client paused - if (client.second != ClientState::WAITING_FOR_PAUSE) { + switch (client.second) { + case ClientState::PAUSED: + break; + case ClientState::WAITING_FOR_PAUSE: + // make sure all client paused + LOG_DEBUG("Wait all clients paused, return"); + return; + case WAITING_FOR_RESUME: + case RESUMED: LOG_DEBUG("Trigger OnPause for client that not paused and not waiting for pause"); client.second = ClientState::WAITING_FOR_PAUSE; client.first->OnPause(); - } - return; + return; } } -- GitLab From 04ebca20bcfcf9cc01ee323225e4910dea624402 Mon Sep 17 00:00:00 2001 From: Chris Manton Date: Tue, 30 Aug 2022 13:00:15 -0700 Subject: [PATCH 054/667] le_impl: Add set privacy address unittest Bug: 242901573 Test: atest bluetooth_test_gd_unit bluetooth_test_gd Tag: #refactor BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines Ignore-AOSP-First: Cherry-pick Merged-In: I1431763a2f9193e70f351684329d1d57d2d4a031 Change-Id: I1431763a2f9193e70f351684329d1d57d2d4a031 --- system/gd/hci/acl_manager/le_impl_test.cc | 142 ++++++++++++++++++++-- system/gd/hci/le_address_manager.cc | 9 ++ 2 files changed, 143 insertions(+), 8 deletions(-) diff --git a/system/gd/hci/acl_manager/le_impl_test.cc b/system/gd/hci/acl_manager/le_impl_test.cc index 09caa42477d..d6bb8c9d4dd 100644 --- a/system/gd/hci/acl_manager/le_impl_test.cc +++ b/system/gd/hci/acl_manager/le_impl_test.cc @@ -20,9 +20,11 @@ #include #include +#include #include "common/bidi_queue.h" #include "common/callback.h" +#include "common/testing/log_capture.h" #include "hci/acl_manager.h" #include "hci/acl_manager/le_connection_management_callbacks.h" #include "hci/address_with_type.h" @@ -32,12 +34,27 @@ #include "os/log.h" #include "packet/raw_builder.h" +using namespace bluetooth; using namespace std::chrono_literals; using ::bluetooth::common::BidiQueue; using ::bluetooth::common::Callback; using ::bluetooth::os::Handler; using ::bluetooth::os::Thread; +using ::bluetooth::testing::LogCapture; + +namespace { +constexpr char kFixedAddress[] = "c0:aa:bb:cc:dd:ee"; +constexpr char kRemoteAddress[] = "00:11:22:33:44:55"; +[[maybe_unused]] constexpr bool kAddToFilterAcceptList = true; +[[maybe_unused]] constexpr bool kSkipFilterAcceptList = !kAddToFilterAcceptList; +[[maybe_unused]] constexpr bool kIsDirectConnection = true; +[[maybe_unused]] constexpr bool kIsBackgroundConnection = !kIsDirectConnection; +[[maybe_unused]] constexpr ::bluetooth::crypto_toolbox::Octet16 kRotationIrk = {}; +[[maybe_unused]] constexpr std::chrono::milliseconds kMinimumRotationTime(14 * 1000); +[[maybe_unused]] constexpr std::chrono::milliseconds kMaximumRotationTime(16 * 1000); + +} // namespace namespace bluetooth { namespace hci { @@ -101,6 +118,8 @@ class TestController : public Controller { }; class TestHciLayer : public HciLayer { + // This is a springboard class that converts from `AclCommandBuilder` + // to `ComandBuilder` for use in the hci layer. template class CommandInterfaceImpl : public CommandInterface { public: @@ -122,6 +141,7 @@ class TestHciLayer : public HciLayer { void EnqueueCommand( std::unique_ptr command, common::ContextualOnceCallback on_status) override { + const std::lock_guard lock(command_queue_mutex_); command_queue_.push(std::move(command)); command_status_callbacks.push_back(std::move(on_status)); if (command_promise_ != nullptr) { @@ -133,6 +153,7 @@ class TestHciLayer : public HciLayer { void EnqueueCommand( std::unique_ptr command, common::ContextualOnceCallback on_complete) override { + const std::lock_guard lock(command_queue_mutex_); command_queue_.push(std::move(command)); command_complete_callbacks.push_back(std::move(on_complete)); if (command_promise_ != nullptr) { @@ -141,6 +162,31 @@ class TestHciLayer : public HciLayer { } } + std::unique_ptr DequeueCommand() { + const std::lock_guard lock(command_queue_mutex_); + auto packet = std::move(command_queue_.front()); + command_queue_.pop(); + return std::move(packet); + } + + std::shared_ptr> DequeueCommandBytes() { + auto command = DequeueCommand(); + auto bytes = std::make_shared>(); + packet::BitInserter bi(*bytes); + command->Serialize(bi); + return bytes; + } + + bool IsPacketQueueEmpty() const { + const std::lock_guard lock(command_queue_mutex_); + return command_queue_.empty(); + } + + size_t NumberOfQueuedCommands() const { + const std::lock_guard lock(command_queue_mutex_); + return command_queue_.size(); + } + public: void SetCommandFuture() { ASSERT_LOG(command_promise_ == nullptr, "Promises, Promises, ... Only one at a time."); @@ -159,7 +205,7 @@ class TestHciLayer : public HciLayer { CommandView GetCommand(OpCode op_code) { if (!command_queue_.empty()) { - std::lock_guard lock(mutex_); + std::lock_guard lock(command_queue_mutex_); if (command_future_ != nullptr) { command_future_.reset(); command_promise_.reset(); @@ -168,7 +214,7 @@ class TestHciLayer : public HciLayer { auto result = command_future_->wait_for(std::chrono::milliseconds(1000)); EXPECT_NE(std::future_status::timeout, result); } - std::lock_guard lock(mutex_); + std::lock_guard lock(command_queue_mutex_); ASSERT_LOG( !command_queue_.empty(), "Expecting command %s but command queue was empty", OpCodeText(op_code).c_str()); CommandView command_packet_view = GetLastCommand(); @@ -222,9 +268,9 @@ class TestHciLayer : public HciLayer { std::list> command_status_callbacks; common::ContextualCallback le_event_handler_; std::queue> command_queue_; + mutable std::mutex command_queue_mutex_; std::unique_ptr> command_promise_; std::unique_ptr> command_future_; - mutable std::mutex mutex_; CommandInterfaceImpl le_acl_connection_manager_interface_{*this}; }; @@ -242,6 +288,15 @@ class LeImplTest : public ::testing::Test { le_impl_ = new le_impl(hci_layer_, controller_, handler_, round_robin_scheduler_, true); le_impl_->handle_register_le_callbacks(&mock_le_connection_callbacks_, handler_); + Address address; + Address::FromString(kFixedAddress, address); + fixed_address_ = AddressWithType(address, AddressType::PUBLIC_DEVICE_ADDRESS); + + Address::FromString(kRemoteAddress, address); + remote_public_address_ = AddressWithType(address, AddressType::PUBLIC_DEVICE_ADDRESS); + } + + void set_random_device_address_policy() { // Set address policy hci_layer_->SetCommandFuture(); hci::Address address; @@ -279,7 +334,7 @@ class LeImplTest : public ::testing::Test { std::promise promise; auto future = promise.get_future(); handler_->BindOnceOn(&promise, &std::promise::set_value).Invoke(); - auto status = future.wait_for(10ms); + auto status = future.wait_for(2s); ASSERT_EQ(status, std::future_status::ready); } @@ -313,6 +368,16 @@ class LeImplTest : public ::testing::Test { MOCK_METHOD(void, OnLeConnectFail, (AddressWithType, ErrorCode reason), (override)); } mock_le_connection_callbacks_; + protected: + void set_privacy_policy_for_initiator_address( + const AddressWithType& address, const LeAddressManager::AddressPolicy& policy) { + le_impl_->set_privacy_policy_for_initiator_address( + policy, address, kRotationIrk, kMinimumRotationTime, kMaximumRotationTime); + } + + AddressWithType fixed_address_; + AddressWithType remote_public_address_; + uint16_t packet_count_; std::unique_ptr> packet_promise_; std::unique_ptr> packet_future_; @@ -370,6 +435,8 @@ TEST_F(LeImplTest, remove_device_from_connect_list) { } TEST_F(LeImplTest, connection_complete_with_periperal_role) { + set_random_device_address_policy(); + // Create connection hci_layer_->SetCommandFuture(); le_impl_->create_le_connection( @@ -388,7 +455,7 @@ TEST_F(LeImplTest, connection_complete_with_periperal_role) { hci::Address remote_address; Address::FromString("D0:05:04:03:02:01", remote_address); hci::AddressWithType address_with_type(remote_address, hci::AddressType::PUBLIC_DEVICE_ADDRESS); - EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, testing::_)); + EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, ::testing::_)); hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, 0x0041, @@ -406,6 +473,8 @@ TEST_F(LeImplTest, connection_complete_with_periperal_role) { } TEST_F(LeImplTest, enhanced_connection_complete_with_periperal_role) { + set_random_device_address_policy(); + controller_->AddSupported(OpCode::LE_EXTENDED_CREATE_CONNECTION); // Create connection hci_layer_->SetCommandFuture(); @@ -425,7 +494,7 @@ TEST_F(LeImplTest, enhanced_connection_complete_with_periperal_role) { hci::Address remote_address; Address::FromString("D0:05:04:03:02:01", remote_address); hci::AddressWithType address_with_type(remote_address, hci::AddressType::PUBLIC_DEVICE_ADDRESS); - EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, testing::_)); + EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, ::testing::_)); hci_layer_->IncomingLeMetaEvent(LeEnhancedConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, 0x0041, @@ -445,6 +514,8 @@ TEST_F(LeImplTest, enhanced_connection_complete_with_periperal_role) { } TEST_F(LeImplTest, connection_complete_with_central_role) { + set_random_device_address_policy(); + hci::Address remote_address; Address::FromString("D0:05:04:03:02:01", remote_address); hci::AddressWithType address_with_type(remote_address, hci::AddressType::PUBLIC_DEVICE_ADDRESS); @@ -462,7 +533,7 @@ TEST_F(LeImplTest, connection_complete_with_central_role) { ASSERT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); // Receive connection complete of outgoing connection (Role::CENTRAL) - EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, testing::_)); + EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, ::testing::_)); hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, 0x0041, @@ -480,6 +551,8 @@ TEST_F(LeImplTest, connection_complete_with_central_role) { } TEST_F(LeImplTest, enhanced_connection_complete_with_central_role) { + set_random_device_address_policy(); + controller_->AddSupported(OpCode::LE_EXTENDED_CREATE_CONNECTION); hci::Address remote_address; Address::FromString("D0:05:04:03:02:01", remote_address); @@ -498,7 +571,7 @@ TEST_F(LeImplTest, enhanced_connection_complete_with_central_role) { ASSERT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); // Receive connection complete of outgoing connection (Role::CENTRAL) - EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, testing::_)); + EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, ::testing::_)); hci_layer_->IncomingLeMetaEvent(LeEnhancedConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, 0x0041, @@ -517,6 +590,59 @@ TEST_F(LeImplTest, enhanced_connection_complete_with_central_role) { ASSERT_EQ(ConnectabilityState::DISARMED, le_impl_->connectability_state_); } +TEST_F(LeImplTest, register_with_address_manager__AddressPolicyNotSet) { + bluetooth::common::InitFlags::SetAllForTesting(); + auto log_capture = std::make_unique(); + + std::promise promise; + auto future = promise.get_future(); + handler_->Post(common::BindOnce( + [](struct le_impl* le_impl, os::Handler* handler, std::promise promise) { + le_impl->register_with_address_manager(); + handler->Post(common::BindOnce([](std::promise promise) { promise.set_value(); }, std::move(promise))); + }, + le_impl_, + handler_, + std::move(promise))); + + // Let |LeAddressManager::register_client| execute on handler + auto status = future.wait_for(2s); + ASSERT_EQ(status, std::future_status::ready); + + handler_->Post(common::BindOnce( + [](struct le_impl* le_impl) { + ASSERT_TRUE(le_impl->address_manager_registered); + ASSERT_TRUE(le_impl->pause_connection); + }, + le_impl_)); + + std::promise promise2; + auto future2 = promise2.get_future(); + handler_->Post(common::BindOnce( + [](struct le_impl* le_impl, os::Handler* handler, std::promise promise) { + le_impl->ready_to_unregister = true; + le_impl->check_for_unregister(); + ASSERT_FALSE(le_impl->address_manager_registered); + ASSERT_FALSE(le_impl->pause_connection); + handler->Post(common::BindOnce([](std::promise promise) { promise.set_value(); }, std::move(promise))); + }, + le_impl_, + handler_, + std::move(promise2))); + + // Let |LeAddressManager::unregister_client| execute on handler + auto status2 = future2.wait_for(2s); + ASSERT_EQ(status2, std::future_status::ready); + + handler_->Post(common::BindOnce( + [](std::unique_ptr log_capture) { + log_capture->Sync(); + ASSERT_TRUE(log_capture->Rewind()->Find("address policy isn't set yet")); + ASSERT_TRUE(log_capture->Rewind()->Find("Client unregistered")); + }, + std::move(log_capture))); +} + } // namespace acl_manager } // namespace hci } // namespace bluetooth diff --git a/system/gd/hci/le_address_manager.cc b/system/gd/hci/le_address_manager.cc index af20e154d2c..3cbc04a86db 100644 --- a/system/gd/hci/le_address_manager.cc +++ b/system/gd/hci/le_address_manager.cc @@ -169,8 +169,10 @@ void LeAddressManager::register_client(LeAddressManagerCallback* callback) { address_policy_ == AddressPolicy::USE_NON_RESOLVABLE_ADDRESS) { if (registered_clients_.size() == 1) { schedule_rotate_random_address(); + LOG_INFO("Scheduled address rotation for first client registered"); } } + LOG_INFO("Client registered"); } void LeAddressManager::Unregister(LeAddressManagerCallback* callback) { @@ -185,9 +187,11 @@ void LeAddressManager::unregister_client(LeAddressManagerCallback* callback) { ack_resume(callback); } registered_clients_.erase(callback); + LOG_INFO("Client unregistered"); } if (registered_clients_.empty() && address_rotation_alarm_ != nullptr) { address_rotation_alarm_->Cancel(); + LOG_INFO("Cancelled address rotation alarm"); } } @@ -243,12 +247,14 @@ void LeAddressManager::push_command(Command command) { void LeAddressManager::ack_pause(LeAddressManagerCallback* callback) { if (registered_clients_.find(callback) == registered_clients_.end()) { + LOG_INFO("No clients registered to ack pause"); return; } registered_clients_.find(callback)->second = ClientState::PAUSED; for (auto client : registered_clients_) { switch (client.second) { case ClientState::PAUSED: + LOG_INFO("Client already in paused state"); break; case ClientState::WAITING_FOR_PAUSE: // make sure all client paused @@ -260,6 +266,8 @@ void LeAddressManager::ack_pause(LeAddressManagerCallback* callback) { client.second = ClientState::WAITING_FOR_PAUSE; client.first->OnPause(); return; + default: + LOG_ERROR("Found client in unexpected state:%u", client.second); } } @@ -275,6 +283,7 @@ void LeAddressManager::resume_registered_clients() { return; } + LOG_INFO("Resuming registered clients"); for (auto& client : registered_clients_) { client.second = ClientState::WAITING_FOR_RESUME; client.first->OnResume(); -- GitLab From 62cdb6a6d904195cddfb63a312734b844cb2ac18 Mon Sep 17 00:00:00 2001 From: Chris Manton Date: Thu, 1 Sep 2022 11:58:30 -0700 Subject: [PATCH 055/667] Extended le_impl capabilities for testing Bug: 242901573 Test: atest bluetooth_test_gd_unit bluetooth_test_gd Tag: #refactor BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines Ignore-AOSP-First: Cherry-pick Merged-In: I39fa842d8d5ba6ab56da127d9d1ca3c9e29c0ae8 Change-Id: I39fa842d8d5ba6ab56da127d9d1ca3c9e29c0ae8 --- system/gd/hci/acl_manager/le_impl_test.cc | 123 +++++++++++++++++++++- system/gd/hci/le_address_manager.h | 5 + 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/system/gd/hci/acl_manager/le_impl_test.cc b/system/gd/hci/acl_manager/le_impl_test.cc index d6bb8c9d4dd..404f0bf829e 100644 --- a/system/gd/hci/acl_manager/le_impl_test.cc +++ b/system/gd/hci/acl_manager/le_impl_test.cc @@ -26,12 +26,14 @@ #include "common/callback.h" #include "common/testing/log_capture.h" #include "hci/acl_manager.h" +#include "hci/acl_manager/le_connection_callbacks.h" #include "hci/acl_manager/le_connection_management_callbacks.h" #include "hci/address_with_type.h" #include "hci/controller.h" #include "hci/hci_packets.h" #include "os/handler.h" #include "os/log.h" +#include "packet/bit_inserter.h" #include "packet/raw_builder.h" using namespace bluetooth; @@ -41,6 +43,8 @@ using ::bluetooth::common::BidiQueue; using ::bluetooth::common::Callback; using ::bluetooth::os::Handler; using ::bluetooth::os::Thread; +using ::bluetooth::packet::BitInserter; +using ::bluetooth::packet::RawBuilder; using ::bluetooth::testing::LogCapture; namespace { @@ -53,6 +57,86 @@ constexpr char kRemoteAddress[] = "00:11:22:33:44:55"; [[maybe_unused]] constexpr ::bluetooth::crypto_toolbox::Octet16 kRotationIrk = {}; [[maybe_unused]] constexpr std::chrono::milliseconds kMinimumRotationTime(14 * 1000); [[maybe_unused]] constexpr std::chrono::milliseconds kMaximumRotationTime(16 * 1000); +[[maybe_unused]] constexpr std::array kPeerIdentityResolvingKey({ + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f, +}); +[[maybe_unused]] constexpr std::array kLocalIdentityResolvingKey({ + 0x80, + 0x81, + 0x82, + 0x83, + 0x84, + 0x85, + 0x86, + 0x87, + 0x88, + 0x89, + 0x8a, + 0x8b, + 0x8c, + 0x8d, + 0x8e, + 0x8f, +}); + +// Generic template for all commands +template +T CreateCommand(U u) { + T command; + return command; +} + +template <> +[[maybe_unused]] hci::LeSetAddressResolutionEnableView CreateCommand(std::shared_ptr> bytes) { + return hci::LeSetAddressResolutionEnableView::Create( + hci::LeSecurityCommandView::Create(hci::CommandView::Create(hci::PacketView(bytes)))); +} + +template <> +[[maybe_unused]] hci::LeAddDeviceToResolvingListView CreateCommand(std::shared_ptr> bytes) { + return hci::LeAddDeviceToResolvingListView::Create( + hci::LeSecurityCommandView::Create(hci::CommandView::Create(hci::PacketView(bytes)))); +} + +template <> +[[maybe_unused]] hci::LeSetPrivacyModeView CreateCommand(std::shared_ptr> bytes) { + return hci::LeSetPrivacyModeView::Create( + hci::LeSecurityCommandView::Create(hci::CommandView::Create(hci::PacketView(bytes)))); +} + +[[maybe_unused]] hci::CommandCompleteView ReturnCommandComplete(hci::OpCode op_code, hci::ErrorCode error_code) { + std::vector success_vector{static_cast(error_code)}; + auto bytes = std::make_shared>(); + BitInserter bi(*bytes); + auto builder = hci::CommandCompleteBuilder::Create(uint8_t{1}, op_code, std::make_unique(success_vector)); + builder->Serialize(bi); + return hci::CommandCompleteView::Create(hci::EventView::Create(hci::PacketView(bytes))); +} + +[[maybe_unused]] hci::CommandStatusView ReturnCommandStatus(hci::OpCode op_code, hci::ErrorCode error_code) { + std::vector success_vector{static_cast(error_code)}; + auto bytes = std::make_shared>(); + BitInserter bi(*bytes); + auto builder = hci::CommandStatusBuilder::Create( + hci::ErrorCode::SUCCESS, uint8_t{1}, op_code, std::make_unique(success_vector)); + builder->Serialize(bi); + return hci::CommandStatusView::Create(hci::EventView::Create(hci::PacketView(bytes))); +} } // namespace @@ -107,6 +191,12 @@ class TestController : public Controller { acl_credits_callback_ = {}; } + bool SupportsBlePrivacy() const override { + return supports_ble_privacy_; + } + bool supports_ble_privacy_{false}; + + public: const uint16_t max_acl_packet_credits_ = 10; const uint16_t hci_mtu_ = 1024; const uint16_t le_max_acl_packet_credits_ = 15; @@ -162,6 +252,7 @@ class TestHciLayer : public HciLayer { } } + public: std::unique_ptr DequeueCommand() { const std::lock_guard lock(command_queue_mutex_); auto packet = std::move(command_queue_.front()); @@ -187,7 +278,6 @@ class TestHciLayer : public HciLayer { return command_queue_.size(); } - public: void SetCommandFuture() { ASSERT_LOG(command_promise_ == nullptr, "Promises, Promises, ... Only one at a time."); command_promise_ = std::make_unique>(); @@ -274,8 +364,16 @@ class TestHciLayer : public HciLayer { CommandInterfaceImpl le_acl_connection_manager_interface_{*this}; }; -class LeImplTest : public ::testing::Test { +class LeConnectionCallbacksTest : public LeConnectionCallbacks { public: + virtual ~LeConnectionCallbacksTest() = default; + virtual void OnLeConnectSuccess( + AddressWithType address_with_type, std::unique_ptr connection) override {} + virtual void OnLeConnectFail(AddressWithType address_with_type, ErrorCode reason) override {} +}; + +class LeImplTest : public ::testing::Test { + protected: void SetUp() override { thread_ = new Thread("thread", Thread::Priority::NORMAL); handler_ = new Handler(thread_); @@ -316,6 +414,14 @@ class LeImplTest : public ::testing::Test { } void TearDown() override { + // We cannot teardown our structure without unregistering + // from our own structure we created. + if (le_impl_->address_manager_registered) { + le_impl_->ready_to_unregister = true; + le_impl_->check_for_unregister(); + sync_handler(); + } + sync_handler(); delete le_impl_; @@ -391,9 +497,22 @@ class LeImplTest : public ::testing::Test { TestController* controller_; RoundRobinScheduler* round_robin_scheduler_{nullptr}; + LeConnectionCallbacksTest connection_callbacks_; struct le_impl* le_impl_; }; +class LeImplWithCallbacksTest : public LeImplTest { + protected: + void SetUp() override { + LeImplTest::SetUp(); + le_impl_->handle_register_le_callbacks(&connection_callbacks_, handler_); + } + + void TearDown() override { + LeImplTest::TearDown(); + } +}; + TEST_F(LeImplTest, nop) {} TEST_F(LeImplTest, add_device_to_connect_list) { diff --git a/system/gd/hci/le_address_manager.h b/system/gd/hci/le_address_manager.h index 4e4e5c3808d..235549ebeed 100644 --- a/system/gd/hci/le_address_manager.h +++ b/system/gd/hci/le_address_manager.h @@ -96,6 +96,11 @@ class LeAddressManager { void OnCommandComplete(CommandCompleteView view); std::chrono::milliseconds GetNextPrivateAddressIntervalMs(); + // Unsynchronized check for testing purposes + size_t NumberCachedCommands() const { + return cached_commands_.size(); + } + private: enum ClientState { WAITING_FOR_PAUSE, -- GitLab From 5e4d2602c2e77d64fff0a6e9254eaebce387eb8c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Sat, 8 Oct 2022 00:36:31 +0200 Subject: [PATCH 056/667] LE Audio: improve Appearance to CoD mapping LE Audio devices with appearance of Wearable Device should have "LE Audio" bit in CoD set. Also set other related bits. Currently, if such device is advertising over LE and Classic at same time, LE Audio bit would flicker. Bug: 247504761 Test: manual (cherry picked from commit 44bb41170428b5584ab06bde289fd864f4268499) Merged-In: Ifca26314320ddef360adf5642e2ae1b0c4e84781 Change-Id: Ifca26314320ddef360adf5642e2ae1b0c4e84781 --- system/stack/btm/btm_ble_gap.cc | 7 ++++++- system/stack/include/btm_ble_api_types.h | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc index c2fda917657..177d0f8e198 100644 --- a/system/stack/btm/btm_ble_gap.cc +++ b/system/stack/btm/btm_ble_gap.cc @@ -2267,8 +2267,13 @@ static void btm_ble_appearance_to_cod(uint16_t appearance, uint8_t* dev_class) { dev_class[1] = BTM_COD_MAJOR_AUDIO; dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; break; + case BTM_BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE: case BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD: - dev_class[1] = BTM_COD_MAJOR_AUDIO; + case BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET: + case BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES: + case BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND: + dev_class[0] = (BTM_COD_SERVICE_AUDIO | BTM_COD_SERVICE_RENDERING) >> 8; + dev_class[1] = (BTM_COD_MAJOR_AUDIO | BTM_COD_SERVICE_LE_AUDIO); dev_class[2] = BTM_COD_MINOR_WEARABLE_HEADSET; break; case BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER: diff --git a/system/stack/include/btm_ble_api_types.h b/system/stack/include/btm_ble_api_types.h index 8de1dcc9aa0..9bc187da88b 100644 --- a/system/stack/include/btm_ble_api_types.h +++ b/system/stack/include/btm_ble_api_types.h @@ -257,7 +257,11 @@ typedef uint8_t BLE_SIGNATURE[BTM_BLE_AUTH_SIGN_LEN]; /* Device address */ #define BTM_BLE_APPEARANCE_CYCLING_CADENCE 0x0483 #define BTM_BLE_APPEARANCE_CYCLING_POWER 0x0484 #define BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE 0x0485 +#define BTM_BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE 0x0940 #define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD 0x0941 +#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET 0x0942 +#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES 0x0943 +#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND 0x0944 #define BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 0x0C40 #define BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 0x0C41 #define BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST 0x0C42 -- GitLab From 043b82a3562e0aa75511aebec7fa72c342ab681d Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 28 Sep 2022 12:57:51 +0200 Subject: [PATCH 057/667] LE Audio: Warn when MCP commands are ignored due to AUTH errors Currently, when LE Audio profile is started, it would instruct MCP that remote device can send media control commands. If such commands was to be ignored, we should log that, so we know we need to take actions to fix it. Bug: 249164851 Merged-In: Ic6124f7db7296be06d6537a41dcf3ce249f5f232 Change-Id: Ic6124f7db7296be06d6537a41dcf3ce249f5f232 --- .../com/android/bluetooth/mcp/MediaControlGattService.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java index 9144562b2f4..83f4a7ea4eb 100644 --- a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java +++ b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java @@ -454,9 +454,7 @@ public class MediaControlGattService implements MediaControlGattServiceInterface } private void onRejectedAuthorizationGattOperation(BluetoothDevice device, GattOpContext op) { - if (VDBG) { - Log.d(TAG, "onRejectedAuthorizationGattOperation device: " + device); - } + Log.w(TAG, "onRejectedAuthorizationGattOperation device: " + device); switch (op.mOperation) { case READ_CHARACTERISTIC: -- GitLab From 09bb036fe556b7ec48efb6a7f60a47ac8b0149f9 Mon Sep 17 00:00:00 2001 From: "hong.wang" Date: Wed, 14 Sep 2022 16:04:28 +0800 Subject: [PATCH 058/667] Fix Advertising_instance address when multi-adv device is connected The Advertising_instance address need be obtained from the event of LE multi-advertising state Change sub-event(0x55) when multi-adv device connect smp get multi-adv mac address error. Cause follow fail 1. Bluetooth LE Insecure Client Test ---Fail 2. Bluetooth LE Insecure Server Test ---Fail 3. Bluetooth LE CoC Secure Client Test ---Fail 4. Bluetooth LE CoC Secure Server Test ---Fail Bug: 247945201 Test: the CTS verifier cases Change-Id: Ib3d6da458b7eeae55432f0adeee91fb5d57a2fbd Signed-off-by: hong.wang (cherry picked from commit 4abeafd6881cdf5def9005ba0697fa513512d469) Merged-In: Ib3d6da458b7eeae55432f0adeee91fb5d57a2fbd --- system/gd/hci/hci_packets.pdl | 12 +++++++ system/gd/hci/le_advertising_manager.cc | 42 ++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/system/gd/hci/hci_packets.pdl b/system/gd/hci/hci_packets.pdl index 2b0defbcce5..600841e42de 100644 --- a/system/gd/hci/hci_packets.pdl +++ b/system/gd/hci/hci_packets.pdl @@ -777,6 +777,7 @@ enum SubeventCode : 8 { // Vendor specific events enum VseSubeventCode : 8 { BLE_THRESHOLD = 0x54, + BLE_STCHANGE = 0x55, BLE_TRACKING = 0x56, DEBUG_INFO = 0x57, BQR_EVENT = 0x58, @@ -5978,6 +5979,17 @@ packet LEAdvertisementTrackingEvent : VendorSpecificEvent (subevent_code = BLE_T _body_, } +enum VseStateChangeReason : 8 { + CONNECTION_RECEIVED = 0x00, +} + +packet LEAdvertiseStateChangeEvent : VendorSpecificEvent (subevent_code = BLE_STCHANGE) { + advertising_instance : 8, + state_change_reason : VseStateChangeReason, + connection_handle : 12, + reserved : 4, +} + packet LEAdvertisementTrackingWithInfoEvent : LEAdvertisementTrackingEvent { tx_power : 8, rssi : 8, diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc index 5e5a79f374e..142781db207 100644 --- a/system/gd/hci/le_advertising_manager.cc +++ b/system/gd/hci/le_advertising_manager.cc @@ -24,6 +24,7 @@ #include "hci/hci_layer.h" #include "hci/hci_packets.h" #include "hci/le_advertising_interface.h" +#include "hci/vendor_specific_event_manager.h" #include "module.h" #include "os/handler.h" #include "os/log.h" @@ -103,17 +104,25 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb advertising_sets_.clear(); } - void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller, - hci::AclManager* acl_manager) { + void start( + os::Handler* handler, + hci::HciLayer* hci_layer, + hci::Controller* controller, + hci::AclManager* acl_manager, + hci::VendorSpecificEventManager* vendor_specific_event_manager) { module_handler_ = handler; hci_layer_ = hci_layer; controller_ = controller; le_maximum_advertising_data_length_ = controller_->GetLeMaximumAdvertisingDataLength(); acl_manager_ = acl_manager; le_address_manager_ = acl_manager->GetLeAddressManager(); + num_instances_ = controller_->GetLeNumberOfSupportedAdverisingSets(); + le_advertising_interface_ = hci_layer_->GetLeAdvertisingInterface(module_handler_->BindOn(this, &LeAdvertisingManager::impl::handle_event)); - num_instances_ = controller_->GetLeNumberOfSupportedAdverisingSets(); + vendor_specific_event_manager->RegisterEventHandler( + hci::VseSubeventCode::BLE_STCHANGE, + handler->BindOn(this, &LeAdvertisingManager::impl::multi_advertising_state_change)); if (controller_->SupportsBleExtendedAdvertising()) { advertising_api_type_ = AdvertisingApiType::EXTENDED; @@ -152,6 +161,24 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb advertising_callbacks_ = advertising_callback; } + void multi_advertising_state_change(hci::VendorSpecificEventView event) { + auto view = hci::LEAdvertiseStateChangeEventView::Create(event); + ASSERT(view.IsValid()); + LOG_INFO( + "Instance: 0x%x StateChangeReason: 0x%s Handle: 0x%x Address: %s", + view.GetAdvertisingInstance(), + VseStateChangeReasonText(view.GetStateChangeReason()).c_str(), + view.GetConnectionHandle(), + advertising_sets_[view.GetAdvertisingInstance()].current_address.ToString().c_str()); + + if (view.GetStateChangeReason() == VseStateChangeReason::CONNECTION_RECEIVED) { + acl_manager_->OnAdvertisingSetTerminated( + ErrorCode::SUCCESS, + view.GetConnectionHandle(), + advertising_sets_[view.GetAdvertisingInstance()].current_address); + } + } + void handle_event(LeMetaEventView event) { switch (event.GetSubeventCode()) { case hci::SubeventCode::SCAN_REQUEST_RECEIVED: @@ -1374,11 +1401,16 @@ void LeAdvertisingManager::ListDependencies(ModuleList* list) const { list->add(); list->add(); list->add(); + list->add(); } void LeAdvertisingManager::Start() { - pimpl_->start(GetHandler(), GetDependency(), GetDependency(), - GetDependency()); + pimpl_->start( + GetHandler(), + GetDependency(), + GetDependency(), + GetDependency(), + GetDependency()); } void LeAdvertisingManager::Stop() { -- GitLab From da144b2ffcd96da7ed8b58c326994e87d79afbbb Mon Sep 17 00:00:00 2001 From: Rahul Arya Date: Mon, 3 Oct 2022 20:18:08 +0000 Subject: [PATCH 059/667] Restart advertising after connection made to multi-advertiser Bug: 244308161 Test: compiles + manual by NP Tag: #stability Change-Id: I183995eaf1c3a20f1ffa0dab45eeb118983904f8 (cherry picked from commit a28ed9bfd991e6e4ccc65056634e0e01bf691b6f) Merged-In: I183995eaf1c3a20f1ffa0dab45eeb118983904f8 --- system/gd/hci/le_advertising_manager.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc index 142781db207..9437a94aa93 100644 --- a/system/gd/hci/le_advertising_manager.cc +++ b/system/gd/hci/le_advertising_manager.cc @@ -164,18 +164,27 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb void multi_advertising_state_change(hci::VendorSpecificEventView event) { auto view = hci::LEAdvertiseStateChangeEventView::Create(event); ASSERT(view.IsValid()); + + auto advertiser_id = view.GetAdvertisingInstance(); + LOG_INFO( "Instance: 0x%x StateChangeReason: 0x%s Handle: 0x%x Address: %s", - view.GetAdvertisingInstance(), + advertiser_id, VseStateChangeReasonText(view.GetStateChangeReason()).c_str(), view.GetConnectionHandle(), advertising_sets_[view.GetAdvertisingInstance()].current_address.ToString().c_str()); if (view.GetStateChangeReason() == VseStateChangeReason::CONNECTION_RECEIVED) { acl_manager_->OnAdvertisingSetTerminated( - ErrorCode::SUCCESS, - view.GetConnectionHandle(), - advertising_sets_[view.GetAdvertisingInstance()].current_address); + ErrorCode::SUCCESS, view.GetConnectionHandle(), advertising_sets_[advertiser_id].current_address); + + enabled_sets_[advertiser_id].advertising_handle_ = kInvalidHandle; + + if (!advertising_sets_[advertiser_id].directed) { + // TODO(250666237) calculate remaining duration and advertising events + LOG_INFO("Resuming advertising, since not directed"); + enable_advertiser(advertiser_id, true, 0, 0); + } } } -- GitLab From fc56c74378e2e559f862e8ad72c92ee218969448 Mon Sep 17 00:00:00 2001 From: Rahul Arya Date: Thu, 6 Oct 2022 02:07:04 +0000 Subject: [PATCH 060/667] Pass PHY change event up to GATT Allow GATT to use 2M phy. Test: manual QA testing Bug: 245969911 Change-Id: I1e3458423dde7f5b7240f573955fec7b5b83e9d2 (cherry picked from commit 6221f7f4691f9c1ad7f4ba1549841ac69880bcaf) Merged-In: I1e3458423dde7f5b7240f573955fec7b5b83e9d2 --- .../bluetooth/btservice/AdapterService.java | 5 +++++ system/gd/rust/common/src/init_flags.rs | 3 ++- system/gd/rust/shim/src/init_flags.rs | 1 + system/main/shim/acl.cc | 16 +++++++++++++--- system/main/shim/acl_legacy_interface.cc | 5 +++++ system/main/shim/acl_legacy_interface.h | 3 ++- 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index f29e1649b63..ca21c3d5762 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -5082,6 +5082,7 @@ public class AdapterService extends Service { private static final String GATT_ROBUST_CACHING_CLIENT_FLAG = "INIT_gatt_robust_caching_client"; private static final String GATT_ROBUST_CACHING_SERVER_FLAG = "INIT_gatt_robust_caching_server"; private static final String IRK_ROTATION_FLAG = "INIT_irk_rotation"; + private static final String PASS_PHY_UPDATE_CALLBACK_FLAG = "INIT_pass_phy_update_callback"; /** * Logging flags logic (only applies to DEBUG and VERBOSE levels): @@ -5145,6 +5146,10 @@ public class AdapterService extends Service { if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, IRK_ROTATION_FLAG, false)) { initFlags.add(String.format("%s=%s", IRK_ROTATION_FLAG, "true")); } + if (DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_BLUETOOTH, PASS_PHY_UPDATE_CALLBACK_FLAG, true)) { + initFlags.add(String.format("%s=%s", PASS_PHY_UPDATE_CALLBACK_FLAG, "true")); + } if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, false)) { initFlags.add(String.format("%s=%s", LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, "true")); diff --git a/system/gd/rust/common/src/init_flags.rs b/system/gd/rust/common/src/init_flags.rs index 07e63f427bf..fba517a7b84 100644 --- a/system/gd/rust/common/src/init_flags.rs +++ b/system/gd/rust/common/src/init_flags.rs @@ -82,7 +82,8 @@ init_flags!( btaa_hci, gd_rust, gd_link_policy, - irk_rotation + irk_rotation, + pass_phy_update_callback }, dependencies: { gd_core => gd_security diff --git a/system/gd/rust/shim/src/init_flags.rs b/system/gd/rust/shim/src/init_flags.rs index 1a1cea1c714..da1969d73ed 100644 --- a/system/gd/rust/shim/src/init_flags.rs +++ b/system/gd/rust/shim/src/init_flags.rs @@ -13,6 +13,7 @@ mod ffi { fn gd_rust_is_enabled() -> bool; fn gd_link_policy_is_enabled() -> bool; fn irk_rotation_is_enabled() -> bool; + fn pass_phy_update_callback_is_enabled() -> bool; } } diff --git a/system/main/shim/acl.cc b/system/main/shim/acl.cc index 3fe132f9761..e2f517ac693 100644 --- a/system/main/shim/acl.cc +++ b/system/main/shim/acl.cc @@ -33,6 +33,7 @@ #include "device/include/controller.h" #include "gd/common/bidi_queue.h" #include "gd/common/bind.h" +#include "gd/common/init_flags.h" #include "gd/common/strings.h" #include "gd/common/sync_map_count.h" #include "gd/hci/acl_manager.h" @@ -61,6 +62,7 @@ #include "stack/include/bt_hdr.h" #include "stack/include/btm_api.h" #include "stack/include/btm_status.h" +#include "stack/include/gatt_api.h" #include "stack/include/pan_api.h" #include "stack/include/sec_hci_link_interface.h" #include "stack/l2cap/l2c_int.h" @@ -753,9 +755,17 @@ class LeShimAclConnection void OnPhyUpdate(hci::ErrorCode hci_status, uint8_t tx_phy, uint8_t rx_phy) override { - TRY_POSTING_ON_MAIN(interface_.on_phy_update, - ToLegacyHciErrorCode(hci_status), handle_, tx_phy, - rx_phy); + if (common::init_flags::pass_phy_update_callback_is_enabled()) { + TRY_POSTING_ON_MAIN( + interface_.on_phy_update, + static_cast(ToLegacyHciErrorCode(hci_status)), handle_, + tx_phy, rx_phy); + } else { + LOG_WARN( + "Not posting OnPhyUpdate callback since it is disabled: (tx:%x, " + "rx:%x, status:%s)", + tx_phy, rx_phy, hci::ErrorCodeText(hci_status).c_str()); + } } void OnLocalAddressUpdate(hci::AddressWithType address_with_type) override { diff --git a/system/main/shim/acl_legacy_interface.cc b/system/main/shim/acl_legacy_interface.cc index 4cc2557d9d8..5b03a3eab56 100644 --- a/system/main/shim/acl_legacy_interface.cc +++ b/system/main/shim/acl_legacy_interface.cc @@ -15,12 +15,16 @@ */ #include "main/shim/acl_legacy_interface.h" + #include "stack/include/acl_hci_link_interface.h" #include "stack/include/ble_acl_interface.h" +#include "stack/include/gatt_api.h" #include "stack/include/sco_hci_link_interface.h" #include "stack/include/sec_hci_link_interface.h" struct tBTM_ESCO_DATA; +void gatt_notify_phy_updated(tGATT_STATUS status, uint16_t handle, + uint8_t tx_phy, uint8_t rx_phy); namespace bluetooth { namespace shim { @@ -78,6 +82,7 @@ const acl_interface_t GetAclInterface() { .link.le.on_data_length_change = acl_ble_data_length_change_event, .link.le.on_read_remote_version_information_complete = btm_read_remote_version_complete, + .link.le.on_phy_update = gatt_notify_phy_updated, }; return acl_interface; } diff --git a/system/main/shim/acl_legacy_interface.h b/system/main/shim/acl_legacy_interface.h index 51726385199..5422df8335a 100644 --- a/system/main/shim/acl_legacy_interface.h +++ b/system/main/shim/acl_legacy_interface.h @@ -20,6 +20,7 @@ #include "stack/include/bt_hdr.h" #include "stack/include/bt_types.h" +#include "stack/include/gatt_api.h" #include "stack/include/hci_error_code.h" #include "stack/include/hci_mode.h" #include "stack/include/hcidefs.h" @@ -122,7 +123,7 @@ typedef struct { void (*on_read_remote_version_information_complete)( tHCI_STATUS status, uint16_t handle, uint8_t lmp_version, uint16_t manufacturer_name, uint16_t sub_version); - void (*on_phy_update)(tHCI_STATUS status, uint16_t handle, uint8_t tx_phy, + void (*on_phy_update)(tGATT_STATUS status, uint16_t handle, uint8_t tx_phy, uint8_t rx_phy); } acl_le_link_interface_t; -- GitLab From f72161f2b02d26fd726442e640fb3092effc371f Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Thu, 13 Oct 2022 01:41:34 +0000 Subject: [PATCH 061/667] Guard mPendingGattOperations to prevent NPE Bug: 252954727 Tag: #refactor Test: atest MediaControlGattServiceTest Change-Id: Iaf9abeaa28670d1b89c9fd177bce94a405943780 (cherry picked from commit 5171c80ad106325089dcc7c7e3df77dc245d0199) --- .../mcp/MediaControlGattService.java | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java index 83f4a7ea4eb..0f1fef7416f 100644 --- a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java +++ b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java @@ -337,16 +337,18 @@ public class MediaControlGattService implements MediaControlGattServiceInterface Log.d(TAG, "onUnauthorizedGattOperation device: " + device); } - List operations = mPendingGattOperations.get(device); - if (operations == null) { - operations = new ArrayList<>(); - mPendingGattOperations.put(device, operations); - } + synchronized (mPendingGattOperations) { + List operations = mPendingGattOperations.get(device); + if (operations == null) { + operations = new ArrayList<>(); + mPendingGattOperations.put(device, operations); + } - operations.add(op); - // Send authorization request for each device only for it's first GATT request - if (operations.size() == 1) { - mMcpService.onDeviceUnauthorized(device); + operations.add(op); + // Send authorization request for each device only for it's first GATT request + if (operations.size() == 1) { + mMcpService.onDeviceUnauthorized(device); + } } } @@ -494,7 +496,9 @@ public class MediaControlGattService implements MediaControlGattServiceInterface Log.d(TAG, "ClearUnauthorizedGattOperations device: " + device); } - mPendingGattOperations.remove(device); + synchronized (mPendingGattOperations) { + mPendingGattOperations.remove(device); + } } private void ProcessPendingGattOperations(BluetoothDevice device) { @@ -502,18 +506,19 @@ public class MediaControlGattService implements MediaControlGattServiceInterface Log.d(TAG, "ProcessPendingGattOperations device: " + device); } - if (mPendingGattOperations.containsKey(device)) { - if (getDeviceAuthorization(device) == BluetoothDevice.ACCESS_ALLOWED) { - for (GattOpContext op : mPendingGattOperations.get(device)) { - onAuthorizedGattOperation(device, op); - } - } else { - for (GattOpContext op : mPendingGattOperations.get(device)) { - onRejectedAuthorizationGattOperation(device, op); + synchronized (mPendingGattOperations) { + if (mPendingGattOperations.containsKey(device)) { + if (getDeviceAuthorization(device) == BluetoothDevice.ACCESS_ALLOWED) { + for (GattOpContext op : mPendingGattOperations.get(device)) { + onAuthorizedGattOperation(device, op); + } + } else { + for (GattOpContext op : mPendingGattOperations.get(device)) { + onRejectedAuthorizationGattOperation(device, op); + } } + ClearUnauthorizedGattOperations(device); } - - ClearUnauthorizedGattOperations(device); } } -- GitLab From 77450e65e7f8570c33c7be8bb221be44b5f00cc4 Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Thu, 13 Oct 2022 11:03:27 +0000 Subject: [PATCH 062/667] Fix NPE in BatteryService Bug: 245657953 Tag: #refactor Test: atest BatteryServiceTest Change-Id: I68b166bccb71ec58d680d8106f14ccefcc7d9bcb (cherry picked from commit c20355926a58198a0c3d9c10de29b9c1a7186b59) --- android/app/src/com/android/bluetooth/bas/BatteryService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/com/android/bluetooth/bas/BatteryService.java b/android/app/src/com/android/bluetooth/bas/BatteryService.java index 43b4e766ffa..79492b30bae 100644 --- a/android/app/src/com/android/bluetooth/bas/BatteryService.java +++ b/android/app/src/com/android/bluetooth/bas/BatteryService.java @@ -214,6 +214,7 @@ public class BatteryService extends ProfileService { BatteryStateMachine sm = getOrCreateStateMachine(device); if (sm == null) { Log.e(TAG, "Cannot connect to " + device + " : no state machine"); + return false; } sm.sendMessage(BatteryStateMachine.CONNECT); } -- GitLab From b9969310ab0d71dc9e0dc888d23c6665773d6412 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Thu, 6 Oct 2022 12:50:05 +0000 Subject: [PATCH 063/667] vc: Send control point request only if device is ready When trying to do control point operations, being connected is not enough as we also need to know the volume change counter. If a group API is used to set volume bofore the individual devices are reported as connected, we should not set it as we dont know the volume change counter yet. The group volume which could not be applied because of this, will be applied from the cache once we report the device as connected. Test: atest --host bluetooth_vc_test --no-bazel-mode Bug: 248475583 Tag: #feature Change-Id: I27b462f7f55122cc7c38fb18908d52e86ea4b5d8 Merged-In: I27b462f7f55122cc7c38fb18908d52e86ea4b5d8 (cherry picked from commit 90b2517650ffd113711a404ed2b4fec694d035fd) --- system/bta/vc/devices.h | 1 + system/bta/vc/vc.cc | 62 +++++++++++++++++++++++++++------------- system/bta/vc/vc_test.cc | 55 ++++++++++++++++++++++++++++++----- 3 files changed, 91 insertions(+), 27 deletions(-) diff --git a/system/bta/vc/devices.h b/system/bta/vc/devices.h index dcf22b0dacf..02b35436ab4 100644 --- a/system/bta/vc/devices.h +++ b/system/bta/vc/devices.h @@ -137,6 +137,7 @@ class VolumeControlDevice { void EnqueueRemainingRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, GATT_WRITE_OP_CB cccd_write_cb); bool VerifyReady(uint16_t handle); + bool IsReady() { return device_ready; } private: /* diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc index 1d406a973a2..9862196a1f1 100644 --- a/system/bta/vc/vc.cc +++ b/system/bta/vc/vc.cc @@ -389,7 +389,11 @@ class VolumeControlImpl : public VolumeControl { << loghex(device->mute) << " change_counter " << loghex(device->change_counter); - if (!device->device_ready) return; + if (!device->IsReady()) { + LOG_INFO("Device: %s is not ready yet.", + device->address.ToString().c_str()); + return; + } /* This is just a read, send single notification */ if (!is_notification) { @@ -462,7 +466,11 @@ class VolumeControlImpl : public VolumeControl { << " offset: " << loghex(offset->offset) << " counter: " << loghex(offset->change_counter); - if (!device->device_ready) return; + if (!device->IsReady()) { + LOG_INFO("Device: %s is not ready yet.", + device->address.ToString().c_str()); + return; + } callbacks_->OnExtAudioOutVolumeOffsetChanged(device->address, offset->id, offset->offset); @@ -483,7 +491,11 @@ class VolumeControlImpl : public VolumeControl { LOG(INFO) << __func__ << "id " << loghex(offset->id) << "location " << loghex(offset->location); - if (!device->device_ready) return; + if (!device->IsReady()) { + LOG_INFO("Device: %s is not ready yet.", + device->address.ToString().c_str()); + return; + } callbacks_->OnExtAudioOutLocationChanged(device->address, offset->id, offset->location); @@ -514,7 +526,11 @@ class VolumeControlImpl : public VolumeControl { LOG(INFO) << __func__ << " " << description; - if (!device->device_ready) return; + if (!device->IsReady()) { + LOG_INFO("Device: %s is not ready yet.", + device->address.ToString().c_str()); + return; + } callbacks_->OnExtAudioOutDescriptionChanged(device->address, offset->id, std::move(description)); @@ -584,7 +600,7 @@ class VolumeControlImpl : public VolumeControl { } // If we get here, it means, device has not been exlicitly disconnected. - bool device_ready = device->device_ready; + bool device_ready = device->IsReady(); device_cleanup_helper(device, device->connecting_actively); @@ -768,14 +784,16 @@ class VolumeControlImpl : public VolumeControl { uint8_t opcode = mute ? kControlPointOpcodeMute : kControlPointOpcodeUnmute; if (std::holds_alternative(addr_or_group_id)) { - LOG_DEBUG("Address: %s: ", - (std::get(addr_or_group_id)).ToString().c_str()); VolumeControlDevice* dev = volume_control_devices_.FindByAddress( std::get(addr_or_group_id)); - if (dev && dev->IsConnected()) { - std::vector devices = {dev->address}; - PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown, - false, opcode, arg); + if (dev != nullptr) { + LOG_DEBUG("Address: %s: isReady: %s", dev->address.ToString().c_str(), + dev->IsReady() ? "true" : "false"); + if (dev->IsReady()) { + std::vector devices = {dev->address}; + PrepareVolumeControlOperation( + devices, bluetooth::groups::kGroupUnknown, false, opcode, arg); + } } } else { /* Handle group change */ @@ -790,7 +808,7 @@ class VolumeControlImpl : public VolumeControl { auto devices = csis_api->GetDeviceList(group_id); for (auto it = devices.begin(); it != devices.end();) { auto dev = volume_control_devices_.FindByAddress(*it); - if (!dev || !dev->IsConnected()) { + if (!dev || !dev->IsReady()) { it = devices.erase(it); } else { it++; @@ -831,12 +849,16 @@ class VolumeControlImpl : public VolumeControl { std::get(addr_or_group_id).ToString().c_str()); VolumeControlDevice* dev = volume_control_devices_.FindByAddress( std::get(addr_or_group_id)); - if (dev && dev->IsConnected() && (dev->volume != volume)) { - std::vector devices = {dev->address}; - RemovePendingVolumeControlOperations(devices, - bluetooth::groups::kGroupUnknown); - PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown, - false, opcode, arg); + if (dev != nullptr) { + LOG_DEBUG("Address: %s: isReady: %s", dev->address.ToString().c_str(), + dev->IsReady() ? "true" : "false"); + if (dev->IsReady() && (dev->volume != volume)) { + std::vector devices = {dev->address}; + RemovePendingVolumeControlOperations( + devices, bluetooth::groups::kGroupUnknown); + PrepareVolumeControlOperation( + devices, bluetooth::groups::kGroupUnknown, false, opcode, arg); + } } } else { /* Handle group change */ @@ -851,7 +873,7 @@ class VolumeControlImpl : public VolumeControl { auto devices = csis_api->GetDeviceList(group_id); for (auto it = devices.begin(); it != devices.end();) { auto dev = volume_control_devices_.FindByAddress(*it); - if (!dev || !dev->IsConnected()) { + if (!dev || !dev->IsReady()) { it = devices.erase(it); } else { it++; @@ -964,7 +986,7 @@ class VolumeControlImpl : public VolumeControl { int latest_operation_id_; void verify_device_ready(VolumeControlDevice* device, uint16_t handle) { - if (device->device_ready) return; + if (device->IsReady()) return; // VerifyReady sets the device_ready flag if all remaining GATT operations // are completed diff --git a/system/bta/vc/vc_test.cc b/system/bta/vc/vc_test.cc index 71fca365c88..3c266ee45be 100644 --- a/system/bta/vc/vc_test.cc +++ b/system/bta/vc/vc_test.cc @@ -229,12 +229,15 @@ class VolumeControlTest : public ::testing::Test { return; } + if (do_not_respond_to_reads) return; cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); })); } protected: + bool do_not_respond_to_reads = false; + void SetUp(void) override { bluetooth::manager::SetMockBtmInterface(&btm_interface); MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_); @@ -1027,13 +1030,6 @@ class VolumeControlCsis : public VolumeControlTest { SetSampleDatabase(conn_id_2); TestAppRegister(); - - TestConnect(test_address_1); - GetConnectedEvent(test_address_1, conn_id_1); - GetSearchCompleteEvent(conn_id_1); - TestConnect(test_address_2); - GetConnectedEvent(test_address_2, conn_id_2); - GetSearchCompleteEvent(conn_id_2); } void TearDown(void) override { @@ -1057,6 +1053,13 @@ class VolumeControlCsis : public VolumeControlTest { }; TEST_F(VolumeControlCsis, test_set_volume) { + TestConnect(test_address_1); + GetConnectedEvent(test_address_1, conn_id_1); + GetSearchCompleteEvent(conn_id_1); + TestConnect(test_address_2); + GetConnectedEvent(test_address_2, conn_id_2); + GetSearchCompleteEvent(conn_id_2); + /* Set value for the group */ EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id_1, 0x0024, _, GATT_WRITE, _, _)); @@ -1073,7 +1076,38 @@ TEST_F(VolumeControlCsis, test_set_volume) { GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value); } +TEST_F(VolumeControlCsis, test_set_volume_device_not_ready) { + /* Make sure we did not get responds to the initial reads, + * so that the device was not marked as ready yet. + */ + do_not_respond_to_reads = true; + + TestConnect(test_address_1); + GetConnectedEvent(test_address_1, conn_id_1); + GetSearchCompleteEvent(conn_id_1); + TestConnect(test_address_2); + GetConnectedEvent(test_address_2, conn_id_2); + GetSearchCompleteEvent(conn_id_2); + + /* Set value for the group */ + EXPECT_CALL(gatt_queue, + WriteCharacteristic(conn_id_1, 0x0024, _, GATT_WRITE, _, _)) + .Times(0); + EXPECT_CALL(gatt_queue, + WriteCharacteristic(conn_id_2, 0x0024, _, GATT_WRITE, _, _)) + .Times(0); + + VolumeControl::Get()->SetVolume(group_id, 10); +} + TEST_F(VolumeControlCsis, autonomus_test_set_volume) { + TestConnect(test_address_1); + GetConnectedEvent(test_address_1, conn_id_1); + GetSearchCompleteEvent(conn_id_1); + TestConnect(test_address_2); + GetConnectedEvent(test_address_2, conn_id_2); + GetSearchCompleteEvent(conn_id_2); + /* Now inject notification and make sure callback is sent up to Java layer */ EXPECT_CALL(*callbacks, OnGroupVolumeStateChanged(group_id, 0x03, false, true)); @@ -1083,6 +1117,13 @@ TEST_F(VolumeControlCsis, autonomus_test_set_volume) { } TEST_F(VolumeControlCsis, autonomus_single_device_test_set_volume) { + TestConnect(test_address_1); + GetConnectedEvent(test_address_1, conn_id_1); + GetSearchCompleteEvent(conn_id_1); + TestConnect(test_address_2); + GetConnectedEvent(test_address_2, conn_id_2); + GetSearchCompleteEvent(conn_id_2); + /* Disconnect one device. */ EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address_1)); -- GitLab From e051ae932477889e3a3976a1a5cd43ae52619e50 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Mon, 10 Oct 2022 14:22:51 +0000 Subject: [PATCH 064/667] VolumeControl: Correct device volume only if connected Native will ignore requests to devices that were not yet reported as connected so there is no reason to call to native. Bug: 248475583 Tag: #feature Test: atest BluetoothInstrumentationTests Change-Id: Icf835d9112b71737dfdccd20ee97a50305fd4ba8 Merged-In: Icf835d9112b71737dfdccd20ee97a50305fd4ba8 (cherry picked from commit 1e1e2f429ff4faa1ac860d56501751822ce41291) --- .../bluetooth/vc/VolumeControlService.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java index d1e5fd0fe62..6922b736a2b 100644 --- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java +++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java @@ -654,7 +654,19 @@ public class VolumeControlService extends ProfileService { Integer groupVolume = mGroupVolumeCache.getOrDefault(groupId, IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME); if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { - mVolumeControlNativeInterface.setVolume(device, groupVolume); + // Correct the volume level only if device was already reported as connected. + boolean can_change_volume = false; + synchronized (mStateMachines) { + VolumeControlStateMachine sm = mStateMachines.get(device); + if (sm != null) { + can_change_volume = + (sm.getConnectionState() == BluetoothProfile.STATE_CONNECTED); + } + } + if (can_change_volume) { + Log.i(TAG, "Setting value:" + groupVolume + " to " + device); + mVolumeControlNativeInterface.setVolume(device, groupVolume); + } } } @@ -696,8 +708,19 @@ public class VolumeControlService extends ProfileService { if (device != null && groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { - Log.i(TAG, "Setting value:" + groupVolume + " to " + device); - mVolumeControlNativeInterface.setVolume(device, groupVolume); + // Correct the volume level only if device was already reported as connected. + boolean can_change_volume = false; + synchronized (mStateMachines) { + VolumeControlStateMachine sm = mStateMachines.get(device); + if (sm != null) { + can_change_volume = + (sm.getConnectionState() == BluetoothProfile.STATE_CONNECTED); + } + } + if (can_change_volume) { + Log.i(TAG, "Setting value:" + groupVolume + " to " + device); + mVolumeControlNativeInterface.setVolume(device, groupVolume); + } } else { Log.e(TAG, "Volume changed did not succeed. Volume: " + volume + " expected volume: " + groupVolume); -- GitLab From 84de8db1fb0b10d2d774aa65aa92f5d18768fb89 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Mon, 10 Oct 2022 14:25:24 +0000 Subject: [PATCH 065/667] vc: Fix doing initial reads and writes twice We already handle encryption complete event which is more reliable than passing callback to the enryption request. We should have a single source of true for the encryption result. For a reconnecting device having both was the cause of duplicate GATT operations for the initial reads and writes (change counter reads etc.) Bug: 248475583 Tag: #feature Test: atest --host bluetooth_vc_test --no-bazel-mode, manual testing Change-Id: Ia4f40a6bead331883a381205cc150e009ec18070 Merged-In: Ia4f40a6bead331883a381205cc150e009ec18070 (cherry picked from commit 9650c45c50a14c6fe2f08278a36744c0e8b9b3e2) --- system/bta/vc/device.cc | 6 ++---- system/bta/vc/devices.h | 2 +- system/bta/vc/vc.cc | 9 +------- system/bta/vc/vc_test.cc | 29 +++++++++++++++++++------- system/test/mock/mock_bta_vc_device.cc | 4 +--- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/system/bta/vc/device.cc b/system/bta/vc/device.cc index 290a2f7b591..4eb15f5f5c8 100644 --- a/system/bta/vc/device.cc +++ b/system/bta/vc/device.cc @@ -415,10 +415,8 @@ bool VolumeControlDevice::IsEncryptionEnabled() { return BTM_IsEncrypted(address, BT_TRANSPORT_LE); } -bool VolumeControlDevice::EnableEncryption(tBTM_SEC_CALLBACK* callback) { - int result = BTM_SetEncryption(address, BT_TRANSPORT_LE, callback, nullptr, +void VolumeControlDevice::EnableEncryption() { + int result = BTM_SetEncryption(address, BT_TRANSPORT_LE, nullptr, nullptr, BTM_BLE_SEC_ENCRYPT); LOG(INFO) << __func__ << ": result=" << +result; - // TODO: should we care about the result?? - return true; } diff --git a/system/bta/vc/devices.h b/system/bta/vc/devices.h index 02b35436ab4..d5126f5ec40 100644 --- a/system/bta/vc/devices.h +++ b/system/bta/vc/devices.h @@ -130,7 +130,7 @@ class VolumeControlDevice { GATT_WRITE_OP_CB cb, void* cb_data); bool IsEncryptionEnabled(); - bool EnableEncryption(tBTM_SEC_CALLBACK* callback); + void EnableEncryption(); bool EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, GATT_WRITE_OP_CB cccd_write_cb); diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc index 9862196a1f1..b2b85cba7ce 100644 --- a/system/bta/vc/vc.cc +++ b/system/bta/vc/vc.cc @@ -145,9 +145,7 @@ class VolumeControlImpl : public VolumeControl { return; } - if (!device->EnableEncryption(enc_callback_static)) { - device_cleanup_helper(device, device->connecting_actively); - } + device->EnableEncryption(); } void OnEncryptionComplete(const RawAddress& address, uint8_t success) { @@ -1120,11 +1118,6 @@ class VolumeControlImpl : public VolumeControl { if (instance) instance->gattc_callback(event, p_data); } - static void enc_callback_static(const RawAddress* address, tBT_TRANSPORT, - void*, tBTM_STATUS status) { - if (instance) instance->OnEncryptionComplete(*address, status); - } - static void chrc_read_callback_static(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len, uint8_t* value, void* data) { diff --git a/system/bta/vc/vc_test.cc b/system/bta/vc/vc_test.cc index 3c266ee45be..cac1f923b54 100644 --- a/system/bta/vc/vc_test.cc +++ b/system/bta/vc/vc_test.cc @@ -441,19 +441,32 @@ class VolumeControlTest : public ::testing::Test { gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, (tBTA_GATTC*)&event_data); } + void GetEncryptionCompleteEvt(const RawAddress& bda) { + tBTA_GATTC cb_data{}; + + cb_data.enc_cmpl.client_if = gatt_if; + cb_data.enc_cmpl.remote_bda = bda; + gatt_callback(BTA_GATTC_ENC_CMPL_CB_EVT, &cb_data); + } + void SetEncryptionResult(const RawAddress& address, bool success) { ON_CALL(btm_interface, BTM_IsEncrypted(address, _)) .WillByDefault(DoAll(Return(false))); - EXPECT_CALL(btm_interface, - SetEncryption(address, _, NotNull(), _, BTM_BLE_SEC_ENCRYPT)) - .WillOnce(Invoke( - [&success](const RawAddress& bd_addr, tBT_TRANSPORT transport, - tBTM_SEC_CALLBACK* p_callback, void* p_ref_data, - tBTM_BLE_SEC_ACT sec_act) -> tBTM_STATUS { - p_callback(&bd_addr, transport, p_ref_data, - success ? BTM_SUCCESS : BTM_FAILED_ON_SECURITY); + ON_CALL(btm_interface, SetEncryption(address, _, _, _, BTM_BLE_SEC_ENCRYPT)) + .WillByDefault(Invoke( + [&success, this](const RawAddress& bd_addr, tBT_TRANSPORT transport, + tBTM_SEC_CALLBACK* p_callback, void* p_ref_data, + tBTM_BLE_SEC_ACT sec_act) -> tBTM_STATUS { + if (p_callback) { + p_callback(&bd_addr, transport, p_ref_data, + success ? BTM_SUCCESS : BTM_FAILED_ON_SECURITY); + } + GetEncryptionCompleteEvt(bd_addr); return BTM_SUCCESS; })); + EXPECT_CALL(btm_interface, + SetEncryption(address, _, _, _, BTM_BLE_SEC_ENCRYPT)) + .Times(1); } void SetSampleDatabaseVCS(uint16_t conn_id) { diff --git a/system/test/mock/mock_bta_vc_device.cc b/system/test/mock/mock_bta_vc_device.cc index a143cf7ef80..fe4159aabe1 100644 --- a/system/test/mock/mock_bta_vc_device.cc +++ b/system/test/mock/mock_bta_vc_device.cc @@ -28,7 +28,6 @@ extern std::map mock_function_count_map; #include #include "bta/vc/devices.h" -#include "stack/btm/btm_sec.h" using namespace bluetooth::vc::internal; @@ -36,9 +35,8 @@ using namespace bluetooth::vc::internal; #define UNUSED_ATTR #endif -bool VolumeControlDevice::EnableEncryption(tBTM_SEC_CALLBACK* callback) { +void VolumeControlDevice::EnableEncryption() { mock_function_count_map[__func__]++; - return false; } bool VolumeControlDevice::EnqueueInitialRequests( tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, -- GitLab From 4bb3cea0f691017e6d7259b92ec57ae0ecc5d3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Wed, 12 Oct 2022 11:24:05 +0000 Subject: [PATCH 066/667] bta_gattc: Fix initialization and free p_q_cmd This will prevent crash when ACL is disconnected while GATT search Bug: 253200147 Test: atest BluetoothInstrumentationTests Test: manual disconnect while bonding and discovering Tag: #feature Merged-In: If7ad51378b650b67e551f8bbec7b38d0d9c14cbd Change-Id: If7ad51378b650b67e551f8bbec7b38d0d9c14cbd (cherry picked from commit e8bf02dd86458743c997b1f07dd742182171b866) --- system/bta/gatt/bta_gattc_utils.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc index 14127021803..6c483c1e310 100644 --- a/system/bta/gatt/bta_gattc_utils.cc +++ b/system/bta/gatt/bta_gattc_utils.cc @@ -146,6 +146,7 @@ tBTA_GATTC_CLCB* bta_gattc_clcb_alloc(tGATT_IF client_if, p_clcb->status = GATT_SUCCESS; p_clcb->transport = transport; p_clcb->bda = remote_bda; + p_clcb->p_q_cmd = NULL; p_clcb->p_rcb = bta_gattc_cl_get_regcb(client_if); @@ -217,7 +218,9 @@ void bta_gattc_clcb_dealloc(tBTA_GATTC_CLCB* p_clcb) { p_srcb->gatt_database.Clear(); } - osi_free_and_reset((void**)&p_clcb->p_q_cmd); + if (p_clcb->p_q_cmd != NULL) { + osi_free_and_reset((void**)&p_clcb->p_q_cmd); + } memset(p_clcb, 0, sizeof(tBTA_GATTC_CLCB)); } -- GitLab From 8466c9c66f2a9679e57f088c9494c5ff185230d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Wed, 12 Oct 2022 10:50:11 +0000 Subject: [PATCH 067/667] gatt: Add missing disconnect call to EATT layer When remote device disconnected the link, usually l2cap calls disconnect callbacks which clears eatt channels. However sometimes Eatt might not get it first and later could be asked to provide queued commands - which should be not there when link disconnected. Bug: 249431973 Test: atest BluetoothInstrumentationTests Test: disconnect while discovering during bonding Tag: #feature Merged-In: I5fa2370dfefaf36646cb61e128c966e9343eeb55 Change-Id: I5fa2370dfefaf36646cb61e128c966e9343eeb55 (cherry picked from commit 176c3cad0e80fcff6421456bcaf34242a7b80bc3) --- system/stack/gatt/gatt_utils.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc index 64b2f7aef5f..9ce144b98ec 100644 --- a/system/stack/gatt/gatt_utils.cc +++ b/system/stack/gatt/gatt_utils.cc @@ -1585,6 +1585,10 @@ void gatt_cleanup_upon_disc(const RawAddress& bda, tGATT_DISCONN_REASON reason, } gatt_set_ch_state(p_tcb, GATT_CH_CLOSE); + + /* Notify EATT about disconnection. */ + EattExtension::GetInstance()->Disconnect(p_tcb->peer_bda); + for (uint8_t i = 0; i < GATT_CL_MAX_LCB; i++) { tGATT_CLCB* p_clcb = &gatt_cb.clcb[i]; if (!p_clcb->in_use || p_clcb->p_tcb != p_tcb) continue; -- GitLab From dc52d3a2be714ae2f05e7896a1603d4dd8e498d3 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 15 Oct 2022 19:23:48 -0700 Subject: [PATCH 068/667] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Icacf6903d00645283e116be65ec5f9ceb8aadd4a --- android/app/res/values-as/strings.xml | 4 ++-- android/app/res/values-fr-rCA/strings.xml | 2 +- android/app/res/values-te/strings.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/app/res/values-as/strings.xml b/android/app/res/values-as/strings.xml index 39eeb14c769..f4b0a607d73 100644 --- a/android/app/res/values-as/strings.xml +++ b/android/app/res/values-as/strings.xml @@ -17,7 +17,7 @@ "ডাউনল’ড মেনেজাৰ এক্সেছ কৰিব পাৰে।" - "এপটোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।" + "এপ্‌টোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।" "ব্লুটুথ ডিভাইচ এক্সেছ কৰাৰ স্বীকৃতি দিয়ে।" "এপ্‌টোক এটা ব্লুটুথ ডিভাইচ অস্থায়ীৰূপে স্বীকাৰ কৰাৰ অনুমতি দিয়ে যিয়ে ডিভাইচটোক ব্যৱহাৰকাৰীৰ নিশ্চিতিকৰণৰ অবিহনেই ইয়ালৈ ফাইল পঠিওৱাৰ অনুমতি দিয়ে।" "ব্লুটুথ" @@ -86,7 +86,7 @@ "ফাইলটো ছেভ কৰিব পৰাকৈ ইউএছবি ষ্ট’ৰেজত পৰ্যাপ্ত খালী ঠাই নাই।" "ফাইলটো ছেভ কৰিব পৰাকৈ এছডি কাৰ্ডখনত পৰ্যাপ্ত খালী ঠাই নাই।" "ইমান খালী ঠাইৰ দৰকাৰ: %1$s" - "বহুত বেছি অনুৰোধৰ ওপৰত প্ৰক্ৰিয়া চলি আছে৷ পিছত আকৌ চেষ্টা কৰক৷" + "বহুত বেছি অনুৰোধৰ ওপৰত প্ৰক্ৰিয়া চলি আছে৷ পাছত আকৌ চেষ্টা কৰক৷" "ফাইলৰ স্থানান্তৰণ এতিয়ালৈকে আৰম্ভ হোৱা নাই।" "ফাইলৰ স্থানান্তৰণ চলি আছে।" "ফাইলৰ স্থানান্তৰণৰ কাৰ্য সফলতাৰে সম্পন্ন কৰা হ’ল।" diff --git a/android/app/res/values-fr-rCA/strings.xml b/android/app/res/values-fr-rCA/strings.xml index 5531ec3f3ee..1e7713471c8 100644 --- a/android/app/res/values-fr-rCA/strings.xml +++ b/android/app/res/values-fr-rCA/strings.xml @@ -132,6 +132,6 @@ "Si vous laissez le Bluetooth activé, il restera en fonction la prochaine fois que vous serez en mode Avion" "Le Bluetooth reste activé" "Votre téléphone se souvient de garder le Bluetooth activé en mode Avion. Pour modifier cela, désactivez le Bluetooth." - "LE Wi-Fi et le Bluetooth restent activés" + "Le Wi-Fi et le Bluetooth restent activés" "Votre téléphone se souvient de garder le Wi-Fi et le Bluetooth activés en mode Avion. Pour modifier cela, désactivez-les." diff --git a/android/app/res/values-te/strings.xml b/android/app/res/values-te/strings.xml index 98d1626b4bc..d96fe339dee 100644 --- a/android/app/res/values-te/strings.xml +++ b/android/app/res/values-te/strings.xml @@ -72,7 +72,7 @@ "మూసివేయి" "సరే" "తెలియని ఫైల్" - "ఈ రకమైన ఫైల్‌ను నిర్వహించడానికి యాప్ ఏదీ లేదు. \n" + "ఈ రకమైన ఫైల్‌ను మేనేజ్ చేయడానికి యాప్ ఏదీ లేదు. \n" "ఫైల్ లేదు" "ఫైల్ ఉనికిలో లేదు. \n" "దయచేసి వేచి ఉండండి..." -- GitLab From 3b8dbf1d6c78e901571a4320157a6a6853610344 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Thu, 29 Sep 2022 14:28:53 +0000 Subject: [PATCH 069/667] Make mServiceListener as null before calling onServiceDisconnected This way, we can prevent an infinite loop made by apps calling BluetoothAdapter.getProfileProxy() in the onServiceDisconnected. Tag: #refactor Bug: 249213217 Test: atest CtsBluetoothTestCases Change-Id: I7342befd9a8fc69c778d20140558afb148a4232e (cherry picked from commit e07b56b412fc4fb78a8721d8240722a175ef905a) Merged-In: I7342befd9a8fc69c778d20140558afb148a4232e --- framework/java/android/bluetooth/BluetoothHeadset.java | 3 ++- .../java/android/bluetooth/BluetoothProfileConnector.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 970eeb4f36c..ba55a09f529 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -450,8 +450,9 @@ public final class BluetoothHeadset implements BluetoothProfile { } if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); + ServiceListener listener = mServiceListener; mServiceListener = null; + listener.onServiceDisconnected(BluetoothProfile.HEADSET); } doUnbind(); mCloseGuard.close(); diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index 39f835b3f1b..58b24beb7db 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -189,8 +189,9 @@ public abstract class BluetoothProfileConnector { void disconnect() { if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(mProfileId); + BluetoothProfile.ServiceListener listener = mServiceListener; mServiceListener = null; + listener.onServiceDisconnected(mProfileId); } IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); if (mgr != null) { -- GitLab From 4169fdef9f5c876bfd991d80d5d5b0b5b23162ef Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Thu, 13 Oct 2022 05:54:25 +0000 Subject: [PATCH 070/667] Add BluetoothMapbMessageTest Bug: 237467631 Test: atest BluetoothMapbMessageTest Change-Id: I77dcb28eccbef53cfbf01c5d947a04cddb9c2408 (cherry picked from commit 8ffe0b1f34a000b58326ee3798dde69cd4a2b728) --- .../map/BluetoothMapbMessageTest.java | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java new file mode 100644 index 00000000000..2bc0e1a5358 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java @@ -0,0 +1,175 @@ +/* + * 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. + */ + +package com.android.bluetooth.map; + +import static com.google.common.truth.Truth.assertThat; + +import android.telephony.PhoneNumberUtils; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.map.BluetoothMapUtils.TYPE; +import com.android.bluetooth.map.BluetoothMapbMessage.VCard; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +@RunWith(AndroidJUnit4.class) +public class BluetoothMapbMessageTest { + private static final String TEST_VERSION_STRING = "1.0"; + private static final boolean TEST_STATUS = true; + private static final TYPE TEST_TYPE = TYPE.IM; + private static final String TEST_FOLDER = "placeholder"; + private static final String TEST_ENCODING = "test_encoding"; + + private static final String TEST_NAME = "test_name"; + private static final String TEST_FORMATTED_NAME = "test_formatted_name"; + private static final String TEST_FIRST_PHONE_NUMBER = "111-1111-1111"; + private static final String[] TEST_PHONE_NUMBERS = + new String[]{TEST_FIRST_PHONE_NUMBER, "222-2222-2222"}; + private static final String TEST_FIRST_EMAIL = "testFirst@email.com"; + private static final String[] TEST_EMAIL_ADDRESSES = + new String[]{TEST_FIRST_EMAIL, "testSecond@email.com"}; + private static final String TEST_FIRST_BT_UCI = "test_first_bt_uci"; + private static final String[] TEST_BT_UCIS = + new String[]{TEST_FIRST_BT_UCI, "test_second_bt_uci"}; + private static final String TEST_FIRST_BT_UID = "1111"; + private static final String[] TEST_BT_UIDS = new String[]{TEST_FIRST_BT_UID, "1112"}; + + private static final VCard TEST_VCARD = new VCard(TEST_NAME, TEST_PHONE_NUMBERS, + TEST_EMAIL_ADDRESSES); + + @Test + public void settersAndGetters() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.setVersionString(TEST_VERSION_STRING); + messageMime.setStatus(TEST_STATUS); + messageMime.setType(TEST_TYPE); + messageMime.setFolder(TEST_FOLDER); + messageMime.setEncoding(TEST_ENCODING); + messageMime.setRecipient(TEST_VCARD); + + assertThat(messageMime.getVersionString()).isEqualTo("VERSION:" + TEST_VERSION_STRING); + assertThat(messageMime.getType()).isEqualTo(TEST_TYPE); + assertThat(messageMime.getFolder()).isEqualTo("telecom/msg/" + TEST_FOLDER); + assertThat(messageMime.getRecipients().size()).isEqualTo(1); + assertThat(messageMime.getOriginators()).isNull(); + } + + @Test + public void setCompleteFolder() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.setCompleteFolder(TEST_FOLDER); + assertThat(messageMime.getFolder()).isEqualTo(TEST_FOLDER); + } + + @Test + public void addOriginator_forVCardVersionTwoPointOne() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.addOriginator(TEST_NAME, TEST_PHONE_NUMBERS, TEST_EMAIL_ADDRESSES); + assertThat(messageMime.getOriginators().get(0).getName()).isEqualTo(TEST_NAME); + assertThat(messageMime.getOriginators().get(0).getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(messageMime.getOriginators().get(0).getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + } + + @Test + public void addOriginator_withVCardObject() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.addOriginator(TEST_VCARD); + assertThat(messageMime.getOriginators().get(0)).isEqualTo(TEST_VCARD); + } + + @Test + public void addOriginator_forVCardVersionThree() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.addOriginator(TEST_NAME, TEST_FORMATTED_NAME, TEST_PHONE_NUMBERS, + TEST_EMAIL_ADDRESSES, TEST_BT_UIDS, TEST_BT_UCIS); + assertThat(messageMime.getOriginators().get(0).getName()).isEqualTo(TEST_NAME); + assertThat(messageMime.getOriginators().get(0).getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(messageMime.getOriginators().get(0).getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + assertThat(messageMime.getOriginators().get(0).getFirstBtUci()).isEqualTo( + TEST_FIRST_BT_UCI); + } + + @Test + public void addOriginator_forVCardVersionThree_withOnlyBtUcisAndBtUids() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.addOriginator(TEST_BT_UCIS, TEST_BT_UIDS); + assertThat(messageMime.getOriginators().get(0).getFirstBtUci()).isEqualTo( + TEST_FIRST_BT_UCI); + } + + @Test + public void addRecipient_forVCardVersionTwoPointOne() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.addRecipient(TEST_NAME, TEST_PHONE_NUMBERS, TEST_EMAIL_ADDRESSES); + assertThat(messageMime.getRecipients().get(0).getName()).isEqualTo(TEST_NAME); + assertThat(messageMime.getRecipients().get(0).getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(messageMime.getRecipients().get(0).getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + } + + @Test + public void addRecipient_forVCardVersionThree() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.addRecipient(TEST_NAME, TEST_FORMATTED_NAME, TEST_PHONE_NUMBERS, + TEST_EMAIL_ADDRESSES, TEST_BT_UIDS, TEST_BT_UCIS); + assertThat(messageMime.getRecipients().get(0).getName()).isEqualTo(TEST_NAME); + assertThat(messageMime.getRecipients().get(0).getFirstPhoneNumber()).isEqualTo( + PhoneNumberUtils.stripSeparators(TEST_FIRST_PHONE_NUMBER)); + assertThat(messageMime.getRecipients().get(0).getFirstEmail()).isEqualTo(TEST_FIRST_EMAIL); + assertThat(messageMime.getRecipients().get(0).getFirstBtUci()).isEqualTo(TEST_FIRST_BT_UCI); + } + + @Test + public void addRecipient_forVCardVersionThree_withOnlyBtUcisAndBtUids() { + BluetoothMapbMessage messageMime = new BluetoothMapbMessageMime(); + messageMime.addRecipient(TEST_BT_UCIS, TEST_BT_UIDS); + assertThat(messageMime.getRecipients().get(0).getFirstBtUci()).isEqualTo(TEST_FIRST_BT_UCI); + } + + @Test + public void encodeToByteArray_thenCreateByParsing_ReturnsCorrectly() throws Exception { + BluetoothMapbMessage messageMimeToEncode = new BluetoothMapbMessageMime(); + messageMimeToEncode.setVersionString(TEST_VERSION_STRING); + messageMimeToEncode.setStatus(TEST_STATUS); + messageMimeToEncode.setType(TEST_TYPE); + messageMimeToEncode.setCompleteFolder(TEST_FOLDER); + messageMimeToEncode.setEncoding(TEST_ENCODING); + messageMimeToEncode.addOriginator(TEST_NAME, TEST_FORMATTED_NAME, TEST_PHONE_NUMBERS, + TEST_EMAIL_ADDRESSES, TEST_BT_UIDS, TEST_BT_UCIS); + messageMimeToEncode.addRecipient(TEST_NAME, TEST_FORMATTED_NAME, TEST_PHONE_NUMBERS, + TEST_EMAIL_ADDRESSES, TEST_BT_UIDS, TEST_BT_UCIS); + + byte[] encodedMessageMime = messageMimeToEncode.encode(); + InputStream inputStream = new ByteArrayInputStream(encodedMessageMime); + + BluetoothMapbMessage messageMimeParsed = BluetoothMapbMessage.parse(inputStream, 1); + assertThat(messageMimeParsed.mAppParamCharset).isEqualTo(1); + assertThat(messageMimeParsed.getVersionString()).isEqualTo( + "VERSION:" + TEST_VERSION_STRING); + assertThat(messageMimeParsed.getType()).isEqualTo(TEST_TYPE); + assertThat(messageMimeParsed.getFolder()).isEqualTo(TEST_FOLDER); + assertThat(messageMimeParsed.getRecipients().size()).isEqualTo(1); + assertThat(messageMimeParsed.getOriginators().size()).isEqualTo(1); + } +} \ No newline at end of file -- GitLab From e78f724ed83a161a6f4acf84541978bab56a79e9 Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Wed, 12 Oct 2022 01:32:27 +0000 Subject: [PATCH 071/667] Add BluetoothMapbMessageMimeTest Bug: 237467631 Test: atest BluetoothMapbMessageMimeTest Change-Id: I84f0473315ecb6fe0d87d2811a7e9d49c4bcbb06 (cherry picked from commit 18a03e0a3681ee4159cbc90bd122573e272befd3) --- .../map/BluetoothMapbMessageMime.java | 16 +- .../map/BluetoothMapbMessageMimeTest.java | 186 +++++++++++++++++- 2 files changed, 193 insertions(+), 9 deletions(-) diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapbMessageMime.java b/android/app/src/com/android/bluetooth/map/BluetoothMapbMessageMime.java index 1d466c5b75b..a2a81fc6c09 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapbMessageMime.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapbMessageMime.java @@ -591,14 +591,6 @@ public class BluetoothMapbMessageMime extends BluetoothMapbMessage { headerValue = BluetoothMapUtils.stripEncoding(headerValue); Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(headerValue); mFrom = new ArrayList(Arrays.asList(tokens)); - } else if (headerType.contains("TO")) { - headerValue = BluetoothMapUtils.stripEncoding(headerValue); - Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(headerValue); - mTo = new ArrayList(Arrays.asList(tokens)); - } else if (headerType.contains("CC")) { - headerValue = BluetoothMapUtils.stripEncoding(headerValue); - Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(headerValue); - mCc = new ArrayList(Arrays.asList(tokens)); } else if (headerType.contains("BCC")) { headerValue = BluetoothMapUtils.stripEncoding(headerValue); Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(headerValue); @@ -607,6 +599,14 @@ public class BluetoothMapbMessageMime extends BluetoothMapbMessage { headerValue = BluetoothMapUtils.stripEncoding(headerValue); Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(headerValue); mReplyTo = new ArrayList(Arrays.asList(tokens)); + } else if (headerType.contains("TO")) { + headerValue = BluetoothMapUtils.stripEncoding(headerValue); + Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(headerValue); + mTo = new ArrayList(Arrays.asList(tokens)); + } else if (headerType.contains("CC")) { + headerValue = BluetoothMapUtils.stripEncoding(headerValue); + Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(headerValue); + mCc = new ArrayList(Arrays.asList(tokens)); } else if (headerType.contains("SUBJECT")) { // Other headers mSubject = BluetoothMapUtils.stripEncoding(headerValue); } else if (headerType.contains("MESSAGE-ID")) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java index cbd37bba651..ac2d9804a6a 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java @@ -16,19 +16,203 @@ package com.android.bluetooth.map; -import static org.mockito.Mockito.*; +import static com.google.common.truth.Truth.assertThat; + +import android.text.util.Rfc822Token; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Locale; + @MediumTest @RunWith(AndroidJUnit4.class) public class BluetoothMapbMessageMimeTest { private static final String TAG = BluetoothMapbMessageMimeTest.class.getSimpleName(); + private static final long TEST_DATE = 1; + private static final String TEST_SUBJECT = "test_subject"; + private static final String TEST_MESSAGE_ID = "test_message_id"; + private static final String TEST_CONTENT_TYPE = "text/plain"; + private static final boolean TEST_TEXT_ONLY = true; + private static final boolean TEST_INCLUDE_ATTACHMENTS = true; + + private final SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", + Locale.US); + private final Date date = new Date(TEST_DATE); + + private final ArrayList TEST_FROM = new ArrayList<>( + Arrays.asList(new Rfc822Token("from_name", "from_address", null))); + private final ArrayList TEST_SENDER = new ArrayList<>( + Arrays.asList(new Rfc822Token("sender_name", "sender_address", null))); + private static final ArrayList TEST_TO = new ArrayList<>( + Arrays.asList(new Rfc822Token("to_name", "to_address", null))); + private static final ArrayList TEST_CC = new ArrayList<>( + Arrays.asList(new Rfc822Token("cc_name", "cc_address", null))); + private final ArrayList TEST_BCC = new ArrayList<>( + Arrays.asList(new Rfc822Token("bcc_name", "bcc_address", null))); + private final ArrayList TEST_REPLY_TO = new ArrayList<>( + Arrays.asList(new Rfc822Token("reply_to_name", "reply_to_address", null))); + + private BluetoothMapbMessageMime mMime; + + @Before + public void setUp() { + mMime = new BluetoothMapbMessageMime(); + + mMime.setSubject(TEST_SUBJECT); + mMime.setDate(TEST_DATE); + mMime.setMessageId(TEST_MESSAGE_ID); + mMime.setContentType(TEST_CONTENT_TYPE); + mMime.setTextOnly(TEST_TEXT_ONLY); + mMime.setIncludeAttachments(TEST_INCLUDE_ATTACHMENTS); + + mMime.setFrom(TEST_FROM); + mMime.setSender(TEST_SENDER); + mMime.setTo(TEST_TO); + mMime.setCc(TEST_CC); + mMime.setBcc(TEST_BCC); + mMime.setReplyTo(TEST_REPLY_TO); + + mMime.addMimePart(); + } + + @Test + public void testGetters() { + assertThat(mMime.getSubject()).isEqualTo(TEST_SUBJECT); + assertThat(mMime.getDate()).isEqualTo(TEST_DATE); + assertThat(mMime.getMessageId()).isEqualTo(TEST_MESSAGE_ID); + assertThat(mMime.getDateString()).isEqualTo(format.format(date)); + assertThat(mMime.getContentType()).isEqualTo(TEST_CONTENT_TYPE); + assertThat(mMime.getTextOnly()).isEqualTo(TEST_TEXT_ONLY); + assertThat(mMime.getIncludeAttachments()).isEqualTo(TEST_INCLUDE_ATTACHMENTS); + + assertThat(mMime.getFrom()).isEqualTo(TEST_FROM); + assertThat(mMime.getSender()).isEqualTo(TEST_SENDER); + assertThat(mMime.getTo()).isEqualTo(TEST_TO); + assertThat(mMime.getCc()).isEqualTo(TEST_CC); + assertThat(mMime.getBcc()).isEqualTo(TEST_BCC); + assertThat(mMime.getReplyTo()).isEqualTo(TEST_REPLY_TO); + + assertThat(mMime.getMimeParts().size()).isEqualTo(1); + } + + @Test + public void testGetSize() { + mMime.getMimeParts().get(0).mData = new byte[10]; + assertThat(mMime.getSize()).isEqualTo(10); + } + + @Test + public void testUpdateCharset() { + mMime.getMimeParts().get(0).mContentType = TEST_CONTENT_TYPE/*="text/plain*/; + mMime.updateCharset(); + assertThat(mMime.mCharset).isEqualTo("UTF-8"); + } + + @Test + public void testAddFrom() { + final BluetoothMapbMessageMime mime = new BluetoothMapbMessageMime(); + final String nameToAdd = "name_to_add"; + final String addressToAdd = "address_to_add"; + mime.addFrom(nameToAdd, addressToAdd); + assertThat(mime.getFrom().get(0)).isEqualTo(new Rfc822Token(nameToAdd, addressToAdd, null)); + } + + @Test + public void testAddSender() { + final BluetoothMapbMessageMime mime = new BluetoothMapbMessageMime(); + final String nameToAdd = "name_to_add"; + final String addressToAdd = "address_to_add"; + mime.addSender(nameToAdd, addressToAdd); + assertThat(mime.getSender().get(0)).isEqualTo( + new Rfc822Token(nameToAdd, addressToAdd, null)); + } + + @Test + public void testAddTo() { + final BluetoothMapbMessageMime mime = new BluetoothMapbMessageMime(); + final String nameToAdd = "name_to_add"; + final String addressToAdd = "address_to_add"; + mime.addTo(nameToAdd, addressToAdd); + assertThat(mime.getTo().get(0)).isEqualTo(new Rfc822Token(nameToAdd, addressToAdd, null)); + } + + @Test + public void testAddCc() { + final BluetoothMapbMessageMime mime = new BluetoothMapbMessageMime(); + final String nameToAdd = "name_to_add"; + final String addressToAdd = "address_to_add"; + mime.addCc(nameToAdd, addressToAdd); + assertThat(mime.getCc().get(0)).isEqualTo(new Rfc822Token(nameToAdd, addressToAdd, null)); + } + + @Test + public void testAddBcc() { + final BluetoothMapbMessageMime mime = new BluetoothMapbMessageMime(); + final String nameToAdd = "name_to_add"; + final String addressToAdd = "address_to_add"; + mime.addBcc(nameToAdd, addressToAdd); + assertThat(mime.getBcc().get(0)).isEqualTo(new Rfc822Token(nameToAdd, addressToAdd, null)); + } + + @Test + public void testAddReplyTo() { + final BluetoothMapbMessageMime mime = new BluetoothMapbMessageMime(); + final String nameToAdd = "name_to_add"; + final String addressToAdd = "address_to_add"; + mime.addReplyTo(nameToAdd, addressToAdd); + assertThat(mime.getReplyTo().get(0)).isEqualTo( + new Rfc822Token(nameToAdd, addressToAdd, null)); + } + + @Test + public void testEncode_ThenCreateByParsing_ReturnsCorrectly() throws Exception { + mMime.setType(BluetoothMapUtils.TYPE.EMAIL); + mMime.setFolder("placeholder"); + byte[] encodedMime = mMime.encodeMime(); + + final BluetoothMapbMessageMime mimeToCreateByParsing = new BluetoothMapbMessageMime(); + mimeToCreateByParsing.parseMsgPart(new String(encodedMime)); + + assertThat(mimeToCreateByParsing.getSubject()).isEqualTo(TEST_SUBJECT); + assertThat(mimeToCreateByParsing.getMessageId()).isEqualTo(TEST_MESSAGE_ID); + assertThat(mimeToCreateByParsing.getContentType()).isEqualTo(TEST_CONTENT_TYPE); + + assertThat(mimeToCreateByParsing.getFrom().get(0).getName()).isEqualTo( + TEST_FROM.get(0).getName()); + assertThat(mimeToCreateByParsing.getFrom().get(0).getAddress()).isEqualTo( + TEST_FROM.get(0).getAddress()); + + assertThat(mimeToCreateByParsing.getTo().get(0).getName()).isEqualTo( + TEST_TO.get(0).getName()); + assertThat(mimeToCreateByParsing.getTo().get(0).getAddress()).isEqualTo( + TEST_TO.get(0).getAddress()); + + assertThat(mimeToCreateByParsing.getCc().get(0).getName()).isEqualTo( + TEST_CC.get(0).getName()); + assertThat(mimeToCreateByParsing.getCc().get(0).getAddress()).isEqualTo( + TEST_CC.get(0).getAddress()); + + assertThat(mimeToCreateByParsing.getBcc().get(0).getName()).isEqualTo( + TEST_BCC.get(0).getName()); + assertThat(mimeToCreateByParsing.getBcc().get(0).getAddress()).isEqualTo( + TEST_BCC.get(0).getAddress()); + + assertThat(mimeToCreateByParsing.getReplyTo().get(0).getName()).isEqualTo( + TEST_REPLY_TO.get(0).getName()); + assertThat(mimeToCreateByParsing.getReplyTo().get(0).getAddress()).isEqualTo( + TEST_REPLY_TO.get(0).getAddress()); + } + @Test public void testParseNullMsgPart_NoExceptionsThrown() { BluetoothMapbMessageMime bMessageMime = new BluetoothMapbMessageMime(); -- GitLab From 48b6c755da19a63cb48bbc6cff161a2e087bba57 Mon Sep 17 00:00:00 2001 From: Kihong Seong Date: Wed, 12 Oct 2022 09:07:54 +0000 Subject: [PATCH 072/667] Add BluetoothMapMessageListingTest Bug: 237467631 Test: atest BluetoothMapMessageListingTest Change-Id: I8d243f02d1fc7d3631be927a211899760eb5a2c2 (cherry picked from commit e5bf4b4793d34e36d5105308af616d52a73f2158) --- .../map/BluetoothMapMessageListing.java | 18 +- .../map/BluetoothMapMessageListingTest.java | 200 ++++++++++++++++++ 2 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapMessageListing.java b/android/app/src/com/android/bluetooth/map/BluetoothMapMessageListing.java index 3a199676306..96427be11c7 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapMessageListing.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapMessageListing.java @@ -18,6 +18,7 @@ import android.util.Log; import android.util.Xml; import com.android.bluetooth.DeviceWorkArounds; +import com.android.bluetooth.Utils; import org.xmlpull.v1.XmlSerializer; @@ -90,9 +91,15 @@ public class BluetoothMapMessageListing { public byte[] encode(boolean includeThreadId, String version) throws UnsupportedEncodingException { StringWriter sw = new StringWriter(); - boolean isBenzCarkit = DeviceWorkArounds.addressStartsWith( - BluetoothMapService.getRemoteDevice().getAddress(), - DeviceWorkArounds.MERCEDES_BENZ_CARKIT); + boolean isBenzCarkit; + + if (Utils.isInstrumentationTestMode()) { + isBenzCarkit = false; + } else { + isBenzCarkit = DeviceWorkArounds.addressStartsWith( + BluetoothMapService.getRemoteDevice().getAddress(), + DeviceWorkArounds.MERCEDES_BENZ_CARKIT); + } try { XmlSerializer xmlMsgElement = Xml.newSerializer(); xmlMsgElement.setOutput(sw); @@ -121,8 +128,9 @@ public class BluetoothMapMessageListing { Log.w(TAG, e); } /* Fix IOT issue to replace '&' by '&', < by < and '> by '>' in MessageListing */ - if (DeviceWorkArounds.addressStartsWith(BluetoothMapService.getRemoteDevice().getAddress(), - DeviceWorkArounds.BREZZA_ZDI_CARKIT)) { + if (!Utils.isInstrumentationTestMode() && DeviceWorkArounds.addressStartsWith( + BluetoothMapService.getRemoteDevice().getAddress(), + DeviceWorkArounds.BREZZA_ZDI_CARKIT)) { return sw.toString() .replaceAll("&", "&") .replaceAll("<", "<") diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java new file mode 100644 index 00000000000..51fdb486dd2 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java @@ -0,0 +1,200 @@ +/* + * 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. + */ + +package com.android.bluetooth.map; + +import static com.google.common.truth.Truth.assertThat; + +import android.util.Xml; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.bluetooth.Utils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + +@RunWith(AndroidJUnit4.class) +public class BluetoothMapMessageListingTest { + private static final long TEST_DATE_TIME_EARLIEST = 0; + private static final long TEST_DATE_TIME_MIDDLE = 1; + private static final long TEST_DATE_TIME_LATEST = 2; + private static final boolean TEST_READ = true; + private static final boolean TEST_REPORT_READ = true; + private static final String TEST_VERSION = "test_version"; + + private final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); + private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss"); + + private BluetoothMapMessageListingElement mListingElementEarliestWithReadFalse; + private BluetoothMapMessageListingElement mListingElementMiddleWithReadFalse; + private BluetoothMapMessageListingElement mListingElementLatestWithReadTrue; + + private BluetoothMapMessageListing mListing; + + @Before + public void setUp() { + mListingElementEarliestWithReadFalse = new BluetoothMapMessageListingElement(); + mListingElementEarliestWithReadFalse.setDateTime(TEST_DATE_TIME_EARLIEST); + + mListingElementMiddleWithReadFalse = new BluetoothMapMessageListingElement(); + mListingElementMiddleWithReadFalse.setDateTime(TEST_DATE_TIME_MIDDLE); + + mListingElementLatestWithReadTrue = new BluetoothMapMessageListingElement(); + mListingElementLatestWithReadTrue.setDateTime(TEST_DATE_TIME_LATEST); + mListingElementLatestWithReadTrue.setRead(TEST_READ, TEST_REPORT_READ); + + mListing = new BluetoothMapMessageListing(); + mListing.add(mListingElementEarliestWithReadFalse); + mListing.add(mListingElementMiddleWithReadFalse); + mListing.add(mListingElementLatestWithReadTrue); + } + + @Test + public void addElement() { + final BluetoothMapMessageListing listing = new BluetoothMapMessageListing(); + assertThat(listing.getCount()).isEqualTo(0); + listing.add(mListingElementEarliestWithReadFalse); + assertThat(listing.getCount()).isEqualTo(1); + assertThat(listing.hasUnread()).isEqualTo(true); + } + + @Test + public void segment_whenCountIsLessThanOne_returnsOffsetToEnd() { + mListing.segment(0, 1); + assertThat(mListing.getList().size()).isEqualTo(2); + } + + @Test + public void segment_whenOffsetIsBiggerThanSize_returnsEmptyList() { + mListing.segment(1, 4); + assertThat(mListing.getList().size()).isEqualTo(0); + } + + @Test + public void segment_whenOffsetCountCombinationIsValid_returnsCorrectly() { + mListing.segment(1, 1); + assertThat(mListing.getList().size()).isEqualTo(1); + } + + @Test + public void sort() { + // BluetoothMapMessageListingElements are sorted according to their mDateTime values + mListing.sort(); + assertThat(mListing.getList().get(0).getDateTime()).isEqualTo(TEST_DATE_TIME_LATEST); + assertThat(mListing.getList().get(1).getDateTime()).isEqualTo(TEST_DATE_TIME_MIDDLE); + assertThat(mListing.getList().get(2).getDateTime()).isEqualTo(TEST_DATE_TIME_EARLIEST); + } + + @Test + public void encodeToXml_thenAppendFromXml() throws Exception { + final BluetoothMapMessageListing listingToAppend = new BluetoothMapMessageListing(); + final BluetoothMapMessageListingElement listingElementToAppendOne = + new BluetoothMapMessageListingElement(); + final BluetoothMapMessageListingElement listingElementToAppendTwo = + new BluetoothMapMessageListingElement(); + + listingElementToAppendOne.setDateTime(TEST_DATE_TIME_EARLIEST); + listingElementToAppendTwo.setRead(TEST_READ, TEST_REPORT_READ); + + listingToAppend.add(listingElementToAppendOne); + listingToAppend.add(listingElementToAppendTwo); + + assertThat(listingToAppend.getList().size()).isEqualTo(2); + + final InputStream listingStream = new ByteArrayInputStream( + listingToAppend.encode(false, TEST_VERSION)); + + BluetoothMapMessageListing listing = new BluetoothMapMessageListing(); + appendFromXml(listingStream, listing); + assertThat(listing.getList().size()).isEqualTo(2); + assertThat(listing.getList().get(0).getDateTime()).isEqualTo(TEST_DATE_TIME_EARLIEST); + assertThat(listing.getList().get(1).getReadBool()).isTrue(); + } + + /** + * Decodes the encoded xml document then append the BluetoothMapMessageListingElements to the + * given BluetoothMapMessageListing object. + */ + private void appendFromXml(InputStream xmlDocument, BluetoothMapMessageListing newListing) + throws XmlPullParserException, IOException { + try { + XmlPullParser parser = Xml.newPullParser(); + int type; + parser.setInput(xmlDocument, "UTF-8"); + + while ((type = parser.next()) != XmlPullParser.END_TAG + && type != XmlPullParser.END_DOCUMENT) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (!name.equalsIgnoreCase("MAP-msg-listing")) { + Utils.skipCurrentTag(parser); + } + readMessageElements(parser, newListing); + } + } finally { + xmlDocument.close(); + } + } + + private void readMessageElements(XmlPullParser parser, BluetoothMapMessageListing newListing) + throws XmlPullParserException, IOException { + int type; + while ((type = parser.next()) != XmlPullParser.END_TAG + && type != XmlPullParser.END_DOCUMENT) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (!name.trim().equalsIgnoreCase("msg")) { + Utils.skipCurrentTag(parser); + continue; + } + newListing.add(createFromXml(parser)); + } + } + + private BluetoothMapMessageListingElement createFromXml(XmlPullParser parser) + throws XmlPullParserException, IOException { + BluetoothMapMessageListingElement newElement = new BluetoothMapMessageListingElement(); + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + String attributeName = parser.getAttributeName(i).trim(); + String attributeValue = parser.getAttributeValue(i); + if (attributeName.equalsIgnoreCase("datetime")) { + newElement.setDateTime(LocalDateTime.parse(attributeValue, formatter).toInstant( + ZoneOffset.ofTotalSeconds(0)).toEpochMilli()); + } else if (attributeName.equalsIgnoreCase("read")) { + newElement.setRead(true, true); + } + } + parser.nextTag(); + return newElement; + } +} \ No newline at end of file -- GitLab From 0b550610a9d74b001b77cfbd4f6987df528cf6c0 Mon Sep 17 00:00:00 2001 From: Patty Huang Date: Thu, 6 Oct 2022 17:58:12 +0800 Subject: [PATCH 073/667] Add LE audio broadcast dynamic switch feature If the device set ro.bluetooth.leaudio_broadcast_switcher.supported=true and the hardware supports le audio required capabilities, the feature could be switched via developer option Bug: 236907310 Test: switch LE audio feature, and check LE audio functionality status Change-Id: I2e5c4cdce2c2b52ae7bd696cf7752410d995d228 Merged-In: I2e5c4cdce2c2b52ae7bd696cf7752410d995d228 (cherry picked from commit 8a4d935e4edc17e14b81ad720ea68b448dac1dbd) --- .../bluetooth/btservice/AdapterService.java | 15 ++++++--- .../android/bluetooth/btservice/Config.java | 32 ++++++++++++++++--- .../bluetooth/le_audio/LeAudioService.java | 8 ++++- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index 416e92ad289..11a0724b435 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -749,8 +749,9 @@ public class AdapterService extends Service { nonSupportedProfiles.add(BassClientService.class); } - if (isLeAudioBroadcastSourceSupported()) { - Config.addSupportedProfile(BluetoothProfile.LE_AUDIO_BROADCAST); + if (!isLeAudioBroadcastSourceSupported()) { + Config.updateSupportedProfileMask( + false, LeAudioService.class, BluetoothProfile.LE_AUDIO_BROADCAST); } if (!nonSupportedProfiles.isEmpty()) { @@ -3399,7 +3400,8 @@ public class AdapterService extends Service { return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } - if (service.isLeAudioBroadcastSourceSupported()) { + long supportBitMask = Config.getSupportedProfilesBitMask(); + if ((supportBitMask & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) != 0) { return BluetoothStatusCodes.FEATURE_SUPPORTED; } @@ -4656,8 +4658,7 @@ public class AdapterService extends Service { * @return true, if the LE audio broadcast source is supported */ public boolean isLeAudioBroadcastSourceSupported() { - return BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false) - && mAdapterProperties.isLePeriodicAdvertisingSupported() + return mAdapterProperties.isLePeriodicAdvertisingSupported() && mAdapterProperties.isLeExtendedAdvertisingSupported() && mAdapterProperties.isLeIsochronousBroadcasterSupported(); } @@ -4674,6 +4675,10 @@ public class AdapterService extends Service { || mAdapterProperties.isLePeriodicAdvertisingSyncTransferRecipientSupported()); } + public long getSupportedProfilesBitMask() { + return Config.getSupportedProfilesBitMask(); + } + /** * Check if the LE audio CIS central feature is supported. * diff --git a/android/app/src/com/android/bluetooth/btservice/Config.java b/android/app/src/com/android/bluetooth/btservice/Config.java index 1c021591bbb..c1493294a8b 100644 --- a/android/app/src/com/android/bluetooth/btservice/Config.java +++ b/android/app/src/com/android/bluetooth/btservice/Config.java @@ -60,10 +60,11 @@ public class Config { private static final String FEATURE_HEARING_AID = "settings_bluetooth_hearing_aid"; private static final String FEATURE_BATTERY = "settings_bluetooth_battery"; - private static long sSupportedMask = 0; private static final String LE_AUDIO_DYNAMIC_SWITCH_PROPERTY = "ro.bluetooth.leaudio_switcher.supported"; + private static final String LE_AUDIO_BROADCAST_DYNAMIC_SWITCH_PROPERTY = + "ro.bluetooth.leaudio_broadcast_switcher.supported"; private static final String LE_AUDIO_DYNAMIC_ENABLED_PROPERTY = "persist.bluetooth.leaudio_switcher.enabled"; @@ -165,6 +166,11 @@ public class Config { private static boolean sIsGdEnabledUptoScanningLayer = false; static void init(Context ctx) { + if (LeAudioService.isBroadcastEnabled()) { + updateSupportedProfileMask( + true, LeAudioService.class, BluetoothProfile.LE_AUDIO_BROADCAST); + } + final boolean leAudioDynamicSwitchSupported = SystemProperties.getBoolean(LE_AUDIO_DYNAMIC_SWITCH_PROPERTY, false); @@ -205,6 +211,15 @@ public class Config { setProfileEnabled(TbsService.class, enable); setProfileEnabled(McpService.class, enable); setProfileEnabled(VolumeControlService.class, enable); + + final boolean broadcastDynamicSwitchSupported = + SystemProperties.getBoolean(LE_AUDIO_BROADCAST_DYNAMIC_SWITCH_PROPERTY, false); + + if (broadcastDynamicSwitchSupported) { + setProfileEnabled(BassClientService.class, enable); + updateSupportedProfileMask( + enable, LeAudioService.class, BluetoothProfile.LE_AUDIO_BROADCAST); + } } /** @@ -226,8 +241,17 @@ public class Config { sSupportedProfiles = profilesList.toArray(new Class[profilesList.size()]); } - static void addSupportedProfile(int supportedProfile) { - sSupportedMask |= (1 << supportedProfile); + static void updateSupportedProfileMask(Boolean enable, Class profile, int supportedProfile) { + for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) { + if (config.mClass == profile) { + if (enable) { + config.mMask |= 1 << supportedProfile; + } else { + config.mMask &= ~(1 << supportedProfile); + } + return; + } + } } static HashSet geLeAudioUnicastProfiles() { @@ -253,7 +277,7 @@ public class Config { } static long getSupportedProfilesBitMask() { - long mask = sSupportedMask; + long mask = 0; for (final Class profileClass : getSupportedProfiles()) { mask |= getProfileMask(profileClass); } 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 7995e461d94..5f14bdffa46 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -194,6 +194,10 @@ public class LeAudioService extends ProfileService { return BluetoothProperties.isProfileBapUnicastClientEnabled().orElse(false); } + public static boolean isBroadcastEnabled() { + return BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false); + } + @Override protected void create() { Log.i(TAG, "create()"); @@ -252,7 +256,9 @@ public class LeAudioService extends ProfileService { LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG | LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS; // Initialize Broadcast native interface - if (mAdapterService.isLeAudioBroadcastSourceSupported()) { + if ((mAdapterService.getSupportedProfilesBitMask() + & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) != 0) { + Log.i(TAG, "Init Le Audio broadcaster"); mBroadcastCallbacks = new RemoteCallbackList(); mLeAudioBroadcasterNativeInterface = Objects.requireNonNull( LeAudioBroadcasterNativeInterface.getInstance(), -- GitLab From 3260385ab6b15570cb84ce17dd0ffea921c9c705 Mon Sep 17 00:00:00 2001 From: Chris Manton Date: Wed, 12 Oct 2022 19:38:39 -0700 Subject: [PATCH 074/667] Delete unused l2c_link_process_num_completed_pkts Bug: 253341939 Test: gd/cert/run Tag: #refactor BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines Ignore-AOSP-First: Cherry-pick Merged-In: I3e3ce6842025bc229c064173fdb45f44ddc07b73 Change-Id: I3e3ce6842025bc229c064173fdb45f44ddc07b73 --- system/stack/include/l2cap_acl_interface.h | 2 - system/stack/l2cap/l2c_link.cc | 106 --------------------- system/test/mock/mock_stack_l2cap_link.cc | 3 - 3 files changed, 111 deletions(-) diff --git a/system/stack/include/l2cap_acl_interface.h b/system/stack/include/l2cap_acl_interface.h index b534f1baa0d..aa0a86c5ca9 100644 --- a/system/stack/include/l2cap_acl_interface.h +++ b/system/stack/include/l2cap_acl_interface.h @@ -46,6 +46,4 @@ extern void l2c_link_hci_conn_req(const RawAddress& bd_addr); extern void l2cu_resubmit_pending_sec_req(const RawAddress* p_bda); -extern void l2c_link_process_num_completed_pkts(uint8_t* p, uint8_t evt_len); - extern void l2c_packets_completed(uint16_t handle, uint16_t num_sent); diff --git a/system/stack/l2cap/l2c_link.cc b/system/stack/l2cap/l2c_link.cc index ea8dd07becf..0053cd85383 100644 --- a/system/stack/l2cap/l2c_link.cc +++ b/system/stack/l2cap/l2c_link.cc @@ -1094,112 +1094,6 @@ static void l2c_link_send_to_lower(tL2C_LCB* p_lcb, BT_HDR* p_buf) { } } -/******************************************************************************* - * - * Function l2c_link_process_num_completed_pkts - * - * Description This function is called when a "number-of-completed-packets" - * event is received from the controller. It updates all the - * LCB transmit counts. - * - * Returns void - * - ******************************************************************************/ -void l2c_link_process_num_completed_pkts(uint8_t* p, uint8_t evt_len) { - if (bluetooth::shim::is_gd_l2cap_enabled()) { - return; - } - uint8_t num_handles, xx; - uint16_t handle; - uint16_t num_sent; - tL2C_LCB* p_lcb; - - if (evt_len > 0) { - STREAM_TO_UINT8(num_handles, p); - } else { - num_handles = 0; - } - - if (num_handles > evt_len / (2 * sizeof(uint16_t))) { - android_errorWriteLog(0x534e4554, "141617601"); - num_handles = evt_len / (2 * sizeof(uint16_t)); - } - - for (xx = 0; xx < num_handles; xx++) { - STREAM_TO_UINT16(handle, p); - /* Extract the handle */ - handle = HCID_GET_HANDLE(handle); - STREAM_TO_UINT16(num_sent, p); - - p_lcb = l2cu_find_lcb_by_handle(handle); - - if (p_lcb) { - if (p_lcb && (p_lcb->transport == BT_TRANSPORT_LE)) - l2cb.controller_le_xmit_window += num_sent; - else { - /* Maintain the total window to the controller */ - l2cb.controller_xmit_window += num_sent; - } - /* If doing round-robin, adjust communal counts */ - if (p_lcb->link_xmit_quota == 0) { - if (p_lcb->transport == BT_TRANSPORT_LE) { - /* Don't go negative */ - if (l2cb.ble_round_robin_unacked > num_sent) - l2cb.ble_round_robin_unacked -= num_sent; - else - l2cb.ble_round_robin_unacked = 0; - } else { - /* Don't go negative */ - if (l2cb.round_robin_unacked > num_sent) - l2cb.round_robin_unacked -= num_sent; - else - l2cb.round_robin_unacked = 0; - } - } - - /* Don't go negative */ - if (p_lcb->sent_not_acked > num_sent) - p_lcb->sent_not_acked -= num_sent; - else - p_lcb->sent_not_acked = 0; - - l2c_link_check_send_pkts(p_lcb, 0, NULL); - - /* If we were doing round-robin for low priority links, check 'em */ - if ((p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) && - (l2cb.check_round_robin) && - (l2cb.round_robin_unacked < l2cb.round_robin_quota)) { - l2c_link_check_send_pkts(NULL, 0, NULL); - } - if ((p_lcb->transport == BT_TRANSPORT_LE) && - (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) && - ((l2cb.ble_check_round_robin) && - (l2cb.ble_round_robin_unacked < l2cb.ble_round_robin_quota))) { - l2c_link_check_send_pkts(NULL, 0, NULL); - } - } - - if (p_lcb) { - if (p_lcb->transport == BT_TRANSPORT_LE) { - LOG_DEBUG("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d", - l2cb.controller_le_xmit_window, p_lcb->Handle(), - p_lcb->sent_not_acked, l2cb.ble_check_round_robin, - l2cb.ble_round_robin_unacked); - } else { - LOG_DEBUG("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d", - l2cb.controller_xmit_window, p_lcb->Handle(), - p_lcb->sent_not_acked, l2cb.check_round_robin, - l2cb.round_robin_unacked); - } - } else { - LOG_DEBUG("TotalWin=%d LE_Win: %d, Handle=0x%x, RRCheck=%d, RRUnack=%d", - l2cb.controller_xmit_window, l2cb.controller_le_xmit_window, - handle, l2cb.ble_check_round_robin, - l2cb.ble_round_robin_unacked); - } - } -} - void l2c_packets_completed(uint16_t handle, uint16_t num_sent) { tL2C_LCB* p_lcb = l2cu_find_lcb_by_handle(handle); if (p_lcb == nullptr) { diff --git a/system/test/mock/mock_stack_l2cap_link.cc b/system/test/mock/mock_stack_l2cap_link.cc index 87a6eaa407b..2f86036bf29 100644 --- a/system/test/mock/mock_stack_l2cap_link.cc +++ b/system/test/mock/mock_stack_l2cap_link.cc @@ -67,9 +67,6 @@ void l2c_link_hci_conn_req(const RawAddress& bd_addr) { mock_function_count_map[__func__]++; } void l2c_link_init() { mock_function_count_map[__func__]++; } -void l2c_link_process_num_completed_pkts(uint8_t* p, uint8_t evt_len) { - mock_function_count_map[__func__]++; -} void l2c_link_role_changed(const RawAddress* bd_addr, uint8_t new_role, uint8_t hci_status) { mock_function_count_map[__func__]++; -- GitLab From ec252f12b177640e857bd9c4e0846c2816cd26d4 Mon Sep 17 00:00:00 2001 From: Chris Manton Date: Thu, 13 Oct 2022 09:03:40 -0700 Subject: [PATCH 075/667] Delete unused acl_process_num_completed_pkts Bug: 253341939 Test: gd/cert/run Tag: #refactor BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines Ignore-AOSP-First: Cherry-pick Merged-In: I3f2c502ea80d8787ac4a00ec847ad248c335d5b3 Change-Id: I3f2c502ea80d8787ac4a00ec847ad248c335d5b3 --- system/stack/acl/btm_acl.cc | 29 ------------------- system/stack/btu/btu_hcif.cc | 4 +-- system/stack/include/acl_hci_link_interface.h | 1 - system/test/mock/mock_stack_acl.cc | 5 ---- system/test/mock/mock_stack_acl.h | 9 ------ 5 files changed, 1 insertion(+), 47 deletions(-) diff --git a/system/stack/acl/btm_acl.cc b/system/stack/acl/btm_acl.cc index 16ee57b5a95..e242c536701 100644 --- a/system/stack/acl/btm_acl.cc +++ b/system/stack/acl/btm_acl.cc @@ -2739,35 +2739,6 @@ void acl_packets_completed(uint16_t handle, uint16_t credits) { credits); } -static void acl_parse_num_completed_pkts(uint8_t* p, uint8_t evt_len) { - if (evt_len == 0) { - LOG_ERROR("Received num completed packets with zero length"); - return; - } - - uint8_t num_handles{0}; - STREAM_TO_UINT8(num_handles, p); - - if (num_handles > evt_len / (2 * sizeof(uint16_t))) { - android_errorWriteLog(0x534e4554, "141617601"); - num_handles = evt_len / (2 * sizeof(uint16_t)); - } - - for (uint8_t xx = 0; xx < num_handles; xx++) { - uint16_t handle{0}; - uint16_t num_packets{0}; - STREAM_TO_UINT16(handle, p); - handle = HCID_GET_HANDLE(handle); - STREAM_TO_UINT16(num_packets, p); - acl_packets_completed(handle, num_packets); - } -} - -void acl_process_num_completed_pkts(uint8_t* p, uint8_t evt_len) { - acl_parse_num_completed_pkts(p, evt_len); - bluetooth::hci::IsoManager::GetInstance()->HandleNumComplDataPkts(p, evt_len); -} - void acl_process_supported_features(uint16_t handle, uint64_t features) { tACL_CONN* p_acl = internal_.acl_get_connection_from_handle(handle); if (p_acl == nullptr) { diff --git a/system/stack/btu/btu_hcif.cc b/system/stack/btu/btu_hcif.cc index 4d24c481504..e76f81ebe2d 100644 --- a/system/stack/btu/btu_hcif.cc +++ b/system/stack/btu/btu_hcif.cc @@ -282,9 +282,6 @@ void btu_hcif_process_event(UNUSED_ATTR uint8_t controller_id, case HCI_HARDWARE_ERROR_EVT: btu_hcif_hardware_error_evt(p); break; - case HCI_NUM_COMPL_DATA_PKTS_EVT: - acl_process_num_completed_pkts(p, hci_evt_len); - break; case HCI_MODE_CHANGE_EVT: btu_hcif_mode_change_evt(p); break; @@ -423,6 +420,7 @@ void btu_hcif_process_event(UNUSED_ATTR uint8_t controller_id, break; // Events now captured by gd::hci_layer module + case HCI_NUM_COMPL_DATA_PKTS_EVT: // EventCode::NUMBER_OF_COMPLETED_PACKETS case HCI_CONNECTION_COMP_EVT: // EventCode::CONNECTION_COMPLETE case HCI_READ_RMT_FEATURES_COMP_EVT: // EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE case HCI_READ_RMT_VERSION_COMP_EVT: // EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE diff --git a/system/stack/include/acl_hci_link_interface.h b/system/stack/include/acl_hci_link_interface.h index 781c7bd4e0f..f109b56750b 100644 --- a/system/stack/include/acl_hci_link_interface.h +++ b/system/stack/include/acl_hci_link_interface.h @@ -67,7 +67,6 @@ void btm_read_tx_power_complete(uint8_t* p, bool is_ble); void acl_rcv_acl_data(BT_HDR* p_msg); void acl_link_segments_xmitted(BT_HDR* p_msg); -void acl_process_num_completed_pkts(uint8_t* p, uint8_t evt_len); void acl_packets_completed(uint16_t handle, uint16_t num_packets); void acl_process_supported_features(uint16_t handle, uint64_t features); void acl_process_extended_features(uint16_t handle, uint8_t current_page_number, diff --git a/system/test/mock/mock_stack_acl.cc b/system/test/mock/mock_stack_acl.cc index 278f951b5eb..91ee709ccb9 100644 --- a/system/test/mock/mock_stack_acl.cc +++ b/system/test/mock/mock_stack_acl.cc @@ -114,7 +114,6 @@ struct acl_disconnect_from_handle acl_disconnect_from_handle; struct acl_link_segments_xmitted acl_link_segments_xmitted; struct acl_packets_completed acl_packets_completed; struct acl_process_extended_features acl_process_extended_features; -struct acl_process_num_completed_pkts acl_process_num_completed_pkts; struct acl_process_supported_features acl_process_supported_features; struct acl_rcv_acl_data acl_rcv_acl_data; struct acl_reject_connection_request acl_reject_connection_request; @@ -476,10 +475,6 @@ void acl_process_extended_features(uint16_t handle, uint8_t current_page_number, test::mock::stack_acl::acl_process_extended_features( handle, current_page_number, max_page_number, features); } -void acl_process_num_completed_pkts(uint8_t* p, uint8_t evt_len) { - mock_function_count_map[__func__]++; - test::mock::stack_acl::acl_process_num_completed_pkts(p, evt_len); -} void acl_process_supported_features(uint16_t handle, uint64_t features) { mock_function_count_map[__func__]++; test::mock::stack_acl::acl_process_supported_features(handle, features); diff --git a/system/test/mock/mock_stack_acl.h b/system/test/mock/mock_stack_acl.h index d03d6383bc6..8747ed7927e 100644 --- a/system/test/mock/mock_stack_acl.h +++ b/system/test/mock/mock_stack_acl.h @@ -751,15 +751,6 @@ struct acl_process_extended_features { }; }; extern struct acl_process_extended_features acl_process_extended_features; -// Name: acl_process_num_completed_pkts -// Params: uint8_t* p, uint8_t evt_len -// Returns: void -struct acl_process_num_completed_pkts { - std::function body{ - [](uint8_t* p, uint8_t evt_len) { ; }}; - void operator()(uint8_t* p, uint8_t evt_len) { body(p, evt_len); }; -}; -extern struct acl_process_num_completed_pkts acl_process_num_completed_pkts; // Name: acl_process_supported_features // Params: uint16_t handle, uint64_t features // Returns: void -- GitLab From 26ae22975194af30ebb4d39d3becdc1442f7b654 Mon Sep 17 00:00:00 2001 From: Oriol Prieto Gasco Date: Thu, 7 Jul 2022 20:27:56 +0000 Subject: [PATCH 076/667] Make the btservices apex updatable. Also, use the new name "com.android.btservices" Bug: 237556594 Test: TH Ignore-AOSP-First: LSC Merged-In: If2443eadfc457da6e040ea3425ed1d5b6f73177a Change-Id: If2443eadfc457da6e040ea3425ed1d5b6f73177a (cherry picked from commit 4c187daf61c768f53eeb2f0666dd9cf663518647) --- AndroidTestTemplate.xml | 2 +- android/app/tests/unit/AndroidTest.xml | 2 +- apex/Android.bp | 2 +- framework/tests/unit/AndroidTest.xml | 2 +- service/tests/AndroidTest.xml | 4 ++-- system/gd/AndroidTestTemplate.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/AndroidTestTemplate.xml b/AndroidTestTemplate.xml index 4fb4bf9f76c..cca8d73e471 100644 --- a/AndroidTestTemplate.xml +++ b/AndroidTestTemplate.xml @@ -38,6 +38,6 @@ - diff --git a/android/app/tests/unit/AndroidTest.xml b/android/app/tests/unit/AndroidTest.xml index 97f1b8b95a0..981e0a8434c 100644 --- a/android/app/tests/unit/AndroidTest.xml +++ b/android/app/tests/unit/AndroidTest.xml @@ -38,6 +38,6 @@ - diff --git a/apex/Android.bp b/apex/Android.bp index 3026a4ce015..edece23fbc6 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -80,7 +80,7 @@ apex { ], key: "com.android.btservices.key", certificate: ":com.android.btservices.certificate", - updatable: false, + updatable: true, compressible: false, } diff --git a/framework/tests/unit/AndroidTest.xml b/framework/tests/unit/AndroidTest.xml index b8ca07a2899..75283c016a5 100644 --- a/framework/tests/unit/AndroidTest.xml +++ b/framework/tests/unit/AndroidTest.xml @@ -31,6 +31,6 @@ - diff --git a/service/tests/AndroidTest.xml b/service/tests/AndroidTest.xml index 1befc15fd5b..3e66360c60f 100644 --- a/service/tests/AndroidTest.xml +++ b/service/tests/AndroidTest.xml @@ -21,7 +21,7 @@ diff --git a/android/app/res/values-zh-rTW/strings.xml b/android/app/res/values-zh-rTW/strings.xml index 3e6c7e929c2..c8ee26c1634 100644 --- a/android/app/res/values-zh-rTW/strings.xml +++ b/android/app/res/values-zh-rTW/strings.xml @@ -131,7 +131,7 @@ "在飛航模式下保持藍牙開啟狀態" "如果你不關閉藍牙,下次手機進入飛航模式時,藍牙將保持開啟狀態" "藍牙會保持開啟狀態" - "手機會記得在飛航模式下繼續開啟藍牙。如要變更此設定,請關閉藍牙。" + "在飛航模式下,手機會保持藍牙開啟。如要變更此設定,請關閉藍牙。" "Wi-Fi 和藍牙會保持開啟狀態" - "手機會記得在飛航模式下繼續開啟 Wi-Fi 和藍牙。如要變更此設定,請關閉這些功能。" + "在飛航模式下,手機會保持 Wi-Fi 和藍牙開啟。如要變更此設定,請關閉這些功能。" -- GitLab From 06f865881ac4fc66b02d373765b33ef9f8f0533b Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Fri, 7 Oct 2022 17:43:42 +0000 Subject: [PATCH 159/667] Replace usage of deprecated coroutines methods This change is necessary to unblock the upgrade of the kotlinx.coroutines library. Bug: 245578454 Test: manual -- all existing tests and builds should pass Ignore-AOSP-First: Cherry-pick from AOSP Merged-In: Ide673105eab416d5a0747189d652ca89ed241bba Change-Id: Ide673105eab416d5a0747189d652ca89ed241bba --- android/pandora/server/src/com/android/pandora/Host.kt | 3 +-- android/pandora/server/src/com/android/pandora/Utils.kt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/android/pandora/server/src/com/android/pandora/Host.kt b/android/pandora/server/src/com/android/pandora/Host.kt index 72e44bada3b..512b234df2d 100644 --- a/android/pandora/server/src/com/android/pandora/Host.kt +++ b/android/pandora/server/src/com/android/pandora/Host.kt @@ -49,7 +49,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.channels.sendBlocking import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -567,7 +566,7 @@ class Host(private val context: Context, private val server: Server) : HostImplB val callback = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult) { - sendBlocking( + trySendBlocking( RunDiscoveryResponse.newBuilder() .setDevice( Device.newBuilder() diff --git a/android/pandora/server/src/com/android/pandora/Utils.kt b/android/pandora/server/src/com/android/pandora/Utils.kt index ad4e46cccbf..091ce6049e7 100644 --- a/android/pandora/server/src/com/android/pandora/Utils.kt +++ b/android/pandora/server/src/com/android/pandora/Utils.kt @@ -179,7 +179,7 @@ fun grpcBidirectionalStream( override fun onNext(req: T) { // Note: this should be made a blocking call, and the handler should run in a separate thread // so we get flow control - but for now we can live with this - if (!inputChannel.offer(req)) { + if (inputChannel.trySend(req).isFailure) { job.cancel(CancellationException("too many incoming requests, buffer exceeded")) responseObserver.onError( CancellationException("too many incoming requests, buffer exceeded") -- GitLab From 2b0e0bd1a14d54f9db5822c45452f92ce899cd33 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Fri, 7 Oct 2022 16:28:14 -0700 Subject: [PATCH 160/667] [Pandora] Fix GATT server test that don't need data. Tag: #feature Bug: 245578454 Test: atest pts-bot:GATT/SR Ignore-AOSP-First: Cherry-pick from AOSP Merged-In: I62a917589d3aa4d1c1b9948d384665c20bee93a9 Change-Id: I62a917589d3aa4d1c1b9948d384665c20bee93a9 --- .../server/src/com/android/pandora/GattServerManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/pandora/server/src/com/android/pandora/GattServerManager.kt b/android/pandora/server/src/com/android/pandora/GattServerManager.kt index cc7d3fd34a0..a1453442cbf 100644 --- a/android/pandora/server/src/com/android/pandora/GattServerManager.kt +++ b/android/pandora/server/src/com/android/pandora/GattServerManager.kt @@ -43,7 +43,7 @@ class GattServerManager( val callback = object : BluetoothGattServerCallback() { override fun onServiceAdded(status: Int, service: BluetoothGattService) { - Log.i(TAG, "onServiceAdded") + Log.i(TAG, "onServiceAdded status=$status") check(status == BluetoothGatt.GATT_SUCCESS) check(newServiceFlow.tryEmit(service)) } @@ -68,7 +68,7 @@ class GattServerManager( ByteArray(negociatedMtu) ) } else { - server.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, ByteArray(512)) + server.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, ByteArray(0)) } } -- GitLab From 990bf743f1a0bc54b019fba12469b872f39bfe28 Mon Sep 17 00:00:00 2001 From: Charlie Boutier Date: Wed, 12 Oct 2022 02:21:53 +0000 Subject: [PATCH 161/667] [avatar_experimental] - Copy avatar from external Copy avatar from external into pandora and mark it as experimental and link it with the interfaces in pandora_experimental. At some point avatar will be only in external/ but currently it is easier to iterate on it and more flexible in avatar_experimental. Bug: 245578454 Test: atest-dev avatar -v Ignore-AOSP-First: Cherry-pick from AOSP Merged-In: I79a2ac247dc37becc61e2fc2158f5836f1894cfe Change-Id: I79a2ac247dc37becc61e2fc2158f5836f1894cfe --- .../pandora/avatar_experimental/Android.bp | 32 +++++ .../avatar_experimental/avatar/__init__.py | 19 +++ .../avatar/android_service.py | 66 +++++++++ .../avatar/bumble_server/__init__.py | 80 +++++++++++ .../avatar/bumble_server/device_config.json | 5 + .../avatar/bumble_server/host.py | 86 ++++++++++++ .../avatar/controllers/__init__.py | 0 .../avatar/controllers/pandora_device.py | 129 ++++++++++++++++++ .../avatar_experimental/avatar/utils.py | 32 +++++ android/pandora/test/Android.bp | 3 +- 10 files changed, 450 insertions(+), 2 deletions(-) create mode 100644 android/pandora/avatar_experimental/Android.bp create mode 100644 android/pandora/avatar_experimental/avatar/__init__.py create mode 100644 android/pandora/avatar_experimental/avatar/android_service.py create mode 100644 android/pandora/avatar_experimental/avatar/bumble_server/__init__.py create mode 100644 android/pandora/avatar_experimental/avatar/bumble_server/device_config.json create mode 100644 android/pandora/avatar_experimental/avatar/bumble_server/host.py create mode 100644 android/pandora/avatar_experimental/avatar/controllers/__init__.py create mode 100644 android/pandora/avatar_experimental/avatar/controllers/pandora_device.py create mode 100644 android/pandora/avatar_experimental/avatar/utils.py diff --git a/android/pandora/avatar_experimental/Android.bp b/android/pandora/avatar_experimental/Android.bp new file mode 100644 index 00000000000..61e7b07b9b7 --- /dev/null +++ b/android/pandora/avatar_experimental/Android.bp @@ -0,0 +1,32 @@ +// 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. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +python_library_host { + name: "libavatar_experimental", + srcs: [ + "avatar/*.py", + "avatar/bumble_server/*.py", + "avatar/controllers/*.py", + ], + libs: [ + "mobly", + "pandora_experimental-python", + "libprotobuf-python", + "bumble", + ], +} diff --git a/android/pandora/avatar_experimental/avatar/__init__.py b/android/pandora/avatar_experimental/avatar/__init__.py new file mode 100644 index 00000000000..8f33b5220b0 --- /dev/null +++ b/android/pandora/avatar_experimental/avatar/__init__.py @@ -0,0 +1,19 @@ +# Copyright 2022 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. +""" +Avatar is a scalable multi-platform Bluetooth testing tool capable of running +any Bluetooth test cases virtually and physically. +""" + +__version__ = "0.0.1" diff --git a/android/pandora/avatar_experimental/avatar/android_service.py b/android/pandora/avatar_experimental/avatar/android_service.py new file mode 100644 index 00000000000..680e4e259e6 --- /dev/null +++ b/android/pandora/avatar_experimental/avatar/android_service.py @@ -0,0 +1,66 @@ +# Copyright 2022 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 time +import threading + +from mobly.controllers.android_device_lib.services.base_service \ + import BaseService + +ANDROID_SERVER_PACKAGE = 'com.android.pandora' +ANDROID_SERVER_GRPC_PORT = 8999 + + +class AndroidService(BaseService): + + def __init__(self, device, configs=None): + super().__init__(device, configs) + self.port = configs['port'] + self._is_alive = False + + @property + def is_alive(self): + return self._is_alive + + def start(self): + # Start Pandora Android gRPC server. + self.instrumentation = threading.Thread(target=lambda: self._device.adb._exec_adb_cmd( + 'shell', + f'am instrument --no-hidden-api-checks -w {ANDROID_SERVER_PACKAGE}/.Main', + shell=False, + timeout=None, + stderr=None)) + + self.instrumentation.start() + + self._device.adb.forward([f'tcp:{self.port}', f'tcp:{ANDROID_SERVER_GRPC_PORT}']) + + # Wait a few seconds for the Android gRPC server to be started. + time.sleep(3) + + self._is_alive = True + + def stop(self): + # Stop Pandora Android gRPC server. + self._device.adb._exec_adb_cmd('shell', + f'am force-stop {ANDROID_SERVER_PACKAGE}', + shell=False, + timeout=None, + stderr=None) + + self._device.adb.forward(['--remove', f'tcp:{self.port}']) + + self.instrumentation.join() + + self._is_alive = False diff --git a/android/pandora/avatar_experimental/avatar/bumble_server/__init__.py b/android/pandora/avatar_experimental/avatar/bumble_server/__init__.py new file mode 100644 index 00000000000..45abf17c665 --- /dev/null +++ b/android/pandora/avatar_experimental/avatar/bumble_server/__init__.py @@ -0,0 +1,80 @@ +# Copyright 2022 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. +"""Pandora Bumble Server.""" + +__version__ = "0.0.1" + +import asyncio +import logging +import os +import grpc + +from bumble.host import Host +from bumble.device import Device, DeviceConfiguration +from bumble.transport import open_transport + +from bumble.a2dp import make_audio_sink_service_sdp_records + +from pandora_experimental.host_grpc import add_HostServicer_to_server +from .host import HostService + +BUMBLE_SERVER_PORT = 7999 +ROOTCANAL_PORT_CUTTLEFISH = 7300 + +current_dir = os.path.dirname(os.path.realpath(__file__)) + + +class BumblePandoraServer: + + def __init__(self, grpc_port, hci, config): + self.hci = hci + device_config = DeviceConfiguration() + device_config.load_from_dict(config) + host = Host(controller_source=hci.source, controller_sink=hci.sink) + self.device = Device(config=device_config, host=host) + self.device.classic_enabled = config.get('classic_enabled', False) + + self.server = grpc.aio.server() + add_HostServicer_to_server(HostService(self.device), self.server) + self.grpc_port = self.server.add_insecure_port(f'localhost:{grpc_port}') + + @classmethod + async def open(cls, grpc_port, transport_name, config): + hci = await open_transport(transport_name) + return cls(grpc_port=grpc_port, hci=hci, config=config) + + async def start(self): + await self.device.power_on() + await self.server.start() + + async def wait_for_termination(self): + await self.server.wait_for_termination() + + async def close(self): + await self.server.stop(None) + await self.hci.close() + + +async def serve(): + transport = f'tcp-client:127.0.0.1:{ROOTCANAL_PORT_CUTTLEFISH}' + server = await BumblePandoraServer.open(BUMBLE_SERVER_PORT, transport, {'classic_enabled': True}) + + await server.start() + await server.wait_for_termination() + await server.close() + + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + asyncio.run(serve()) diff --git a/android/pandora/avatar_experimental/avatar/bumble_server/device_config.json b/android/pandora/avatar_experimental/avatar/bumble_server/device_config.json new file mode 100644 index 00000000000..26b7f05b768 --- /dev/null +++ b/android/pandora/avatar_experimental/avatar/bumble_server/device_config.json @@ -0,0 +1,5 @@ +{ + "name": "Bumble", + "class_of_device": 2360324, + "keystore": "JsonKeyStore" +} \ No newline at end of file diff --git a/android/pandora/avatar_experimental/avatar/bumble_server/host.py b/android/pandora/avatar_experimental/avatar/bumble_server/host.py new file mode 100644 index 00000000000..7370d6974d7 --- /dev/null +++ b/android/pandora/avatar_experimental/avatar/bumble_server/host.py @@ -0,0 +1,86 @@ +# Copyright 2022 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 + +from bumble.core import BT_BR_EDR_TRANSPORT +from bumble.hci import Address, HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR +from bumble.smp import PairingConfig + +from pandora_experimental.host_pb2 import ReadLocalAddressResponse, ConnectResponse, \ + Connection, DisconnectResponse, GetConnectionResponse +from pandora_experimental.host_grpc import HostServicer + + +class HostService(HostServicer): + + def __init__(self, device): + self.device = device + self.device.pairing_config_factory = lambda connection: PairingConfig(bonding=False) + + async def ReadLocalAddress(self, request, context): + logging.info('ReadLocalAddress') + return ReadLocalAddressResponse(address=bytes(reversed(bytes(self.device.public_address)))) + + async def Connect(self, request, context): + # Need to reverse bytes order since Bumble Address is using MSB. + address = Address(bytes(reversed(request.address))) + logging.info(f"Connect: {address}") + + try: + logging.info("Connecting...") + connection = await self.device.connect(address, transport=BT_BR_EDR_TRANSPORT) + logging.info("Connected") + + logging.info("Authenticating...") + await self.device.authenticate(connection) + logging.info("Authenticated") + + logging.info("Enabling encryption...") + await self.device.encrypt(connection) + logging.info("Encryption on") + + logging.info(f"Connect: connection handle: {connection.handle}") + connection_handle = connection.handle.to_bytes(4, 'big') + return ConnectResponse(connection=Connection(cookie=connection_handle)) + + except Exception as error: + logging.error(error) + return ConnectResponse() + + async def Disconnect(self, request, context): + # Need to reverse bytes order since Bumble Address is using MSB. + connection_handle = int.from_bytes(request.connection.cookie, 'big') + logging.info(f"Disconnect: {connection_handle}") + + try: + logging.info("Disconnecting...") + connection = self.device.lookup_connection(connection_handle) + await connection.disconnect(HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR) + except Exception as error: + logging.error(error) + + return DisconnectResponse() + + async def GetConnection(self, request, context): + address = Address(bytes(reversed(request.address))) + logging.info(f"GetConnection: {address}") + + try: + connection_handle = self.device.find_connection_by_bd_addr(address).handle.to_bytes(4, 'big') + return GetConnectionResponse(connection=Connection(cookie=connection_handle)) + + except Exception as error: + logging.error(error) + return GetConnectionResponse() diff --git a/android/pandora/avatar_experimental/avatar/controllers/__init__.py b/android/pandora/avatar_experimental/avatar/controllers/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/android/pandora/avatar_experimental/avatar/controllers/pandora_device.py b/android/pandora/avatar_experimental/avatar/controllers/pandora_device.py new file mode 100644 index 00000000000..dc9a77f18a8 --- /dev/null +++ b/android/pandora/avatar_experimental/avatar/controllers/pandora_device.py @@ -0,0 +1,129 @@ +# Copyright 2022 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 grpc +import importlib +import asyncio +import threading + +import mobly.controllers.android_device +import mobly.signals + +from ..android_service import AndroidService +from ..bumble_server import BumblePandoraServer +from ..utils import Address + +from pandora_experimental.host_grpc import Host + +MOBLY_CONTROLLER_CONFIG_NAME = 'PandoraDevice' + + +def create(configs): + + def create_device(config): + module_name = config.pop('module', PandoraDevice.__module__) + class_name = config.pop('class', PandoraDevice.__name__) + + module = importlib.import_module(module_name) + return getattr(module, class_name).create(**config) + + return list(map(create_device, configs)) + + +def destroy(devices): + for device in devices: + device.close() + + +class PandoraDevice: + + def __init__(self, target): + self.channel = grpc.insecure_channel(target) + self.log = PandoraDeviceLoggerAdapter(logging.getLogger(), { + 'class': self.__class__.__name__, + 'address': self.address + }) + + @classmethod + def create(cls, **kwargs): + return cls(**kwargs) + + @property + def host(self): + return Host(self.channel) + + @property + def address(self): + return Address(self.host.ReadLocalAddress().address) + + def close(self): + self.channel.close() + + +class PandoraDeviceLoggerAdapter(logging.LoggerAdapter): + + def process(self, msg, kwargs): + msg = f'[{self.extra["class"]}|{self.extra["address"]}] {msg}' + return (msg, kwargs) + + +class AndroidPandoraDevice(PandoraDevice): + + def __init__(self, android_device): + self.android_device = android_device + # TODO: Use a dynamic port + port = 8999 + self.android_device.services.register('pandora', AndroidService, configs={'port': port}) + super().__init__(f'localhost:{port}') + + def close(self): + super().close() + mobly.controllers.android_device.destroy([self.android_device]) + + @classmethod + def create(cls, config): + android_devices = mobly.controllers.android_device.create(config) + if not android_devices: + raise mobly.signals.ControllerError('Expected to get at least 1 android controller objects, got 0.') + head, *tail = android_devices + mobly.controllers.android_device.destroy(tail) + return cls(head) + + +class BumblePandoraDevice(PandoraDevice): + + def __init__(self, server): + self.server = server + self.loop = asyncio.get_event_loop() + self.loop.run_until_complete(self.server.start()) + self.thread = threading.Thread(target=lambda: self.loop.run_forever()) + self.thread.start() + + super().__init__(f'localhost:{self.server.grpc_port}') + + def close(self): + super().close() + self.loop.call_soon_threadsafe(lambda: self.loop.stop()) + self.thread.join() + + @property + def device(self): + return self.server.device + + @classmethod + def create(cls, transport, **kwargs): + loop = asyncio.get_event_loop() + server = loop.run_until_complete(BumblePandoraServer.open(0, transport, kwargs)) + return cls(server) diff --git a/android/pandora/avatar_experimental/avatar/utils.py b/android/pandora/avatar_experimental/avatar/utils.py new file mode 100644 index 00000000000..fdf67dac617 --- /dev/null +++ b/android/pandora/avatar_experimental/avatar/utils.py @@ -0,0 +1,32 @@ +# Copyright 2022 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 Address(bytes): + + def __new__(cls, address): + if type(address) is bytes: + address_bytes = address + elif type(address) is str: + address_bytes = bytes.fromhex(address.replace(':', '')) + else: + raise ValueError('Invalid address format') + + if len(address_bytes) != 6: + raise ValueError('Invalid address length') + + return bytes.__new__(cls, address_bytes) + + def __str__(self): + return ':'.join([f'{x:02X}' for x in self]) diff --git a/android/pandora/test/Android.bp b/android/pandora/test/Android.bp index b2a67c957e6..93d9cbf4b57 100644 --- a/android/pandora/test/Android.bp +++ b/android/pandora/test/Android.bp @@ -23,8 +23,7 @@ python_test_host { "connect.py", ], libs: [ - "libavatar", - "pandora_experimental-python", + "libavatar_experimental", ], required: ["PandoraServer"], test_suites: ["general-tests"], -- GitLab From f6997b36999c3b6431b7a9332f78fbd13113888d Mon Sep 17 00:00:00 2001 From: Thomas Girardier Date: Wed, 12 Oct 2022 19:16:47 +0000 Subject: [PATCH 162/667] [PTS-bot] Remove GAP flaky tests Bug: 245578454 Test: atest pts-bot Ignore-AOSP-First: Cherry-pick from AOSP Merged-In: I0514454b1870f22b35150aa845563c6bc7a4d695 Change-Id: I0514454b1870f22b35150aa845563c6bc7a4d695 --- android/pandora/server/configs/pts_bot_tests_config.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/pandora/server/configs/pts_bot_tests_config.json b/android/pandora/server/configs/pts_bot_tests_config.json index c63585a0f1d..726e7cd2b8a 100644 --- a/android/pandora/server/configs/pts_bot_tests_config.json +++ b/android/pandora/server/configs/pts_bot_tests_config.json @@ -505,6 +505,10 @@ "GAP/SEC/SEM/BI-04-C", "GAP/CONN/TERM/BV-01-C", "GAP/CONN/ACEP/BV-01-C", + "GAP/DM/BON/BV-01-C", + "GAP/CONN/NCON/BV-02-C", + "GAP/DM/LEP/BV-08-C", + "GAP/DM/LEP/BV-01-C", "GATT/CL/GAR/BI-04-C", "GATT/CL/GAR/BI-05-C", "GATT/CL/GAR/BI-10-C", -- GitLab From a8110339d86e5c18f215a3ff329561fa3e7c2d0e Mon Sep 17 00:00:00 2001 From: David Duarte Date: Thu, 29 Sep 2022 14:54:35 +0000 Subject: [PATCH 163/667] Pandora: Move interfaces into a new pandora folder at root The experimental pandora interfaces defined for android might also be used for floss so we move them to a non-android specific directory Bug: 245578454 Test: TreeHugger Ignore-AOSP-First: Cherry-pick from AOSP Merged-In: I3febb66dadd4f2acc951f4c54802681dfbc55940 Change-Id: I3febb66dadd4f2acc951f4c54802681dfbc55940 --- android/pandora/server/Android.bp | 98 ----------------- pandora/OWNERS | 5 + pandora/interfaces/Android.bp | 101 ++++++++++++++++++ .../pandora_experimental/_android.proto | 0 .../pandora_experimental/a2dp.proto | 0 .../pandora_experimental/avrcp.proto | 0 .../pandora_experimental/gatt.proto | 0 .../pandora_experimental/hfp.proto | 0 .../pandora_experimental/hid.proto | 0 .../pandora_experimental/host.proto | 0 .../pandora_experimental/l2cap.proto | 0 .../pandora_experimental/security.proto | 0 12 files changed, 106 insertions(+), 98 deletions(-) create mode 100644 pandora/OWNERS create mode 100644 pandora/interfaces/Android.bp rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/_android.proto (100%) rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/a2dp.proto (100%) rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/avrcp.proto (100%) rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/gatt.proto (100%) rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/hfp.proto (100%) rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/hid.proto (100%) rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/host.proto (100%) rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/l2cap.proto (100%) rename {android/pandora/server/proto => pandora/interfaces}/pandora_experimental/security.proto (100%) diff --git a/android/pandora/server/Android.bp b/android/pandora/server/Android.bp index 01a911f626c..f6aac5485a8 100644 --- a/android/pandora/server/Android.bp +++ b/android/pandora/server/Android.bp @@ -71,101 +71,3 @@ android_test { ], test_suites: ["mts-bluetooth"], } - -java_library { - name: "pandora-grpc-java", - visibility: ["//visibility:private"], - srcs: [ - "proto/pandora_experimental/*.proto", - ], - static_libs: [ - "grpc-java-lite", - "guava", - "javax_annotation-api_1.3.2", - "libprotobuf-java-lite", - "opencensus-java-api", - "pandora-proto-java", - ], - proto: { - include_dirs: [ - "external/protobuf/src", - "packages/modules/Bluetooth/android/pandora/server/proto", - ], - plugin: "grpc-java-plugin", - output_params: [ - "lite", - ], - }, -} - -java_library { - name: "pandora-proto-java", - visibility: ["//visibility:private"], - srcs: [ - "proto/pandora_experimental/*.proto", - ":libprotobuf-internal-protos", - ], - static_libs: [ - "libprotobuf-java-lite", - ], - proto: { - type: "lite", - include_dirs: [ - "external/protobuf/src", - "packages/modules/Bluetooth/android/pandora/server/proto", - ], - }, -} - -genrule { - name: "pandora_experimental-python-src", - tools: [ - "aprotoc", - "protoc-gen-mmi2grpc-python" - ], - cmd: "$(location aprotoc)" + - " -Ipackages/modules/Bluetooth/android/pandora/server/proto" + - " -Iexternal/protobuf/src" + - " --plugin=protoc-gen-grpc=$(location protoc-gen-mmi2grpc-python)" + - " --grpc_out=$(genDir)" + - " --python_out=$(genDir)" + - " $(in)", - srcs: [ - "proto/pandora_experimental/_android.proto", - "proto/pandora_experimental/a2dp.proto", - "proto/pandora_experimental/avrcp.proto", - "proto/pandora_experimental/gatt.proto", - "proto/pandora_experimental/hfp.proto", - "proto/pandora_experimental/hid.proto", - "proto/pandora_experimental/host.proto", - "proto/pandora_experimental/l2cap.proto", - "proto/pandora_experimental/security.proto", - ], - out: [ - "pandora_experimental/_android_grpc.py", - "pandora_experimental/_android_pb2.py", - "pandora_experimental/a2dp_grpc.py", - "pandora_experimental/a2dp_pb2.py", - "pandora_experimental/avrcp_grpc.py", - "pandora_experimental/avrcp_pb2.py", - "pandora_experimental/gatt_grpc.py", - "pandora_experimental/gatt_pb2.py", - "pandora_experimental/hfp_grpc.py", - "pandora_experimental/hfp_pb2.py", - "pandora_experimental/hid_grpc.py", - "pandora_experimental/hid_pb2.py", - "pandora_experimental/host_grpc.py", - "pandora_experimental/host_pb2.py", - "pandora_experimental/l2cap_grpc.py", - "pandora_experimental/l2cap_pb2.py", - "pandora_experimental/security_grpc.py", - "pandora_experimental/security_pb2.py", - ] -} - -python_library_host { - name: "pandora_experimental-python", - srcs: [ - ":pandora_experimental-python-src", - ], -} diff --git a/pandora/OWNERS b/pandora/OWNERS new file mode 100644 index 00000000000..610b34604a2 --- /dev/null +++ b/pandora/OWNERS @@ -0,0 +1,5 @@ +# Project owners +girardier@google.com +licorne@google.com +uael@google.com +charliebout@google.com diff --git a/pandora/interfaces/Android.bp b/pandora/interfaces/Android.bp new file mode 100644 index 00000000000..38a344a3ca7 --- /dev/null +++ b/pandora/interfaces/Android.bp @@ -0,0 +1,101 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library { + name: "pandora-grpc-java", + visibility: ["//packages/modules/Bluetooth/android/pandora/server"], + srcs: [ + "pandora_experimental/*.proto", + ], + static_libs: [ + "grpc-java-lite", + "guava", + "javax_annotation-api_1.3.2", + "libprotobuf-java-lite", + "opencensus-java-api", + "pandora-proto-java", + ], + proto: { + include_dirs: [ + "external/protobuf/src", + "packages/modules/Bluetooth/pandora/interfaces", + ], + plugin: "grpc-java-plugin", + output_params: [ + "lite", + ], + }, +} + +java_library { + name: "pandora-proto-java", + visibility: ["//packages/modules/Bluetooth/android/pandora/server"], + srcs: [ + "pandora_experimental/*.proto", + ":libprotobuf-internal-protos", + ], + static_libs: [ + "libprotobuf-java-lite", + ], + proto: { + type: "lite", + include_dirs: [ + "external/protobuf/src", + "packages/modules/Bluetooth/pandora/interfaces", + ], + }, +} + +genrule { + name: "pandora_experimental-python-src", + tools: [ + "aprotoc", + "protoc-gen-mmi2grpc-python" + ], + cmd: "$(location aprotoc)" + + " -Ipackages/modules/Bluetooth/pandora/interfaces" + + " -Iexternal/protobuf/src" + + " --plugin=protoc-gen-grpc=$(location protoc-gen-mmi2grpc-python)" + + " --grpc_out=$(genDir)" + + " --python_out=$(genDir)" + + " $(in)", + srcs: [ + "pandora_experimental/_android.proto", + "pandora_experimental/a2dp.proto", + "pandora_experimental/avrcp.proto", + "pandora_experimental/gatt.proto", + "pandora_experimental/hfp.proto", + "pandora_experimental/hid.proto", + "pandora_experimental/host.proto", + "pandora_experimental/l2cap.proto", + "pandora_experimental/security.proto", + ], + out: [ + "pandora_experimental/_android_grpc.py", + "pandora_experimental/_android_pb2.py", + "pandora_experimental/a2dp_grpc.py", + "pandora_experimental/a2dp_pb2.py", + "pandora_experimental/avrcp_grpc.py", + "pandora_experimental/avrcp_pb2.py", + "pandora_experimental/gatt_grpc.py", + "pandora_experimental/gatt_pb2.py", + "pandora_experimental/hfp_grpc.py", + "pandora_experimental/hfp_pb2.py", + "pandora_experimental/hid_grpc.py", + "pandora_experimental/hid_pb2.py", + "pandora_experimental/host_grpc.py", + "pandora_experimental/host_pb2.py", + "pandora_experimental/l2cap_grpc.py", + "pandora_experimental/l2cap_pb2.py", + "pandora_experimental/security_grpc.py", + "pandora_experimental/security_pb2.py", + ] +} + +python_library_host { + name: "pandora_experimental-python", + srcs: [ + ":pandora_experimental-python-src", + ], +} diff --git a/android/pandora/server/proto/pandora_experimental/_android.proto b/pandora/interfaces/pandora_experimental/_android.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/_android.proto rename to pandora/interfaces/pandora_experimental/_android.proto diff --git a/android/pandora/server/proto/pandora_experimental/a2dp.proto b/pandora/interfaces/pandora_experimental/a2dp.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/a2dp.proto rename to pandora/interfaces/pandora_experimental/a2dp.proto diff --git a/android/pandora/server/proto/pandora_experimental/avrcp.proto b/pandora/interfaces/pandora_experimental/avrcp.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/avrcp.proto rename to pandora/interfaces/pandora_experimental/avrcp.proto diff --git a/android/pandora/server/proto/pandora_experimental/gatt.proto b/pandora/interfaces/pandora_experimental/gatt.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/gatt.proto rename to pandora/interfaces/pandora_experimental/gatt.proto diff --git a/android/pandora/server/proto/pandora_experimental/hfp.proto b/pandora/interfaces/pandora_experimental/hfp.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/hfp.proto rename to pandora/interfaces/pandora_experimental/hfp.proto diff --git a/android/pandora/server/proto/pandora_experimental/hid.proto b/pandora/interfaces/pandora_experimental/hid.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/hid.proto rename to pandora/interfaces/pandora_experimental/hid.proto diff --git a/android/pandora/server/proto/pandora_experimental/host.proto b/pandora/interfaces/pandora_experimental/host.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/host.proto rename to pandora/interfaces/pandora_experimental/host.proto diff --git a/android/pandora/server/proto/pandora_experimental/l2cap.proto b/pandora/interfaces/pandora_experimental/l2cap.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/l2cap.proto rename to pandora/interfaces/pandora_experimental/l2cap.proto diff --git a/android/pandora/server/proto/pandora_experimental/security.proto b/pandora/interfaces/pandora_experimental/security.proto similarity index 100% rename from android/pandora/server/proto/pandora_experimental/security.proto rename to pandora/interfaces/pandora_experimental/security.proto -- GitLab From fe007cfd852fef8fddaf967e15d451981be1f2aa Mon Sep 17 00:00:00 2001 From: Thomas Girardier Date: Wed, 19 Oct 2022 20:13:13 +0000 Subject: [PATCH 164/667] [PTS-bot] Update configuration files - Update pass and skip list. - Simplify PtsBotTest configuration file. - Update PtsBotTestMts configuration file. Bug: 245578454 Test: atest pts-bot Ignore-AOSP-First: Cherry-pick from AOSP Merged-In: Idaebfd1d5d93c88a3a2f29d6903ee80b8a25c8cb Change-Id: Idaebfd1d5d93c88a3a2f29d6903ee80b8a25c8cb --- android/pandora/server/configs/PtsBotTest.xml | 35 +- .../pandora/server/configs/PtsBotTestMts.xml | 23 +- .../server/configs/pts_bot_tests_config.json | 379 +++++++++--------- 3 files changed, 208 insertions(+), 229 deletions(-) diff --git a/android/pandora/server/configs/PtsBotTest.xml b/android/pandora/server/configs/PtsBotTest.xml index 8fd4dd4ddb8..8af399562a9 100644 --- a/android/pandora/server/configs/PtsBotTest.xml +++ b/android/pandora/server/configs/PtsBotTest.xml @@ -29,42 +29,19 @@