diff --git a/CleanSpec.mk b/CleanSpec.mk index 1eca2a1c21b167e5ed07154d815c1ceac351d057..2e9d9ab328874aa79d35ab0308d2cccf6610b146 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -85,3 +85,4 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/bin/hw/android.hardware.cas@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.cas@1.1*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/vintf/manifest/android.hardware.cas@1.1*) $(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/hardware/interfaces/wifi/1.4/android.hardware.wifi@1.4-adapter_genc++/) +$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/hardware/interfaces/automotive/vehicle/aidl/android.hardware.automotive.vehicle-V2-ndk-source/) diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 2116e21c5a058ec71400dc7057d75f822ecf3830..de7aa354bdfadce0d0f5ca3273c7bdf3f2d529e0 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -10,3 +10,4 @@ aidl_format = true aosp_hook_confirmationui = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} confirmationui aosp_hook_gatekeeper = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} gatekeeper aosp_hook_keymaster = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} keymaster +generate_vehicle_property_enums = ${REPO_ROOT}/hardware/interfaces/automotive/vehicle/tools/generate_annotation_enums.py --android_build_top ${REPO_ROOT} --preupload_files ${PREUPLOAD_FILES} --check_only diff --git a/apexkey/Android.bp b/apexkey/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..e74717ae3ea09fd1fde3e55f1eecbef2e7b27133 --- /dev/null +++ b/apexkey/Android.bp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +apex_key { + name: "com.android.hardware.key", + public_key: "com.android.hardware.avbpubkey", + private_key: "com.android.hardware.pem", +} + +android_app_certificate { + name: "com.android.hardware.certificate", + certificate: "com.android.hardware", +} diff --git a/apexkey/OWNERS b/apexkey/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..38765f91efdb50c76f649e57f53b0fa9ea668a96 --- /dev/null +++ b/apexkey/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 296524155 + +jooyung@google.com diff --git a/apexkey/com.android.hardware.avbpubkey b/apexkey/com.android.hardware.avbpubkey new file mode 100644 index 0000000000000000000000000000000000000000..12f0370a6cfe1f92b24da1b0a341678151092efe Binary files /dev/null and b/apexkey/com.android.hardware.avbpubkey differ diff --git a/apexkey/com.android.hardware.pem b/apexkey/com.android.hardware.pem new file mode 100644 index 0000000000000000000000000000000000000000..4d2882b3d50f390ee5eb06bb4b3610b29725206d --- /dev/null +++ b/apexkey/com.android.hardware.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCmirL5vFcGDqg9 +JqKZwRqzPr76t8aqJSu+UOdnlmqBLmUBaeun2KwRsZiLku933m5MNv+CPnVQW7ij +z0lv0z/qB8SXN+KAgSqRdIjL1ySQgp8ygKHu+GcvbMFj9pVuqufwSrEOY9Un1RRY +InDDj88c/aNbCw64d1QVedgUIs5KDDLjOzkcrrbH+Th26r2YjhlRKQXCZT5jW1fY +2gJg5LPeuM4fI63ESxFyRTY/V8E3LbAuTYKBSrHRbec22oCkL/pUilJBe2r8aNyp +ACWEJB96uWdKmIYDCdj6r6SNzHM3S4X+l1C9/Gi9UNK8nuIAI3v2qW07S2RABAWX +vKfE8rIKAdFmajGlHKfRe6IL9ka1/rsM1xgDr2/FgEsyRgrfJvlvvMb7ampMgTKu +Az/hv4siSaDD2E3JrRyh3nNxTCuIABla7/jGmNz6vIdwTRJsa2RIEezkeMiUy+jp +VyeYsWIEBmQwZ5+k6YbdrKTULaay3UJ6/YXEQY9Sbg0JL/7sjQWILwnrtab3/jsn +iWsr59Qsaf+q3PnrWPCXT/KC8DwKi/2zSWfCX8MHreqaoozjfWXeD87lnBAhukBS +984xWEmStJGFQG29J4sF4NmgO1I8GPqQRtoB6ghme11U2EX0vIqrDNccX12ohqgh +CfwMXX64WV5fWscmSLZin5xK+AN+/wIDAQABAoICABLDnda1ebic/ielqrxYHYIE +l8/31NJmzb7rrqblM6rKZ2b5YCvRJdH4iI9xRrnivdqod7FdCCq9qtgKCuQmkRT1 +Oxkumr6PzxQEZUVSQDRoAzfVdmlOY5HcVw3EQqHtb6SLRdexN9r9DwW/G6VtFpDV +owom/Mmb6EVjLQ1XvpZpOXKQwaaRqNZvJgV//Uyq9f9G9/cnIRhguk0cgxgXrIxz +lgW8J1/udVDCq5IHeSWWaDtPUFzw5YgLMfnkNIpLPZE45HusRCmcRwBjm9Z/m2xg +Z1roRHEjMudL0uaHGpQoVjiRI+/D+Kkb6iKGZEBPvy/TdxMvM2RjD55BC0YNcjGv +kXTMLwj2ke824rUPJJOYN8bJFIpOYblMcbDrxYVlDZetKOMc6EJw90xaj4za2IMS +vWDxZx1WQPcn+B/nUvrMMOTN6MkNYKR97lmxBYP6TeCiUUytZiCF72cvvpYcdhE9 +zzOES5kVakSE8NnLTH9xAjrpAguxI9gqxeyLALhz/Rb46fgWwnhcBdqmPtez/IHb +BgF81SSobSaBlzLcnRyrjwBRWA21DP8U/ApUkmHR/Lm1BJejzxf1hj35Hp9XUwdC +JK7aoVnjuko/l5TPn0Bvm4pcBArG0AUL75GlRgDJTVCDZbLFR/2JZlnZ9optvN6E +vniu3HwBRHDHn0K1DqIBAoIBAQDpdBJtyizIlZ5p6k1f0wWE/B2riPzPwv/a+ygD +GOtzWsukyZjWHXVPjx2XY3bwlbusiqhJE1OcF+oymHTtSSv3VsqMmo2VDIj8d21T +ycpBvV7+mcehGk6o6lGTJAiPc1wVMajmM0Co2LQb4hRvf2p8dW6+RRn5c3o288B5 +e45zpbq9+l9PMpgrmOBU3m652JmGK8JqQKeKKT96kZQVf/U3ArMif+jW2piAZnmG +CS00el0K0RtZe/iKrFftoD27ai9GNZx/Z1p55x+9vUD15LiQ/MK0Bk/f9b24zE98 +6T07KAGPB5gXWlSs2yEk4Waufjw3JSYXs18kEoMklTA0IGGhAoIBAQC2oEwGQZI0 +oR1oL7P76FyMNjtp2T2wYnOcnGeKjp4vxZWTZNRfNUmzGqSamVE1ZPhj721sAImS +mk1pUJbglkpP6aMDNXzA6Z50PuXfhF5+dXBrLftT1tcNo/8/ysd/iKsi1C6hWZ9L +YYt/7uR8wazeG65jahsOKEfCrNyDBQzyoSft1qfVTvYzxUVmJRQr5S7H0c9jPTwg +Qj+/RQsDOspsJrBC5+H0tLPkvZDdZCYvdBzVWFL7npHd0fy53TmT6kEjDwwAiE8W +VggvzkhHOnprAiwq36nlpGo3um5Ona98dXWZ4rfELEdSUOiWCz2Gzz7IL+H8uhen +iXQAtlAlMVyfAoIBAQDDGmlobgW9YSVyWQlvIxnCyT02pZa7P7m6VhpbdFEaJ/B3 +eLANibMH2ZCee9bkPA8LpRn9cHR4yJfGzxkxUey3mkZO+b+HqGE3tcudsdAh3tTs +LLLk1eqVSrcAJKYu1PdJEyaXtI++TNVBVFFF2ZOyDiaOR4vsqPCjylS6cmNcDU6j +BQLwWlVIYkxhQUACe53avumNCRm74rsVgWLkEPtrjQsOsx1xqmC3Nm0rcrHf4L+v +kZCs2Wme42fcxQWygoydvaJb65F2vY0OcoM+vXbuXP+PmpLYljSHo+BugTLWJJog +4fTe13RavgvWGbZJ8F/8qkRKnHLP39TQFwqhFcrhAoIBAQCZYipX+syUHVl4ftA4 +2+CW9pmR/h5cvWOCfq5L5hqf3uAQqlfgGv5qSg9DtU9KfCJOtPH/CriBA83OjRwb +Rr7lPUJnWI4N7GjkE0u4HhHLPlGMA1OaB9AZKcsgxewmhh/OIc2EQKSUiDnE2lzX +F0dcSH2AC8brbhNPQyX1eNF2GrpRfL77XdVUh/QQpUW61EMlGmeHXw/XvCyUNaXZ +MZB/o0LY/q59eyO4EyrPduTHaEsrOIKByJ2UY2BSrqZEQDQzANlAvTtHdDxuAmAc +StY0d9+mprGoKR347kDlEHUqH/EErTFQtyuzzDZ8hrZHOlrtgaHZRGbHlJ90Vasc +gcfbAoIBAEIR0dtmd38dC7r0h0SZ7ORIvm9F8JRs+wKdWjiPfWLK3trR/jxRx6Eh +xrrJEoRCeUmT6rc0TwUkzow5ApcEY338KmsqKVbm3iGfPpvZFEO44vBrg3280QBM +PerIrEfidflUAE9QXNoBpvvnh2KXIOsvyN2MeY2SAyebPJQJtWzJkP35Y8fOtZkJ +VtIhYj2wvFuobl7iuxKJG0cn4xL3OLysc03oX7Al2qA42hUCy7UMsTT6ZAPWd49+ +3gOFrqmaV3Hu5KY2HaX6Yly5ohhQWSkSL1kZMo9Uq6tTM+2HFzFU6sveFbWW/XQc +rWnazDdd1WkHT6+nzA/NX24OzPjiUk0= +-----END PRIVATE KEY----- diff --git a/apexkey/com.android.hardware.pk8 b/apexkey/com.android.hardware.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..97007d272d989698c24f714e52ff2b7ee1c8ee37 Binary files /dev/null and b/apexkey/com.android.hardware.pk8 differ diff --git a/apexkey/com.android.hardware.x509.pem b/apexkey/com.android.hardware.x509.pem new file mode 100644 index 0000000000000000000000000000000000000000..2728a23666cfc8d466fb770e7c0f92924c0d9b53 --- /dev/null +++ b/apexkey/com.android.hardware.x509.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF6TCCA9ECFDAU6pJXjblHA/w9a354h2QUOharMA0GCSqGSIb3DQEBCwUAMIGv +MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 +bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi +MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTErMCkGA1UEAwwiY29t +LmFuZHJvaWQuaGFyZHdhcmUudGhyZWFkbmV0d29yazAgFw0yMzA4MjQwNjE4MTZa +GA80NzYxMDcyMDA2MTgxNlowga8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp +Zm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lk +MRAwDgYDVQQLDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJv +aWQuY29tMSswKQYDVQQDDCJjb20uYW5kcm9pZC5oYXJkd2FyZS50aHJlYWRuZXR3 +b3JrMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5eMXZfeadUJiqZ0b +kbXYSbzjd1opoeAVk/+LxGyBLLPy2z2EAeuUBx8HYEJhyPOyUrs3ohP6OvAtE8O4 +JeEbGBzqOymusKbve8ght7tDjUuTYsbBsKQWw1Gu/UUK7x3lc6zD+wDTK/5NUoTA +I/5lI8Q4+NHCI1jb/uBRGSlzSO+k2A2oVUPdyaWWgGhLeEiXKo+Xy6XXduhFK1wc +iSCgVyuQkZnbrJrGCGS1LD1MavsfBTthD3jfuOq+YqMjGjdi6xVPG3fZnwwdVNzW +mSFwWwHif56ZAtxyZVtQOAksTqwAwGrUjhn70rw5eLBQjXIL1061cLrOZ4XlRSZj +kHB8mwr6OQOw7isMORO0h+NNJ+5POa5V9rGylqk/hn1vzcTX62YUZy1N6b3rfGc2 +p9LA6STjvoXLeCFaSIc+LfTiHOamkg7LuLlsQ/AIoXeuaLyWAgg4ls2U8IG1Mdkz +BClX5vMG/At6yZqHiCIbCU8V8VSCz7Rtuu+C/87G5Pwb/G1r4FHXr9x7d0qj4rWV +u9MhdzvZWoTTnEzDA+DWPQM/ILkGdkSj0g4yC8GR/F5p7xWQ0vqyMyM/ii1KzWVs +ZJFUxzpGX99C0mfjT2hezHzfEyisPec39PFbdxzC756yP1Le8I5qjMbjl+XeFjxO +REoC0nDFgHEI7C5RunT1osKmpDUCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA3HPY ++nHGFE9aFQbEsjvotuxguLMKmrgQ44yGBTr9349MPvnxGDdz1HPWs/m456k9HQxt +1YWV+xr56NS37UDVMkqS7hzCkJe/LdMT51qvUS+hPHLzSuZNKuFAaCOb8WDa7tGQ +MuvISlb6T7G512fUdFDRjFjnIXDLTYANzoxI3cqs2N2xQNxsL8uIJ79RWlPm2Upz +Y5Ad55kv3EygWgirvZnSNo27E/Tt4Bo9KS0HsYXVHgWK9zHHtup1vDOntJ8bX16C +rP7e8SELanPbUtyIUDcf14P3EFztUYBZIXO2HUSsoOTX3+Hd592h7EsnGDdlWSLU +oreaRWCAMh4TKI/eP+1N35b8zmPMHaG+WsWjdDPrs6oNpr2MMA4U8AHGW2d6QY0N +xZ/VhhbjbHMgmmB3Q2LXZZufbSEBawBfuFjhodfmKSgTjNCTYXtTiT/pL2w0PqEM +v4UxrGD0uFQafFKuxQfL2LYG5tg46daRrT3BoIqLKoseTuykzW4z08NuurwsLvmy +t7njqMxlf/tN9o+d3D3lWHQpiDJ8kC/TjgKBj7iQTCOJeShutxyR2F3R+3+kUXpP +w7O4QfoqZDca1qtJRtew2tpmOb1hFkj/QzRi5WRx/oM/Cm0yMttYIA+4vCJeDOja +gngrtH3jmEwMME71HDyXHlcCIIt95odXoBSBKgk= +-----END CERTIFICATE----- diff --git a/atrace/1.0/vts/functional/OWNERS b/atrace/OWNERS similarity index 97% rename from atrace/1.0/vts/functional/OWNERS rename to atrace/OWNERS index 31043aa7161c44b8c911ab368abd19c67060c013..d76ffa6d08ced17d71a8404689bada356327115f 100644 --- a/atrace/1.0/vts/functional/OWNERS +++ b/atrace/OWNERS @@ -1,2 +1,3 @@ # Bug component: 837454 + wvw@google.com diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp index 5a009fe6747cfefbc2057742c688ad5eb5930ac1..89d186c633cb70f876c50cbee9a9d4ffd4202e0d 100644 --- a/audio/aidl/Android.bp +++ b/audio/aidl/Android.bp @@ -34,6 +34,7 @@ aidl_interface { name: "android.hardware.audio.common", defaults: [ "android.hardware.audio_defaults", + "latest_android_media_audio_common_types_import_interface", ], srcs: [ "android/hardware/audio/common/AudioOffloadMetadata.aidl", @@ -42,10 +43,7 @@ aidl_interface { "android/hardware/audio/common/SinkMetadata.aidl", "android/hardware/audio/common/SourceMetadata.aidl", ], - frozen: true, - imports: [ - "android.media.audio.common.types-V2", - ], + frozen: false, backend: { cpp: { enabled: true, @@ -83,7 +81,7 @@ aidl_interface { } // Note: This should always be one version ahead of the last frozen version -latest_android_hardware_audio_common = "android.hardware.audio.common-V2" +latest_android_hardware_audio_common = "android.hardware.audio.common-V3" // Modules that depend on android.hardware.audio.common directly can include // the following cc_defaults to avoid explicitly managing dependency versions @@ -109,10 +107,21 @@ cc_defaults { ], } +aidl_interface_defaults { + name: "latest_android_hardware_audio_common_import_interface", + imports: [ + latest_android_hardware_audio_common, + ], +} + aidl_interface { name: "android.hardware.audio.core", defaults: [ "android.hardware.audio_defaults", + "latest_android_hardware_audio_common_import_interface", + "latest_android_hardware_audio_core_sounddose_import_interface", + "latest_android_hardware_audio_effect_import_interface", + "latest_android_media_audio_common_types_import_interface", ], srcs: [ "android/hardware/audio/core/AudioPatch.aidl", @@ -137,10 +146,6 @@ aidl_interface { imports: [ "android.hardware.common-V2", "android.hardware.common.fmq-V1", - "android.hardware.audio.common-V2", - "android.hardware.audio.core.sounddose-V1", - "android.hardware.audio.effect-V1", - "android.media.audio.common.types-V2", ], backend: { // The C++ backend is disabled transitively due to use of FMQ. @@ -167,11 +172,11 @@ aidl_interface { // IMPORTANT: Update latest_android_hardware_audio_core every time you // add the latest frozen version to versions_with_info ], - frozen: true, + frozen: false, } // Note: This should always be one version ahead of the last frozen version -latest_android_hardware_audio_core = "android.hardware.audio.core-V1" +latest_android_hardware_audio_core = "android.hardware.audio.core-V2" // Modules that depend on android.hardware.audio.core directly can include // the following cc_defaults to avoid explicitly managing dependency versions @@ -190,18 +195,23 @@ cc_defaults { ], } +aidl_interface_defaults { + name: "latest_android_hardware_audio_core_import_interface", + imports: [ + latest_android_hardware_audio_core, + ], +} + // Used for the standalone sounddose HAL aidl_interface { name: "android.hardware.audio.core.sounddose", defaults: [ "android.hardware.audio_defaults", + "latest_android_media_audio_common_types_import_interface", ], srcs: [ "android/hardware/audio/core/sounddose/ISoundDose.aidl", ], - imports: [ - "android.media.audio.common.types-V2", - ], backend: { // The C++ backend is disabled transitively due to use of FMQ by the core HAL. cpp: { @@ -220,11 +230,11 @@ aidl_interface { // IMPORTANT: Update latest_android_hardware_audio_core_sounddose every time you // add the latest frozen version to versions_with_info ], - frozen: true, + frozen: false, } // Note: This should always be one version ahead of the last frozen version -latest_android_hardware_audio_core_sounddose = "android.hardware.audio.core.sounddose-V1" +latest_android_hardware_audio_core_sounddose = "android.hardware.audio.core.sounddose-V2" // Modules that depend on android.hardware.audio.core.sounddose directly can include // the following cc_defaults to avoid explicitly managing dependency versions @@ -236,6 +246,13 @@ cc_defaults { ], } +cc_defaults { + name: "latest_android_hardware_audio_core_sounddose_ndk_export_shared_lib_header", + export_shared_lib_headers: [ + latest_android_hardware_audio_core_sounddose + "-ndk", + ], +} + cc_defaults { name: "latest_android_hardware_audio_core_sounddose_ndk_static", static_libs: [ @@ -243,10 +260,19 @@ cc_defaults { ], } +aidl_interface_defaults { + name: "latest_android_hardware_audio_core_sounddose_import_interface", + imports: [ + latest_android_hardware_audio_core_sounddose, + ], +} + aidl_interface { name: "android.hardware.audio.effect", defaults: [ "android.hardware.audio_defaults", + "latest_android_hardware_audio_common_import_interface", + "latest_android_media_audio_common_types_import_interface", ], srcs: [ "android/hardware/audio/effect/AcousticEchoCanceler.aidl", @@ -271,6 +297,7 @@ aidl_interface { "android/hardware/audio/effect/PresetReverb.aidl", "android/hardware/audio/effect/Processing.aidl", "android/hardware/audio/effect/Range.aidl", + "android/hardware/audio/effect/Spatializer.aidl", "android/hardware/audio/effect/State.aidl", "android/hardware/audio/effect/VendorExtension.aidl", "android/hardware/audio/effect/Virtualizer.aidl", @@ -280,8 +307,6 @@ aidl_interface { imports: [ "android.hardware.common-V2", "android.hardware.common.fmq-V1", - "android.hardware.audio.common-V2", - "android.media.audio.common.types-V2", ], backend: { // The C++ backend is disabled transitively due to use of FMQ. @@ -303,11 +328,11 @@ aidl_interface { ], }, ], - frozen: true, + frozen: false, } -latest_android_hardware_audio_effect = "android.hardware.audio.effect-V1" +latest_android_hardware_audio_effect = "android.hardware.audio.effect-V2" cc_defaults { name: "latest_android_hardware_audio_effect_ndk_shared", @@ -322,3 +347,10 @@ cc_defaults { latest_android_hardware_audio_effect + "-ndk", ], } + +aidl_interface_defaults { + name: "latest_android_hardware_audio_effect_import_interface", + imports: [ + latest_android_hardware_audio_effect, + ], +} diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING index 12bce0b47c00c43af19ba8bc97df1dc581fe63ce..81c99f78a9b4ad730b784f6fc6d9189113cc9cc0 100644 --- a/audio/aidl/TEST_MAPPING +++ b/audio/aidl/TEST_MAPPING @@ -3,9 +3,6 @@ { "name": "VtsHalAudioCoreTargetTest" }, - { - "name": "VtsHalAudioCoreConfigTargetTest" - }, { "name": "audio_policy_config_xml_converter_tests" }, diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl index e14e9c0f710acc05efd38979a0287b3bab36a397..07a85f8fd8f214e6db52f8637e803ce1dca00de1 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl @@ -74,6 +74,7 @@ interface IModule { boolean supportsVariableLatency(); int getAAudioMixerBurstCount(); int getAAudioHardwareBurstMinUsec(); + void prepareToDisconnectExternalDevice(int portId); const int DEFAULT_AAUDIO_MIXER_BURST_COUNT = 2; const int DEFAULT_AAUDIO_HARDWARE_BURST_MIN_DURATION_US = 1000; @VintfStability diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl index bcbf870fd7d8dfb57f327e77cc19af82480dafdd..046c220ea072986d6df5ac0a5235b9fe4bc61c53 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl @@ -43,6 +43,8 @@ parcelable Flags { boolean audioModeIndication; boolean audioSourceIndication; boolean bypass; + boolean sinkMetadataIndication; + boolean sourceMetadataIndication; @Backing(type="byte") @VintfStability enum Type { INSERT = 0, diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl index 0422bd92c76ac2af39dfe451697a90c2dd0b2892..ff33c42047bad8e5298344339b5ba14490f480f3 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl @@ -41,6 +41,8 @@ union Parameter { boolean offload; android.hardware.audio.effect.Parameter.VolumeStereo volumeStereo; android.hardware.audio.effect.Parameter.Specific specific; + android.hardware.audio.common.SinkMetadata sinkMetadata; + android.hardware.audio.common.SourceMetadata sourceMetadata; @VintfStability union Id { android.hardware.audio.effect.VendorExtension vendorEffectTag; @@ -60,6 +62,7 @@ union Parameter { android.hardware.audio.effect.Visualizer.Id visualizerTag; android.hardware.audio.effect.Volume.Id volumeTag; android.hardware.audio.effect.Parameter.Tag commonTag; + android.hardware.audio.effect.Spatializer.Id spatializerTag; } @VintfStability parcelable Common { @@ -91,5 +94,6 @@ union Parameter { android.hardware.audio.effect.Virtualizer virtualizer; android.hardware.audio.effect.Visualizer visualizer; android.hardware.audio.effect.Volume volume; + android.hardware.audio.effect.Spatializer spatializer; } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl index 93edc5edface4d6e9a943a11ec841de4f4b696b3..40ee6b54888ecfd665a878e527c8899887edc2c5 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl @@ -50,6 +50,7 @@ union Range { android.hardware.audio.effect.Range.VirtualizerRange[] virtualizer; android.hardware.audio.effect.Range.VisualizerRange[] visualizer; android.hardware.audio.effect.Range.VolumeRange[] volume; + android.hardware.audio.effect.Range.SpatializerRange[] spatializer; @VintfStability parcelable AcousticEchoCancelerRange { android.hardware.audio.effect.AcousticEchoCanceler min; @@ -111,6 +112,11 @@ union Range { android.hardware.audio.effect.PresetReverb max; } @VintfStability + parcelable SpatializerRange { + android.hardware.audio.effect.Spatializer min; + android.hardware.audio.effect.Spatializer max; + } + @VintfStability parcelable VendorExtensionRange { android.hardware.audio.effect.VendorExtension min; android.hardware.audio.effect.VendorExtension max; diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl new file mode 100644 index 0000000000000000000000000000000000000000..98ecee061abe49d523cb05f84e11bdbf0a48bcd9 --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.audio.effect; +@VintfStability +union Spatializer { + android.hardware.audio.effect.VendorExtension vendor; + android.media.audio.common.AudioChannelLayout[] supportedChannelLayout; + android.media.audio.common.Spatialization.Level spatializationLevel; + android.media.audio.common.Spatialization.Mode spatializationMode; + int headTrackingSensorId; + android.media.audio.common.HeadTracking.Mode headTrackingMode; + android.media.audio.common.HeadTracking.ConnectionMode headTrackingConnectionMode; + android.media.audio.common.HeadTracking.SensorData headTrackingSensorData; + @VintfStability + union Id { + android.hardware.audio.effect.VendorExtension vendorExtensionTag; + android.hardware.audio.effect.Spatializer.Tag commonTag; + } +} diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl index e736c32e98779aef3aafd0712f5be992bb6fbd20..2d4d283be2eec4712249ea22ec158d109f09512f 100644 --- a/audio/aidl/android/hardware/audio/core/IModule.aidl +++ b/audio/aidl/android/hardware/audio/core/IModule.aidl @@ -206,8 +206,10 @@ interface IModule { * after successful connection of an external device. * * Handling of a disconnect is done in a reverse order: - * 1. Reset port configuration using the 'resetAudioPortConfig' method. - * 2. Release the connected device port by calling the 'disconnectExternalDevice' + * 1. Notify the HAL module to prepare for device disconnection using + * 'prepareToDisconnectExternalDevice' method. + * 2. Reset port configuration using the 'resetAudioPortConfig' method. + * 3. Release the connected device port by calling the 'disconnectExternalDevice' * method. This also removes the audio routes associated with this * device port. * @@ -234,11 +236,15 @@ interface IModule { * instance previously instantiated using the 'connectExternalDevice' * method. * - * The framework will call this method before closing streams and resetting - * patches. This call can be used by the HAL module to prepare itself to - * device disconnection. If the HAL module indicates an error after the first - * call, the framework will call this method once again after closing associated - * streams and patches. + * On AIDL HAL v1, the framework will call this method before closing streams + * and resetting patches. This call can be used by the HAL module to prepare + * itself to device disconnection. If the HAL module indicates an error after + * the first call, the framework will call this method once again after closing + * associated streams and patches. + * + * On AIDL HAL v2 and later, the framework will call 'prepareToDisconnectExternalDevice' + * method to notify the HAL module to prepare itself for device disconnection. The + * framework will only call this method after closing associated streams and patches. * * @throws EX_ILLEGAL_ARGUMENT In the following cases: * - If the port can not be found by the ID. @@ -912,4 +918,20 @@ interface IModule { * @throw EX_UNSUPPORTED_OPERATION If the module does not support aaudio MMAP. */ int getAAudioHardwareBurstMinUsec(); + + /** + * Notify the HAL module to prepare for disconnecting an external device. + * + * This method is used to inform the HAL module that 'disconnectExternalDevice' will be + * called soon. The HAL module can rely on this method to abort active data operations + * early. The 'portId' must be of a connected device Port instance previously instantiated + * using 'connectExternalDevice' method. 'disconnectExternalDevice' method will be called + * soon after this method with the same 'portId'. + * + * @param portId The ID of the audio port that is about to disconnect + * @throws EX_ILLEGAL_ARGUMENT In the following cases: + * - If the port can not be found by the ID. + * - If this is not a connected device port. + */ + void prepareToDisconnectExternalDevice(int portId); } diff --git a/audio/aidl/android/hardware/audio/effect/Flags.aidl b/audio/aidl/android/hardware/audio/effect/Flags.aidl index 28685c3e03724664388242b29e936ff20dbc35e7..70668a39cfb87ae1e980f4eacc354d8ad33c22bc 100644 --- a/audio/aidl/android/hardware/audio/effect/Flags.aidl +++ b/audio/aidl/android/hardware/audio/effect/Flags.aidl @@ -144,4 +144,18 @@ parcelable Flags { * Set to true if the effect instance bypass audio data (no processing). */ boolean bypass; + + /** + * Effect instance sets this flag to true if it requires record AudioTrack metadata update. In + * this case the framework must call IEffect.setParameter to notify effect instance when there + * is a change in sinkMetadata. + */ + boolean sinkMetadataIndication; + + /** + * Effect instance sets this flag to true if it requires playback AudioTrack metadata update. In + * this case the framework must call IEffect.setParameter to notify effect instance when there + * is a change in sourceMetadata. + */ + boolean sourceMetadataIndication; } diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl index 09540558780a8332cd1759dc352cdcf358969a2c..6fd916133ea1578604712e7a297a8706d3f4bbdd 100644 --- a/audio/aidl/android/hardware/audio/effect/Parameter.aidl +++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl @@ -16,6 +16,8 @@ package android.hardware.audio.effect; +import android.hardware.audio.common.SinkMetadata; +import android.hardware.audio.common.SourceMetadata; import android.hardware.audio.effect.AcousticEchoCanceler; import android.hardware.audio.effect.AutomaticGainControlV1; import android.hardware.audio.effect.AutomaticGainControlV2; @@ -28,6 +30,7 @@ import android.hardware.audio.effect.HapticGenerator; import android.hardware.audio.effect.LoudnessEnhancer; import android.hardware.audio.effect.NoiseSuppression; import android.hardware.audio.effect.PresetReverb; +import android.hardware.audio.effect.Spatializer; import android.hardware.audio.effect.VendorExtension; import android.hardware.audio.effect.Virtualizer; import android.hardware.audio.effect.Visualizer; @@ -103,6 +106,11 @@ union Parameter { * directly. */ Parameter.Tag commonTag; + + /** + * Parameter tag defined for Spatializer parameters. + */ + Spatializer.Id spatializerTag; } /** @@ -189,6 +197,23 @@ union Parameter { Virtualizer virtualizer; Visualizer visualizer; Volume volume; + Spatializer spatializer; } Specific specific; + + /** + * SinkMetadata defines the metadata of record AudioTracks which the effect instance associate + * with. + * The effect engine is required to set Flags.sinkMetadataIndication to true if it wants to + * receive sinkMetadata update from the audio framework. + */ + SinkMetadata sinkMetadata; + + /** + * SourceMetadata defines the metadata of playback AudioTracks which the effect instance + * associate with. + * The effect engine is required to set Flags.sourceMetadataIndication to true if it wants to + * receive sourceMetadata update from the audio framework. + */ + SourceMetadata sourceMetadata; } diff --git a/audio/aidl/android/hardware/audio/effect/Range.aidl b/audio/aidl/android/hardware/audio/effect/Range.aidl index 567320a0bbf92bf9134d79b003ff62ad1edcecf8..e5acb681069700271ae69d083ff0db9d7299e960 100644 --- a/audio/aidl/android/hardware/audio/effect/Range.aidl +++ b/audio/aidl/android/hardware/audio/effect/Range.aidl @@ -28,6 +28,7 @@ import android.hardware.audio.effect.HapticGenerator; import android.hardware.audio.effect.LoudnessEnhancer; import android.hardware.audio.effect.NoiseSuppression; import android.hardware.audio.effect.PresetReverb; +import android.hardware.audio.effect.Spatializer; import android.hardware.audio.effect.VendorExtension; import android.hardware.audio.effect.Virtualizer; import android.hardware.audio.effect.Visualizer; @@ -168,6 +169,12 @@ union Range { PresetReverb max; } + @VintfStability + parcelable SpatializerRange { + Spatializer min; + Spatializer max; + } + @VintfStability parcelable VendorExtensionRange { VendorExtension min; @@ -217,4 +224,5 @@ union Range { VirtualizerRange[] virtualizer; VisualizerRange[] visualizer; VolumeRange[] volume; + SpatializerRange[] spatializer; } diff --git a/audio/aidl/android/hardware/audio/effect/Spatializer.aidl b/audio/aidl/android/hardware/audio/effect/Spatializer.aidl new file mode 100644 index 0000000000000000000000000000000000000000..6ebe0d58c06b598fc771142df2a3dbcf07d0436d --- /dev/null +++ b/audio/aidl/android/hardware/audio/effect/Spatializer.aidl @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.audio.effect; + +import android.hardware.audio.effect.VendorExtension; +import android.media.audio.common.AudioChannelLayout; +import android.media.audio.common.HeadTracking; +import android.media.audio.common.Spatialization; + +/** + * Union representing parameters for audio spatialization effects. + * + * Sound spatialization simulates sounds around the listener as if they were emanating from virtual + * positions based on the original recording. + * For more details, refer to the documentation: + * https://developer.android.com/reference/android/media/Spatializer. + * + * android.hardware.audio.effect.Spatializer specifies parameters for the implementation of audio + * spatialization effects. + * + * A Spatializer implementation must report its supported parameter ranges using Capability.Range. + * spatializer. + */ +@VintfStability +union Spatializer { + /** + * Parameter tag to identify the parameters for getParameter(). + */ + @VintfStability + union Id { + VendorExtension vendorExtensionTag; + Spatializer.Tag commonTag; + } + + /** + * Vendor extension implementation for additional parameters. + */ + VendorExtension vendor; + + /** + * List of supported input channel layouts. + */ + AudioChannelLayout[] supportedChannelLayout; + + /** + * Level of spatialization. + */ + Spatialization.Level spatializationLevel; + + /** + * Spatialization mode, Binaural or Transaural for example. + */ + Spatialization.Mode spatializationMode; + + /** + * Head tracking sensor ID. + */ + int headTrackingSensorId; + + /** + * Head tracking mode for spatialization. + */ + HeadTracking.Mode headTrackingMode; + + /** + * Head tracking sensor connection mode for spatialization. + */ + HeadTracking.ConnectionMode headTrackingConnectionMode; + + /** + * Headtracking sensor data. + */ + HeadTracking.SensorData headTrackingSensorData; +} diff --git a/audio/aidl/common/Android.bp b/audio/aidl/common/Android.bp index 4c6a74e563eda3ace498e56f1d9c7b5d3f2799a5..85ece3b026aa87b0a98bd4139adcf8eb91d63720 100644 --- a/audio/aidl/common/Android.bp +++ b/audio/aidl/common/Android.bp @@ -45,8 +45,8 @@ cc_library { name: "libaudioaidlranges", host_supported: true, vendor_available: true, - static_libs: [ - "android.hardware.audio.effect-V1-ndk", + defaults: [ + "latest_android_hardware_audio_effect_ndk_shared", ], export_include_dirs: ["include"], header_libs: ["libaudioaidl_headers"], @@ -59,8 +59,10 @@ cc_test { name: "libaudioaidlcommon_test", host_supported: true, vendor_available: true, + defaults: [ + "latest_android_media_audio_common_types_ndk_static", + ], static_libs: [ - "android.media.audio.common.types-V1-ndk", "libaudioaidlcommon", ], shared_libs: [ diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h index 3b08de7f207c8affab1b94275480d1d466b2da32..ef312d501c27b036dc0a6e47c12da4cb1ec92f7e 100644 --- a/audio/aidl/common/include/Utils.h +++ b/audio/aidl/common/include/Utils.h @@ -174,4 +174,12 @@ constexpr U makeBitPositionFlagMask(std::initializer_list flags) { return result; } +constexpr int32_t frameCountFromDurationUs(long durationUs, int32_t sampleRateHz) { + return (static_cast(durationUs) * sampleRateHz) / 1000000LL; +} + +constexpr int32_t frameCountFromDurationMs(int32_t durationMs, int32_t sampleRateHz) { + return frameCountFromDurationUs(durationMs * 1000, sampleRateHz); +} + } // namespace aidl::android::hardware::audio::common diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index bb8d76f8fca54d36bbf95eb58a0b4779cbe4c6b6..7cd054535c31d9852174fcfb74fc15898744ed57 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -12,6 +12,7 @@ cc_defaults { vendor: true, shared_libs: [ "libalsautilsv2", + "libaudio_aidl_conversion_common_ndk", "libaudioaidlcommon", "libaudioutils", "libbase", @@ -19,6 +20,8 @@ cc_defaults { "libcutils", "libfmq", "libnbaio_mono", + "liblog", + "libmedia_helper", "libstagefright_foundation", "libtinyalsav2", "libutils", @@ -31,28 +34,8 @@ cc_defaults { "libaudioaidl_headers", "libxsdc-utils", ], -} - -cc_library { - name: "libaudioservicesounddoseimpl", - vendor: true, - defaults: [ - "latest_android_media_audio_common_types_ndk_shared", - "latest_android_hardware_audio_core_sounddose_ndk_shared", - "latest_android_hardware_audio_sounddose_ndk_shared", - ], - export_include_dirs: ["include"], - srcs: [ - "SoundDose.cpp", - ], - shared_libs: [ - "libbase", - "libbinder_ndk", - "libcutils", - "libutils", - ], - visibility: [ - "//hardware/interfaces/audio/aidl/sounddose/default", + cflags: [ + "-DBACKEND_NDK", ], } @@ -63,6 +46,7 @@ cc_library { "latest_android_media_audio_common_types_ndk_shared", "latest_android_hardware_audio_core_ndk_shared", "latest_android_hardware_audio_core_sounddose_ndk_shared", + "latest_android_hardware_bluetooth_audio_ndk_shared", ], export_include_dirs: ["include"], srcs: [ @@ -78,6 +62,7 @@ cc_library { "Stream.cpp", "StreamSwitcher.cpp", "Telephony.cpp", + "XsdcConversion.cpp", "alsa/Mixer.cpp", "alsa/ModuleAlsa.cpp", "alsa/StreamAlsa.cpp", @@ -109,10 +94,13 @@ cc_library { "audio_policy_engine_configuration_aidl_default", ], shared_libs: [ - "android.hardware.bluetooth.audio-V3-ndk", + "android.hardware.bluetooth.audio-impl", "libaudio_aidl_conversion_common_ndk", + "libaudioutils", "libbluetooth_audio_session_aidl", + "liblog", "libmedia_helper", + "libmediautils_vendor", "libstagefright_foundation", ], export_shared_lib_headers: [ @@ -134,17 +122,19 @@ cc_binary { vintf_fragments: ["android.hardware.audio.service-aidl.xml"], defaults: [ "aidlaudioservice_defaults", - "latest_android_media_audio_common_types_ndk_shared", "latest_android_hardware_audio_core_sounddose_ndk_shared", "latest_android_hardware_audio_core_ndk_shared", + "latest_android_hardware_bluetooth_audio_ndk_shared", + "latest_android_media_audio_common_types_ndk_shared", ], static_libs: [ "libaudioserviceexampleimpl", ], shared_libs: [ - "android.hardware.bluetooth.audio-V3-ndk", + "android.hardware.bluetooth.audio-impl", "libaudio_aidl_conversion_common_ndk", "libbluetooth_audio_session_aidl", + "liblog", "libmedia_helper", "libstagefright_foundation", ], @@ -172,6 +162,7 @@ cc_test { "libbase", "libbinder_ndk", "libcutils", + "libfmq", "libmedia_helper", "libstagefright_foundation", "libutils", @@ -184,9 +175,11 @@ cc_test { ], generated_sources: [ "audio_policy_configuration_aidl_default", + "audio_policy_engine_configuration_aidl_default", ], generated_headers: [ "audio_policy_configuration_aidl_default", + "audio_policy_engine_configuration_aidl_default", ], srcs: [ "AudioPolicyConfigXmlConverter.cpp", diff --git a/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp b/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp index 7452c8ec7ac54abad3d947ba9043f988eeac334a..2f1282a3064cd33539c390ea033a6789ce58fd4c 100644 --- a/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp +++ b/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp @@ -30,6 +30,7 @@ #include "core-impl/AidlConversionXsdc.h" #include "core-impl/AudioPolicyConfigXmlConverter.h" +#include "core-impl/XsdcConversion.h" using aidl::android::media::audio::common::AudioFormatDescription; using aidl::android::media::audio::common::AudioHalEngineConfig; @@ -37,60 +38,39 @@ using aidl::android::media::audio::common::AudioHalVolumeCurve; using aidl::android::media::audio::common::AudioHalVolumeGroup; using aidl::android::media::audio::common::AudioStreamType; -namespace xsd = android::audio::policy::configuration; +namespace ap_xsd = android::audio::policy::configuration; namespace aidl::android::hardware::audio::core::internal { static const int kDefaultVolumeIndexMin = 0; static const int kDefaultVolumeIndexMax = 100; static const int KVolumeIndexDeferredToAudioService = -1; -/** - * Valid curve points take the form ",", where the index - * must be in the range [0,100]. kInvalidCurvePointIndex is used to indicate - * that a point was formatted incorrectly (e.g. if a vendor accidentally typed a - * '.' instead of a ',' in their XML) -- using such a curve point will result in - * failed VTS tests. - */ -static const int8_t kInvalidCurvePointIndex = -1; - -AudioHalVolumeCurve::CurvePoint AudioPolicyConfigXmlConverter::convertCurvePointToAidl( - const std::string& xsdcCurvePoint) { - AudioHalVolumeCurve::CurvePoint aidlCurvePoint{}; - if (sscanf(xsdcCurvePoint.c_str(), "%" SCNd8 ",%d", &aidlCurvePoint.index, - &aidlCurvePoint.attenuationMb) != 2) { - aidlCurvePoint.index = kInvalidCurvePointIndex; - } - return aidlCurvePoint; -} -AudioHalVolumeCurve AudioPolicyConfigXmlConverter::convertVolumeCurveToAidl( - const xsd::Volume& xsdcVolumeCurve) { +ConversionResult AudioPolicyConfigXmlConverter::convertVolumeCurveToAidl( + const ap_xsd::Volume& xsdcVolumeCurve) { AudioHalVolumeCurve aidlVolumeCurve; aidlVolumeCurve.deviceCategory = static_cast(xsdcVolumeCurve.getDeviceCategory()); if (xsdcVolumeCurve.hasRef()) { if (mVolumesReferenceMap.empty()) { - mVolumesReferenceMap = generateReferenceMap( + mVolumesReferenceMap = generateReferenceMap( getXsdcConfig()->getVolumes()); } - aidlVolumeCurve.curvePoints = - convertCollectionToAidlUnchecked( + aidlVolumeCurve.curvePoints = VALUE_OR_FATAL( + (convertCollectionToAidl( mVolumesReferenceMap.at(xsdcVolumeCurve.getRef()).getPoint(), - std::bind(&AudioPolicyConfigXmlConverter::convertCurvePointToAidl, this, - std::placeholders::_1)); + &convertCurvePointToAidl))); } else { - aidlVolumeCurve.curvePoints = - convertCollectionToAidlUnchecked( - xsdcVolumeCurve.getPoint(), - std::bind(&AudioPolicyConfigXmlConverter::convertCurvePointToAidl, this, - std::placeholders::_1)); + aidlVolumeCurve.curvePoints = VALUE_OR_FATAL( + (convertCollectionToAidl( + xsdcVolumeCurve.getPoint(), &convertCurvePointToAidl))); } return aidlVolumeCurve; } -void AudioPolicyConfigXmlConverter::mapStreamToVolumeCurve(const xsd::Volume& xsdcVolumeCurve) { +void AudioPolicyConfigXmlConverter::mapStreamToVolumeCurve(const ap_xsd::Volume& xsdcVolumeCurve) { mStreamToVolumeCurvesMap[xsdcVolumeCurve.getStream()].push_back( - convertVolumeCurveToAidl(xsdcVolumeCurve)); + VALUE_OR_FATAL(convertVolumeCurveToAidl(xsdcVolumeCurve))); } const SurroundSoundConfig& AudioPolicyConfigXmlConverter::getSurroundSoundConfig() { @@ -109,6 +89,11 @@ const SurroundSoundConfig& AudioPolicyConfigXmlConverter::getSurroundSoundConfig return aidlSurroundSoundConfig; } +std::unique_ptr +AudioPolicyConfigXmlConverter::releaseModuleConfigs() { + return std::move(mModuleConfigurations); +} + const AudioHalEngineConfig& AudioPolicyConfigXmlConverter::getAidlEngineConfig() { if (mAidlEngineConfig.volumeGroups.empty() && getXsdcConfig() && getXsdcConfig()->hasVolumes()) { @@ -160,8 +145,8 @@ const SurroundSoundConfig& AudioPolicyConfigXmlConverter::getDefaultSurroundSoun void AudioPolicyConfigXmlConverter::mapStreamsToVolumeCurves() { if (getXsdcConfig()->hasVolumes()) { - for (const xsd::Volumes& xsdcWrapperType : getXsdcConfig()->getVolumes()) { - for (const xsd::Volume& xsdcVolume : xsdcWrapperType.getVolume()) { + for (const ap_xsd::Volumes& xsdcWrapperType : getXsdcConfig()->getVolumes()) { + for (const ap_xsd::Volume& xsdcVolume : xsdcWrapperType.getVolume()) { mapStreamToVolumeCurve(xsdcVolume); } } @@ -171,7 +156,7 @@ void AudioPolicyConfigXmlConverter::mapStreamsToVolumeCurves() { void AudioPolicyConfigXmlConverter::addVolumeGroupstoEngineConfig() { for (const auto& [xsdcStream, volumeCurves] : mStreamToVolumeCurvesMap) { AudioHalVolumeGroup volumeGroup; - volumeGroup.name = xsd::toString(xsdcStream); + volumeGroup.name = ap_xsd::toString(xsdcStream); if (static_cast(xsdcStream) >= AUDIO_STREAM_PUBLIC_CNT) { volumeGroup.minIndex = kDefaultVolumeIndexMin; volumeGroup.maxIndex = kDefaultVolumeIndexMax; @@ -190,4 +175,24 @@ void AudioPolicyConfigXmlConverter::parseVolumes() { addVolumeGroupstoEngineConfig(); } } + +void AudioPolicyConfigXmlConverter::init() { + if (!getXsdcConfig()->hasModules()) return; + for (const ap_xsd::Modules& xsdcModulesType : getXsdcConfig()->getModules()) { + if (!xsdcModulesType.has_module()) continue; + for (const ap_xsd::Modules::Module& xsdcModule : xsdcModulesType.get_module()) { + // 'primary' in the XML schema used by HIDL is equivalent to 'default' module. + const std::string name = + xsdcModule.getName() != "primary" ? xsdcModule.getName() : "default"; + if (name != "r_submix") { + mModuleConfigurations->emplace_back( + name, VALUE_OR_FATAL(convertModuleConfigToAidl(xsdcModule))); + } else { + // See the note on the 'getRSubmixConfiguration' function. + mModuleConfigurations->emplace_back(name, nullptr); + } + } + } +} + } // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp index b9f1131ecab3d02ef07cc1fbc88a862ce767e6b5..2a8e58f84341a62f7660e48929dbc27e8005f07e 100644 --- a/audio/aidl/default/Configuration.cpp +++ b/audio/aidl/default/Configuration.cpp @@ -41,8 +41,8 @@ using aidl::android::media::audio::common::AudioPortExt; using aidl::android::media::audio::common::AudioPortMixExt; using aidl::android::media::audio::common::AudioProfile; using aidl::android::media::audio::common::Int; -using aidl::android::media::audio::common::MicrophoneInfo; using aidl::android::media::audio::common::PcmType; +using Configuration = aidl::android::hardware::audio::core::Module::Configuration; namespace aidl::android::hardware::audio::core::internal { @@ -105,15 +105,11 @@ static AudioPort createPort(int32_t id, const std::string& name, int32_t flags, return port; } -static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmType, int32_t layout, - int32_t sampleRate, int32_t flags, bool isInput, - const AudioPortExt& ext) { +static AudioPortConfig createDynamicPortConfig(int32_t id, int32_t portId, int32_t flags, + bool isInput, const AudioPortExt& ext) { AudioPortConfig config; config.id = id; config.portId = portId; - config.sampleRate = Int{.value = sampleRate}; - config.channelMask = AudioChannelLayout::make(layout); - config.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType}; config.gain = AudioGainConfig(); config.flags = isInput ? AudioIoFlags::make(flags) : AudioIoFlags::make(flags); @@ -121,6 +117,16 @@ static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmT return config; } +static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmType, int32_t layout, + int32_t sampleRate, int32_t flags, bool isInput, + const AudioPortExt& ext) { + AudioPortConfig config = createDynamicPortConfig(id, portId, flags, isInput, ext); + config.sampleRate = Int{.value = sampleRate}; + config.channelMask = AudioChannelLayout::make(layout); + config.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType}; + return config; +} + static AudioRoute createRoute(const std::vector& sources, const AudioPort& sink) { AudioRoute route; route.sinkPortId = sink.id; @@ -129,6 +135,22 @@ static AudioRoute createRoute(const std::vector& sources, const Audio return route; } +std::vector getStandard16And24BitPcmAudioProfiles() { + auto createStdPcmAudioProfile = [](const PcmType& pcmType) { + return AudioProfile{ + .format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType}, + .channelMasks = {AudioChannelLayout::make( + AudioChannelLayout::LAYOUT_MONO), + AudioChannelLayout::make( + AudioChannelLayout::LAYOUT_STEREO)}, + .sampleRates = {8000, 11025, 16000, 32000, 44100, 48000}}; + }; + return { + createStdPcmAudioProfile(PcmType::INT_16_BIT), + createStdPcmAudioProfile(PcmType::INT_24_BIT), + }; +} + // Primary (default) configuration: // // Device ports: @@ -147,8 +169,7 @@ static AudioRoute createRoute(const std::vector& sources, const Audio // * "primary output", PRIMARY, 1 max open, 1 max active stream // - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 // * "primary input", 1 max open, 1 max active stream -// - profile PCM 16-bit; MONO, STEREO; -// 8000, 11025, 16000, 32000, 44100, 48000 +// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 // * "telephony_tx", 1 max open, 1 max active stream // - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 // * "telephony_rx", 1 max open, 1 max active stream @@ -164,11 +185,11 @@ static AudioRoute createRoute(const std::vector& sources, const Audio // "FM Tuner" -> "fm_tuner" // // Initial port configs: -// * "Speaker" device port: PCM 16-bit; STEREO; 48000 -// * "Built-In Mic" device port: PCM 16-bit; MONO; 48000 -// * "Telephony Tx" device port: PCM 16-bit; MONO; 48000 -// * "Telephony Rx" device port: PCM 16-bit; MONO; 48000 -// * "FM Tuner" device port: PCM 16-bit; STEREO; 48000 +// * "Speaker" device port: dynamic configuration +// * "Built-In Mic" device port: dynamic configuration +// * "Telephony Tx" device port: dynamic configuration +// * "Telephony Rx" device port: dynamic configuration +// * "FM Tuner" device port: dynamic configuration // std::unique_ptr getPrimaryConfiguration() { static const Configuration configuration = []() { @@ -186,9 +207,8 @@ std::unique_ptr getPrimaryConfiguration() { 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE)); c.ports.push_back(speakerOutDevice); c.initialConfigs.push_back( - createPortConfig(speakerOutDevice.id, speakerOutDevice.id, PcmType::INT_16_BIT, - AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false, - createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0))); + createDynamicPortConfig(speakerOutDevice.id, speakerOutDevice.id, 0, false, + createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0))); AudioPort micInDevice = createPort(c.nextPortId++, "Built-In Mic", 0, true, @@ -196,35 +216,31 @@ std::unique_ptr getPrimaryConfiguration() { 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE)); c.ports.push_back(micInDevice); c.initialConfigs.push_back( - createPortConfig(micInDevice.id, micInDevice.id, PcmType::INT_16_BIT, - AudioChannelLayout::LAYOUT_MONO, 48000, 0, true, - createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0))); + createDynamicPortConfig(micInDevice.id, micInDevice.id, 0, true, + createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0))); AudioPort telephonyTxOutDevice = createPort(c.nextPortId++, "Telephony Tx", 0, false, createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0)); c.ports.push_back(telephonyTxOutDevice); c.initialConfigs.push_back( - createPortConfig(telephonyTxOutDevice.id, telephonyTxOutDevice.id, - PcmType::INT_16_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0, - false, createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0))); + createDynamicPortConfig(telephonyTxOutDevice.id, telephonyTxOutDevice.id, 0, false, + createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0))); AudioPort telephonyRxInDevice = createPort(c.nextPortId++, "Telephony Rx", 0, true, createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0)); c.ports.push_back(telephonyRxInDevice); c.initialConfigs.push_back( - createPortConfig(telephonyRxInDevice.id, telephonyRxInDevice.id, - PcmType::INT_16_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0, - true, createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0))); + createDynamicPortConfig(telephonyRxInDevice.id, telephonyRxInDevice.id, 0, true, + createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0))); AudioPort fmTunerInDevice = createPort(c.nextPortId++, "FM Tuner", 0, true, createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)); c.ports.push_back(fmTunerInDevice); c.initialConfigs.push_back( - createPortConfig(fmTunerInDevice.id, fmTunerInDevice.id, PcmType::INT_16_BIT, - AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true, - createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0))); + createDynamicPortConfig(fmTunerInDevice.id, fmTunerInDevice.id, 0, true, + createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0))); // Mix ports @@ -237,7 +253,7 @@ std::unique_ptr getPrimaryConfiguration() { c.ports.push_back(primaryOutMix); AudioPort primaryInMix = - createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(0, 0)); + createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(0, 1)); primaryInMix.profiles.push_back( createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, @@ -252,14 +268,14 @@ std::unique_ptr getPrimaryConfiguration() { c.ports.push_back(telephonyTxOutMix); AudioPort telephonyRxInMix = - createPort(c.nextPortId++, "telephony_rx", 0, true, createPortMixExt(1, 1)); + createPort(c.nextPortId++, "telephony_rx", 0, true, createPortMixExt(0, 1)); telephonyRxInMix.profiles.insert(telephonyRxInMix.profiles.begin(), standardPcmAudioProfiles.begin(), standardPcmAudioProfiles.end()); c.ports.push_back(telephonyRxInMix); AudioPort fmTunerInMix = - createPort(c.nextPortId++, "fm_tuner", 0, true, createPortMixExt(1, 1)); + createPort(c.nextPortId++, "fm_tuner", 0, true, createPortMixExt(0, 1)); fmTunerInMix.profiles.insert(fmTunerInMix.profiles.begin(), standardPcmAudioProfiles.begin(), standardPcmAudioProfiles.end()); @@ -273,18 +289,28 @@ std::unique_ptr getPrimaryConfiguration() { c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end()); - MicrophoneInfo mic; - mic.id = "mic"; - mic.device = micInDevice.ext.get().device; - mic.group = 0; - mic.indexInTheGroup = 0; - c.microphones = std::vector{mic}; - return c; }(); return std::make_unique(configuration); } +// Note: When transitioning to loading of XML configs, either keep the configuration +// of the remote submix sources from this static configuration, or update the XML +// config to match it. There are several reasons for that: +// 1. The "Remote Submix In" device is listed in the XML config as "attached", +// however in the AIDL scheme its device type has a "virtual" connection. +// 2. The canonical r_submix configuration only lists 'STEREO' and '48000', +// however the framework attempts to open streams for other sample rates +// as well. The legacy r_submix implementation allowed that, but libaudiohal@aidl +// will not find a mix port to use. Because of that, list all sample rates that +// the legacy implementation allowed (note that mono was not allowed, the framework +// is expected to upmix mono tracks into stereo if needed). +// 3. The legacy implementation had a hard limit on the number of routes (10), +// and this is checked indirectly by AudioPlaybackCaptureTest#testPlaybackCaptureDoS +// CTS test. Instead of hardcoding the number of routes, we can use +// "maxOpen/ActiveStreamCount" to enforce a similar limit. However, the canonical +// XML file lacks this specification. +// // Remote Submix configuration: // // Device ports: @@ -294,16 +320,10 @@ std::unique_ptr getPrimaryConfiguration() { // - no profiles specified // // Mix ports: -// * "r_submix output", unlimited max open, unlimited max active stream -// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// - profile PCM 32-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// - profile PCM 32-bit float; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// * "r_submix input", unlimited max open, unlimited max active stream -// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// - profile PCM 32-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// - profile PCM 32-bit float; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 +// * "r_submix output", maximum 10 opened streams, maximum 10 active streams +// - profile PCM 16-bit; STEREO; 8000, 11025, 16000, 32000, 44100, 48000 +// * "r_submix input", maximum 10 opened streams, maximum 10 active streams +// - profile PCM 16-bit; STEREO; 8000, 11025, 16000, 32000, 44100, 48000 // // Routes: // "r_submix output" -> "Remote Submix Out" @@ -312,18 +332,8 @@ std::unique_ptr getPrimaryConfiguration() { std::unique_ptr getRSubmixConfiguration() { static const Configuration configuration = []() { Configuration c; - const std::vector standardPcmAudioProfiles{ - createProfile(PcmType::FLOAT_32_BIT, - {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, - {8000, 11025, 16000, 32000, 44100, 48000}), - createProfile(PcmType::INT_32_BIT, - {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, - {8000, 11025, 16000, 32000, 44100, 48000}), - createProfile(PcmType::INT_24_BIT, - {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, - {8000, 11025, 16000, 32000, 44100, 48000}), - createProfile(PcmType::INT_16_BIT, - {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, + const std::vector remoteSubmixPcmAudioProfiles{ + createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {8000, 11025, 16000, 32000, 44100, 48000})}; // Device ports @@ -333,25 +343,25 @@ std::unique_ptr getRSubmixConfiguration() { createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0, AudioDeviceDescription::CONNECTION_VIRTUAL)); c.ports.push_back(rsubmixOutDevice); - c.connectedProfiles[rsubmixOutDevice.id] = standardPcmAudioProfiles; + c.connectedProfiles[rsubmixOutDevice.id] = remoteSubmixPcmAudioProfiles; AudioPort rsubmixInDevice = createPort(c.nextPortId++, "Remote Submix In", 0, true, createDeviceExt(AudioDeviceType::IN_SUBMIX, 0, AudioDeviceDescription::CONNECTION_VIRTUAL)); c.ports.push_back(rsubmixInDevice); - c.connectedProfiles[rsubmixInDevice.id] = standardPcmAudioProfiles; + c.connectedProfiles[rsubmixInDevice.id] = remoteSubmixPcmAudioProfiles; // Mix ports AudioPort rsubmixOutMix = - createPort(c.nextPortId++, "r_submix output", 0, false, createPortMixExt(0, 0)); - rsubmixOutMix.profiles = standardPcmAudioProfiles; + createPort(c.nextPortId++, "r_submix output", 0, false, createPortMixExt(10, 10)); + rsubmixOutMix.profiles = remoteSubmixPcmAudioProfiles; c.ports.push_back(rsubmixOutMix); AudioPort rsubmixInMix = - createPort(c.nextPortId++, "r_submix input", 0, true, createPortMixExt(0, 0)); - rsubmixInMix.profiles = standardPcmAudioProfiles; + createPort(c.nextPortId++, "r_submix input", 0, true, createPortMixExt(10, 10)); + rsubmixInMix.profiles = remoteSubmixPcmAudioProfiles; c.ports.push_back(rsubmixInMix); c.routes.push_back(createRoute({rsubmixOutMix}, rsubmixOutDevice)); @@ -385,7 +395,7 @@ std::unique_ptr getRSubmixConfiguration() { // * "usb_device output" -> "USB Headset Out" // * "USB Device In", "USB Headset In" -> "usb_device input" // -// Profiles for device port connected state: +// Profiles for device port connected state (when simulating connections): // * "USB Device Out", "USB Headset Out": // - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000 // - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000 @@ -442,7 +452,7 @@ std::unique_ptr getUsbConfiguration() { c.ports.push_back(usbDeviceOutMix); AudioPort usbDeviceInMix = - createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(1, 1)); + createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(0, 1)); c.ports.push_back(usbDeviceInMix); c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutDevice)); @@ -462,9 +472,9 @@ std::unique_ptr getUsbConfiguration() { // * "Test In", IN_AFE_PROXY // - no profiles specified // * "Wired Headset", OUT_HEADSET -// - profile PCM 24-bit; STEREO; 48000 +// - no profiles specified // * "Wired Headset Mic", IN_HEADSET -// - profile PCM 24-bit; MONO; 48000 +// - no profiles specified // // Mix ports: // * "test output", 1 max open, 1 max active stream @@ -487,6 +497,10 @@ std::unique_ptr getUsbConfiguration() { // * "Test Out" device port: PCM 24-bit; STEREO; 48000 // * "Test In" device port: PCM 24-bit; MONO; 48000 // +// Profiles for device port connected state (when simulating connections): +// * "Wired Headset": dynamic profiles +// * "Wired Headset Mic": dynamic profiles +// std::unique_ptr getStubConfiguration() { static const Configuration configuration = []() { Configuration c; @@ -505,8 +519,6 @@ std::unique_ptr getStubConfiguration() { createPort(c.nextPortId++, "Wired Headset", 0, false, createDeviceExt(AudioDeviceType::OUT_HEADSET, 0, AudioDeviceDescription::CONNECTION_ANALOG)); - headsetOutDevice.profiles.push_back( - createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})); c.ports.push_back(headsetOutDevice); AudioPort testInDevice = createPort(c.nextPortId++, "Test In", 0, true, @@ -521,8 +533,6 @@ std::unique_ptr getStubConfiguration() { createPort(c.nextPortId++, "Wired Headset Mic", 0, true, createDeviceExt(AudioDeviceType::IN_HEADSET, 0, AudioDeviceDescription::CONNECTION_ANALOG)); - headsetInDevice.profiles.push_back( - createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_MONO}, {48000})); c.ports.push_back(headsetInDevice); // Mix ports @@ -554,24 +564,24 @@ std::unique_ptr getStubConfiguration() { {44100, 48000})); c.ports.push_back(compressedOffloadOutMix); - AudioPort testInMIx = + AudioPort testInMix = createPort(c.nextPortId++, "test input", 0, true, createPortMixExt(2, 2)); - testInMIx.profiles.push_back( + testInMix.profiles.push_back( createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO, AudioChannelLayout::LAYOUT_FRONT_BACK}, {8000, 11025, 16000, 22050, 32000, 44100, 48000})); - testInMIx.profiles.push_back( + testInMix.profiles.push_back( createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO, AudioChannelLayout::LAYOUT_FRONT_BACK}, {8000, 11025, 16000, 22050, 32000, 44100, 48000})); - c.ports.push_back(testInMIx); + c.ports.push_back(testInMix); c.routes.push_back( createRoute({testOutMix, testFastOutMix, compressedOffloadOutMix}, testOutDevice)); c.routes.push_back(createRoute({testOutMix}, headsetOutDevice)); - c.routes.push_back(createRoute({testInDevice, headsetInDevice}, testInMIx)); + c.routes.push_back(createRoute({testInDevice, headsetInDevice}, testInMix)); c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end()); @@ -604,11 +614,19 @@ std::unique_ptr getStubConfiguration() { // "a2dp output" -> "BT A2DP Speaker" // "hearing aid output" -> "BT Hearing Aid Out" // +// Profiles for device port connected state (when simulating connections): +// * "BT A2DP Out", "BT A2DP Headphones", "BT A2DP Speaker": +// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000 +// * "BT Hearing Aid Out": +// - profile PCM 16-bit; STEREO; 16000, 24000 +// std::unique_ptr getBluetoothConfiguration() { static const Configuration configuration = []() { const std::vector standardPcmAudioProfiles = { createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {44100, 48000, 88200, 96000})}; + const std::vector hearingAidAudioProfiles = {createProfile( + PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000})}; Configuration c; // Device ports @@ -646,8 +664,7 @@ std::unique_ptr getBluetoothConfiguration() { createDeviceExt(AudioDeviceType::OUT_HEARING_AID, 0, AudioDeviceDescription::CONNECTION_WIRELESS)); c.ports.push_back(btOutHearingAid); - c.connectedProfiles[btOutHearingAid.id] = std::vector( - {createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000})}); + c.connectedProfiles[btOutHearingAid.id] = hearingAidAudioProfiles; // Mix ports AudioPort btOutMix = @@ -656,8 +673,7 @@ std::unique_ptr getBluetoothConfiguration() { AudioPort btHearingOutMix = createPort(c.nextPortId++, "hearing aid output", 0, false, createPortMixExt(1, 1)); - btHearingOutMix.profiles.push_back(createProfile( - PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000})); + btHearingOutMix.profiles = hearingAidAudioProfiles; c.ports.push_back(btHearingOutMix); c.routes.push_back(createRoute({btOutMix}, btOutDevice)); @@ -670,4 +686,19 @@ std::unique_ptr getBluetoothConfiguration() { return std::make_unique(configuration); } +std::unique_ptr getConfiguration(Module::Type moduleType) { + switch (moduleType) { + case Module::Type::DEFAULT: + return getPrimaryConfiguration(); + case Module::Type::R_SUBMIX: + return getRSubmixConfiguration(); + case Module::Type::STUB: + return getStubConfiguration(); + case Module::Type::USB: + return getUsbConfiguration(); + case Module::Type::BLUETOOTH: + return getBluetoothConfiguration(); + } +} + } // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/EffectConfig.cpp b/audio/aidl/default/EffectConfig.cpp index 730c0bf758a519f17ffdee10f4eee2fe4bd4c758..4a12f8a7dfa091bc7ad7ee087287a227bd2117cd 100644 --- a/audio/aidl/default/EffectConfig.cpp +++ b/audio/aidl/default/EffectConfig.cpp @@ -196,16 +196,16 @@ std::optional EffectConfig::stringToProcessingType(Processing: // see list of audio sources in audio_source_t: // system/media/audio/include/system/audio_effects/audio_effects_conf.h static const std::map sAudioSourceTable = { - {MIC_SRC_TAG, AudioSource::VOICE_CALL}, - {VOICE_UL_SRC_TAG, AudioSource::VOICE_CALL}, - {VOICE_DL_SRC_TAG, AudioSource::VOICE_CALL}, + {MIC_SRC_TAG, AudioSource::MIC}, + {VOICE_UL_SRC_TAG, AudioSource::VOICE_UPLINK}, + {VOICE_DL_SRC_TAG, AudioSource::VOICE_DOWNLINK}, {VOICE_CALL_SRC_TAG, AudioSource::VOICE_CALL}, - {CAMCORDER_SRC_TAG, AudioSource::VOICE_CALL}, - {VOICE_REC_SRC_TAG, AudioSource::VOICE_CALL}, - {VOICE_COMM_SRC_TAG, AudioSource::VOICE_CALL}, - {REMOTE_SUBMIX_SRC_TAG, AudioSource::VOICE_CALL}, - {UNPROCESSED_SRC_TAG, AudioSource::VOICE_CALL}, - {VOICE_PERFORMANCE_SRC_TAG, AudioSource::VOICE_CALL}}; + {CAMCORDER_SRC_TAG, AudioSource::CAMCORDER}, + {VOICE_REC_SRC_TAG, AudioSource::VOICE_RECOGNITION}, + {VOICE_COMM_SRC_TAG, AudioSource::VOICE_COMMUNICATION}, + {REMOTE_SUBMIX_SRC_TAG, AudioSource::REMOTE_SUBMIX}, + {UNPROCESSED_SRC_TAG, AudioSource::UNPROCESSED}, + {VOICE_PERFORMANCE_SRC_TAG, AudioSource::VOICE_PERFORMANCE}}; if (typeTag == Processing::Type::streamType) { auto typeIter = sAudioStreamTypeTable.find(type); diff --git a/audio/aidl/default/EngineConfigXmlConverter.cpp b/audio/aidl/default/EngineConfigXmlConverter.cpp index 96b555c640b9907aa5b1abc68e88ee202a27cee3..631cdce1a0cb2672b87014174239f3a04e8edc30 100644 --- a/audio/aidl/default/EngineConfigXmlConverter.cpp +++ b/audio/aidl/default/EngineConfigXmlConverter.cpp @@ -20,11 +20,14 @@ #include #include +#define LOG_TAG "AHAL_Config" #include #include #include +#include #include "core-impl/EngineConfigXmlConverter.h" +#include "core-impl/XsdcConversion.h" using aidl::android::media::audio::common::AudioAttributes; using aidl::android::media::audio::common::AudioContentType; @@ -40,20 +43,13 @@ using aidl::android::media::audio::common::AudioProductStrategyType; using aidl::android::media::audio::common::AudioSource; using aidl::android::media::audio::common::AudioStreamType; using aidl::android::media::audio::common::AudioUsage; +using ::android::BAD_VALUE; +using ::android::base::unexpected; -namespace xsd = android::audio::policy::engine::configuration; +namespace eng_xsd = android::audio::policy::engine::configuration; namespace aidl::android::hardware::audio::core::internal { -/** - * Valid curve points take the form ",", where the index - * must be in the range [0,100]. kInvalidCurvePointIndex is used to indicate - * that a point was formatted incorrectly (e.g. if a vendor accidentally typed a - * '.' instead of a ',' in their XML)-- using such a curve point will result in - * failed VTS tests. - */ -static const int8_t kInvalidCurvePointIndex = -1; - void EngineConfigXmlConverter::initProductStrategyMap() { #define STRATEGY_ENTRY(name) {"STRATEGY_" #name, static_cast(AudioProductStrategyType::name)} @@ -68,7 +64,7 @@ void EngineConfigXmlConverter::initProductStrategyMap() { #undef STRATEGY_ENTRY } -int EngineConfigXmlConverter::convertProductStrategyNameToAidl( +ConversionResult EngineConfigXmlConverter::convertProductStrategyNameToAidl( const std::string& xsdcProductStrategyName) { const auto [it, success] = mProductStrategyMap.insert( std::make_pair(xsdcProductStrategyName, mNextVendorStrategy)); @@ -85,12 +81,12 @@ bool isDefaultAudioAttributes(const AudioAttributes& attributes) { (attributes.tags.empty())); } -AudioAttributes EngineConfigXmlConverter::convertAudioAttributesToAidl( - const xsd::AttributesType& xsdcAudioAttributes) { +ConversionResult EngineConfigXmlConverter::convertAudioAttributesToAidl( + const eng_xsd::AttributesType& xsdcAudioAttributes) { if (xsdcAudioAttributes.hasAttributesRef()) { if (mAttributesReferenceMap.empty()) { mAttributesReferenceMap = - generateReferenceMap( + generateReferenceMap( getXsdcConfig()->getAttributesRef()); } return convertAudioAttributesToAidl( @@ -111,16 +107,16 @@ AudioAttributes EngineConfigXmlConverter::convertAudioAttributesToAidl( static_cast(xsdcAudioAttributes.getFirstSource()->getValue()); } if (xsdcAudioAttributes.hasFlags()) { - std::vector xsdcFlagTypeVec = + std::vector xsdcFlagTypeVec = xsdcAudioAttributes.getFirstFlags()->getValue(); - for (const xsd::FlagType& xsdcFlagType : xsdcFlagTypeVec) { - if (xsdcFlagType != xsd::FlagType::AUDIO_FLAG_NONE) { + for (const eng_xsd::FlagType& xsdcFlagType : xsdcFlagTypeVec) { + if (xsdcFlagType != eng_xsd::FlagType::AUDIO_FLAG_NONE) { aidlAudioAttributes.flags |= 1 << (static_cast(xsdcFlagType) - 1); } } } if (xsdcAudioAttributes.hasBundle()) { - const xsd::BundleType* xsdcBundle = xsdcAudioAttributes.getFirstBundle(); + const eng_xsd::BundleType* xsdcBundle = xsdcAudioAttributes.getFirstBundle(); aidlAudioAttributes.tags[0] = xsdcBundle->getKey() + "=" + xsdcBundle->getValue(); } if (isDefaultAudioAttributes(aidlAudioAttributes)) { @@ -129,53 +125,54 @@ AudioAttributes EngineConfigXmlConverter::convertAudioAttributesToAidl( return aidlAudioAttributes; } -AudioHalAttributesGroup EngineConfigXmlConverter::convertAttributesGroupToAidl( - const xsd::AttributesGroup& xsdcAttributesGroup) { +ConversionResult EngineConfigXmlConverter::convertAttributesGroupToAidl( + const eng_xsd::AttributesGroup& xsdcAttributesGroup) { AudioHalAttributesGroup aidlAttributesGroup; static const int kStreamTypeEnumOffset = - static_cast(xsd::Stream::AUDIO_STREAM_VOICE_CALL) - + static_cast(eng_xsd::Stream::AUDIO_STREAM_VOICE_CALL) - static_cast(AudioStreamType::VOICE_CALL); aidlAttributesGroup.streamType = static_cast( static_cast(xsdcAttributesGroup.getStreamType()) - kStreamTypeEnumOffset); aidlAttributesGroup.volumeGroupName = xsdcAttributesGroup.getVolumeGroup(); if (xsdcAttributesGroup.hasAttributes_optional()) { aidlAttributesGroup.attributes = - convertCollectionToAidlUnchecked( + VALUE_OR_FATAL((convertCollectionToAidl( xsdcAttributesGroup.getAttributes_optional(), std::bind(&EngineConfigXmlConverter::convertAudioAttributesToAidl, this, - std::placeholders::_1)); + std::placeholders::_1)))); } else if (xsdcAttributesGroup.hasContentType_optional() || xsdcAttributesGroup.hasUsage_optional() || xsdcAttributesGroup.hasSource_optional() || xsdcAttributesGroup.hasFlags_optional() || xsdcAttributesGroup.hasBundle_optional()) { - aidlAttributesGroup.attributes.push_back(convertAudioAttributesToAidl(xsd::AttributesType( - xsdcAttributesGroup.getContentType_optional(), - xsdcAttributesGroup.getUsage_optional(), xsdcAttributesGroup.getSource_optional(), - xsdcAttributesGroup.getFlags_optional(), xsdcAttributesGroup.getBundle_optional(), - std::nullopt))); + aidlAttributesGroup.attributes.push_back(VALUE_OR_FATAL(convertAudioAttributesToAidl( + eng_xsd::AttributesType(xsdcAttributesGroup.getContentType_optional(), + xsdcAttributesGroup.getUsage_optional(), + xsdcAttributesGroup.getSource_optional(), + xsdcAttributesGroup.getFlags_optional(), + xsdcAttributesGroup.getBundle_optional(), std::nullopt)))); } else { - // do nothing; - // TODO: check if this is valid or if we should treat as an error. - // Currently, attributes are not mandatory in schema, but an AttributesGroup - // without attributes does not make much sense. + LOG(ERROR) << __func__ << " Review Audio Policy config: no audio attributes provided for " + << aidlAttributesGroup.toString(); + return unexpected(BAD_VALUE); } return aidlAttributesGroup; } -AudioHalProductStrategy EngineConfigXmlConverter::convertProductStrategyToAidl( - const xsd::ProductStrategies::ProductStrategy& xsdcProductStrategy) { +ConversionResult EngineConfigXmlConverter::convertProductStrategyToAidl( + const eng_xsd::ProductStrategies::ProductStrategy& xsdcProductStrategy) { AudioHalProductStrategy aidlProductStrategy; - aidlProductStrategy.id = convertProductStrategyNameToAidl(xsdcProductStrategy.getName()); + aidlProductStrategy.id = + VALUE_OR_FATAL(convertProductStrategyNameToAidl(xsdcProductStrategy.getName())); if (xsdcProductStrategy.hasAttributesGroup()) { - aidlProductStrategy.attributesGroups = - convertCollectionToAidlUnchecked( + aidlProductStrategy.attributesGroups = VALUE_OR_FATAL( + (convertCollectionToAidl( xsdcProductStrategy.getAttributesGroup(), std::bind(&EngineConfigXmlConverter::convertAttributesGroupToAidl, this, - std::placeholders::_1)); + std::placeholders::_1)))); } if ((mDefaultProductStrategyId != std::nullopt) && (mDefaultProductStrategyId.value() == -1)) { mDefaultProductStrategyId = aidlProductStrategy.id; @@ -183,82 +180,42 @@ AudioHalProductStrategy EngineConfigXmlConverter::convertProductStrategyToAidl( return aidlProductStrategy; } -AudioHalVolumeCurve::CurvePoint EngineConfigXmlConverter::convertCurvePointToAidl( - const std::string& xsdcCurvePoint) { - AudioHalVolumeCurve::CurvePoint aidlCurvePoint{}; - if (sscanf(xsdcCurvePoint.c_str(), "%" SCNd8 ",%d", &aidlCurvePoint.index, - &aidlCurvePoint.attenuationMb) != 2) { - aidlCurvePoint.index = kInvalidCurvePointIndex; - } - return aidlCurvePoint; -} - -AudioHalVolumeCurve EngineConfigXmlConverter::convertVolumeCurveToAidl( - const xsd::Volume& xsdcVolumeCurve) { +ConversionResult EngineConfigXmlConverter::convertVolumeCurveToAidl( + const eng_xsd::Volume& xsdcVolumeCurve) { AudioHalVolumeCurve aidlVolumeCurve; aidlVolumeCurve.deviceCategory = static_cast(xsdcVolumeCurve.getDeviceCategory()); if (xsdcVolumeCurve.hasRef()) { if (mVolumesReferenceMap.empty()) { - mVolumesReferenceMap = generateReferenceMap( + mVolumesReferenceMap = generateReferenceMap( getXsdcConfig()->getVolumes()); } - aidlVolumeCurve.curvePoints = - convertCollectionToAidlUnchecked( + aidlVolumeCurve.curvePoints = VALUE_OR_FATAL( + (convertCollectionToAidl( mVolumesReferenceMap.at(xsdcVolumeCurve.getRef()).getPoint(), - std::bind(&EngineConfigXmlConverter::convertCurvePointToAidl, this, - std::placeholders::_1)); + &convertCurvePointToAidl))); } else { - aidlVolumeCurve.curvePoints = - convertCollectionToAidlUnchecked( - xsdcVolumeCurve.getPoint(), - std::bind(&EngineConfigXmlConverter::convertCurvePointToAidl, this, - std::placeholders::_1)); + aidlVolumeCurve.curvePoints = VALUE_OR_FATAL( + (convertCollectionToAidl( + xsdcVolumeCurve.getPoint(), &convertCurvePointToAidl))); } return aidlVolumeCurve; } -AudioHalVolumeGroup EngineConfigXmlConverter::convertVolumeGroupToAidl( - const xsd::VolumeGroupsType::VolumeGroup& xsdcVolumeGroup) { +ConversionResult EngineConfigXmlConverter::convertVolumeGroupToAidl( + const eng_xsd::VolumeGroupsType::VolumeGroup& xsdcVolumeGroup) { AudioHalVolumeGroup aidlVolumeGroup; aidlVolumeGroup.name = xsdcVolumeGroup.getName(); aidlVolumeGroup.minIndex = xsdcVolumeGroup.getIndexMin(); aidlVolumeGroup.maxIndex = xsdcVolumeGroup.getIndexMax(); aidlVolumeGroup.volumeCurves = - convertCollectionToAidlUnchecked( + VALUE_OR_FATAL((convertCollectionToAidl( xsdcVolumeGroup.getVolume(), std::bind(&EngineConfigXmlConverter::convertVolumeCurveToAidl, this, - std::placeholders::_1)); + std::placeholders::_1)))); return aidlVolumeGroup; } -AudioHalCapCriterion EngineConfigXmlConverter::convertCapCriterionToAidl( - const xsd::CriterionType& xsdcCriterion) { - AudioHalCapCriterion aidlCapCriterion; - aidlCapCriterion.name = xsdcCriterion.getName(); - aidlCapCriterion.criterionTypeName = xsdcCriterion.getType(); - aidlCapCriterion.defaultLiteralValue = xsdcCriterion.get_default(); - return aidlCapCriterion; -} - -std::string EngineConfigXmlConverter::convertCriterionTypeValueToAidl( - const xsd::ValueType& xsdcCriterionTypeValue) { - return xsdcCriterionTypeValue.getLiteral(); -} - -AudioHalCapCriterionType EngineConfigXmlConverter::convertCapCriterionTypeToAidl( - const xsd::CriterionTypeType& xsdcCriterionType) { - AudioHalCapCriterionType aidlCapCriterionType; - aidlCapCriterionType.name = xsdcCriterionType.getName(); - aidlCapCriterionType.isInclusive = !(static_cast(xsdcCriterionType.getType())); - aidlCapCriterionType.values = - convertWrappedCollectionToAidlUnchecked( - xsdcCriterionType.getValues(), &xsd::ValuesType::getValue, - std::bind(&EngineConfigXmlConverter::convertCriterionTypeValueToAidl, this, - std::placeholders::_1)); - return aidlCapCriterionType; -} - AudioHalEngineConfig& EngineConfigXmlConverter::getAidlEngineConfig() { return mAidlEngineConfig; } @@ -266,39 +223,42 @@ AudioHalEngineConfig& EngineConfigXmlConverter::getAidlEngineConfig() { void EngineConfigXmlConverter::init() { initProductStrategyMap(); if (getXsdcConfig()->hasProductStrategies()) { - mAidlEngineConfig.productStrategies = - convertWrappedCollectionToAidlUnchecked( + mAidlEngineConfig.productStrategies = VALUE_OR_FATAL( + (convertWrappedCollectionToAidl( getXsdcConfig()->getProductStrategies(), - &xsd::ProductStrategies::getProductStrategy, + &eng_xsd::ProductStrategies::getProductStrategy, std::bind(&EngineConfigXmlConverter::convertProductStrategyToAidl, this, - std::placeholders::_1)); + std::placeholders::_1)))); if (mDefaultProductStrategyId) { mAidlEngineConfig.defaultProductStrategyId = mDefaultProductStrategyId.value(); } } if (getXsdcConfig()->hasVolumeGroups()) { - mAidlEngineConfig.volumeGroups = convertWrappedCollectionToAidlUnchecked< - xsd::VolumeGroupsType, xsd::VolumeGroupsType::VolumeGroup, AudioHalVolumeGroup>( - getXsdcConfig()->getVolumeGroups(), &xsd::VolumeGroupsType::getVolumeGroup, - std::bind(&EngineConfigXmlConverter::convertVolumeGroupToAidl, this, - std::placeholders::_1)); + mAidlEngineConfig.volumeGroups = VALUE_OR_FATAL( + (convertWrappedCollectionToAidl( + getXsdcConfig()->getVolumeGroups(), + &eng_xsd::VolumeGroupsType::getVolumeGroup, + std::bind(&EngineConfigXmlConverter::convertVolumeGroupToAidl, this, + std::placeholders::_1)))); } if (getXsdcConfig()->hasCriteria() && getXsdcConfig()->hasCriterion_types()) { AudioHalEngineConfig::CapSpecificConfig capSpecificConfig; - capSpecificConfig.criteria = - convertWrappedCollectionToAidlUnchecked( - getXsdcConfig()->getCriteria(), &xsd::CriteriaType::getCriterion, - std::bind(&EngineConfigXmlConverter::convertCapCriterionToAidl, this, - std::placeholders::_1)); - capSpecificConfig.criterionTypes = convertWrappedCollectionToAidlUnchecked< - xsd::CriterionTypesType, xsd::CriterionTypeType, AudioHalCapCriterionType>( - getXsdcConfig()->getCriterion_types(), &xsd::CriterionTypesType::getCriterion_type, - std::bind(&EngineConfigXmlConverter::convertCapCriterionTypeToAidl, this, - std::placeholders::_1)); - mAidlEngineConfig.capSpecificConfig = capSpecificConfig; + capSpecificConfig.criteria = VALUE_OR_FATAL( + (convertWrappedCollectionToAidl( + getXsdcConfig()->getCriteria(), &eng_xsd::CriteriaType::getCriterion, + &convertCapCriterionToAidl))); + capSpecificConfig.criterionTypes = + VALUE_OR_FATAL((convertWrappedCollectionToAidl( + getXsdcConfig()->getCriterion_types(), + &eng_xsd::CriterionTypesType::getCriterion_type, + &convertCapCriterionTypeToAidl))); } } } // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp index b7761bf244ce566919176426fc6c7b96b91dac06..d0e8745d78ba3b60191e82dc23c832a7baa18bea 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -18,13 +18,13 @@ #include #define LOG_TAG "AHAL_Module" -#include #include #include #include #include #include +#include "core-impl/Configuration.h" #include "core-impl/Module.h" #include "core-impl/ModuleBluetooth.h" #include "core-impl/ModulePrimary.h" @@ -34,6 +34,7 @@ #include "core-impl/SoundDose.h" #include "core-impl/utils.h" +using aidl::android::hardware::audio::common::frameCountFromDurationMs; using aidl::android::hardware::audio::common::getFrameSizeInBytes; using aidl::android::hardware::audio::common::isBitPositionFlagSet; using aidl::android::hardware::audio::common::isValidAudioMode; @@ -42,6 +43,7 @@ using aidl::android::hardware::audio::common::SourceMetadata; using aidl::android::hardware::audio::core::sounddose::ISoundDose; using aidl::android::media::audio::common::AudioChannelLayout; using aidl::android::media::audio::common::AudioDevice; +using aidl::android::media::audio::common::AudioDeviceType; using aidl::android::media::audio::common::AudioFormatDescription; using aidl::android::media::audio::common::AudioFormatType; using aidl::android::media::audio::common::AudioInputFlags; @@ -65,32 +67,56 @@ namespace aidl::android::hardware::audio::core { namespace { +inline bool hasDynamicChannelMasks(const std::vector& channelMasks) { + return channelMasks.empty() || + std::all_of(channelMasks.begin(), channelMasks.end(), + [](const auto& channelMask) { return channelMask == AudioChannelLayout{}; }); +} + +inline bool hasDynamicFormat(const AudioFormatDescription& format) { + return format == AudioFormatDescription{}; +} + +inline bool hasDynamicSampleRates(const std::vector& sampleRates) { + return sampleRates.empty() || + std::all_of(sampleRates.begin(), sampleRates.end(), + [](const auto& sampleRate) { return sampleRate == 0; }); +} + +inline bool isDynamicProfile(const AudioProfile& profile) { + return hasDynamicFormat(profile.format) || hasDynamicChannelMasks(profile.channelMasks) || + hasDynamicSampleRates(profile.sampleRates); +} + +bool hasDynamicProfilesOnly(const std::vector& profiles) { + if (profiles.empty()) return true; + return std::all_of(profiles.begin(), profiles.end(), isDynamicProfile); +} + +// Note: does not assign an ID to the config. bool generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) { + const bool allowDynamicConfig = port.ext.getTag() == AudioPortExt::device; *config = {}; config->portId = port.id; - if (port.profiles.empty()) { - LOG(ERROR) << __func__ << ": port " << port.id << " has no profiles"; - return false; - } - const auto& profile = port.profiles.begin(); - config->format = profile->format; - if (profile->channelMasks.empty()) { - LOG(ERROR) << __func__ << ": the first profile in port " << port.id - << " has no channel masks"; - return false; + for (const auto& profile : port.profiles) { + if (isDynamicProfile(profile)) continue; + config->format = profile.format; + config->channelMask = *profile.channelMasks.begin(); + config->sampleRate = Int{.value = *profile.sampleRates.begin()}; + config->flags = port.flags; + config->ext = port.ext; + return true; } - config->channelMask = *profile->channelMasks.begin(); - if (profile->sampleRates.empty()) { - LOG(ERROR) << __func__ << ": the first profile in port " << port.id - << " has no sample rates"; - return false; + if (allowDynamicConfig) { + config->format = AudioFormatDescription{}; + config->channelMask = AudioChannelLayout{}; + config->sampleRate = Int{.value = 0}; + config->flags = port.flags; + config->ext = port.ext; + return true; } - Int sampleRate; - sampleRate.value = *profile->sampleRates.begin(); - config->sampleRate = sampleRate; - config->flags = port.flags; - config->ext = port.ext; - return true; + LOG(ERROR) << __func__ << ": port " << port.id << " only has dynamic profiles"; + return false; } bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& format, @@ -108,21 +134,36 @@ bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& forma } // namespace // static -std::shared_ptr Module::createInstance(Type type) { +std::shared_ptr Module::createInstance(Type type, std::unique_ptr&& config) { switch (type) { case Type::DEFAULT: - return ndk::SharedRefBase::make(); + return ndk::SharedRefBase::make(std::move(config)); case Type::R_SUBMIX: - return ndk::SharedRefBase::make(); + return ndk::SharedRefBase::make(std::move(config)); case Type::STUB: - return ndk::SharedRefBase::make(); + return ndk::SharedRefBase::make(std::move(config)); case Type::USB: - return ndk::SharedRefBase::make(); + return ndk::SharedRefBase::make(std::move(config)); case Type::BLUETOOTH: - return ndk::SharedRefBase::make(); + return ndk::SharedRefBase::make(std::move(config)); } } +// static +std::optional Module::typeFromString(const std::string& type) { + if (type == "default") + return Module::Type::DEFAULT; + else if (type == "r_submix") + return Module::Type::R_SUBMIX; + else if (type == "stub") + return Module::Type::STUB; + else if (type == "usb") + return Module::Type::USB; + else if (type == "bluetooth") + return Module::Type::BLUETOOTH; + return {}; +} + std::ostream& operator<<(std::ostream& os, Module::Type t) { switch (t) { case Module::Type::DEFAULT: @@ -144,6 +185,11 @@ std::ostream& operator<<(std::ostream& os, Module::Type t) { return os; } +Module::Module(Type type, std::unique_ptr&& config) + : mType(type), mConfig(std::move(config)) { + populateConnectedProfiles(); +} + void Module::cleanUpPatch(int32_t patchId) { erase_all_values(mPatches, std::set{patchId}); } @@ -156,15 +202,17 @@ ndk::ScopedAStatus Module::createStreamContext( LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - if (in_bufferSizeFrames < kMinimumStreamBufferSizeFrames) { - LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames - << ", must be at least " << kMinimumStreamBufferSizeFrames; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } auto& configs = getConfig().portConfigs; auto portConfigIt = findById(configs, in_portConfigId); // Since this is a private method, it is assumed that // validity of the portConfigId has already been checked. + const int32_t minimumStreamBufferSizeFrames = calculateBufferSizeFrames( + getNominalLatencyMs(*portConfigIt), portConfigIt->sampleRate.value().value); + if (in_bufferSizeFrames < minimumStreamBufferSizeFrames) { + LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames + << ", must be at least " << minimumStreamBufferSizeFrames; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } const size_t frameSize = getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value()); if (frameSize == 0) { @@ -189,14 +237,19 @@ ndk::ScopedAStatus Module::createStreamContext( StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs, mVendorDebug.forceTransientBurst, mVendorDebug.forceSynchronousDrain}; + std::shared_ptr soundDose; + if (!getSoundDose(&soundDose).isOk()) { + LOG(ERROR) << __func__ << ": could not create sound dose instance"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } StreamContext temp( std::make_unique(1, true /*configureEventFlagWord*/), std::make_unique(1, true /*configureEventFlagWord*/), - portConfigIt->portId, portConfigIt->format.value(), - portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags, + portConfigIt->format.value(), portConfigIt->channelMask.value(), + portConfigIt->sampleRate.value().value, flags, getNominalLatencyMs(*portConfigIt), portConfigIt->ext.get().handle, std::make_unique(frameSize * in_bufferSizeFrames), - asyncCallback, outEventCallback, params); + asyncCallback, outEventCallback, mSoundDose.getInstance(), params); if (temp.isValid()) { *out_context = std::move(temp); } else { @@ -279,6 +332,22 @@ ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, Audio return ndk::ScopedAStatus::ok(); } +void Module::populateConnectedProfiles() { + Configuration& config = getConfig(); + for (const AudioPort& port : config.ports) { + if (port.ext.getTag() == AudioPortExt::device) { + if (auto devicePort = port.ext.get(); + !devicePort.device.type.connection.empty() && port.profiles.empty()) { + if (auto connIt = config.connectedProfiles.find(port.id); + connIt == config.connectedProfiles.end()) { + config.connectedProfiles.emplace( + port.id, internal::getStandard16And24BitPcmAudioProfiles()); + } + } + } + } +} + template std::set Module::portIdsFromPortConfigIds(C portConfigIds) { std::set result; @@ -292,35 +361,53 @@ std::set Module::portIdsFromPortConfigIds(C portConfigIds) { return result; } -std::unique_ptr Module::initializeConfig() { - std::unique_ptr config; - switch (getType()) { - case Type::DEFAULT: - config = std::move(internal::getPrimaryConfiguration()); - break; - case Type::R_SUBMIX: - config = std::move(internal::getRSubmixConfiguration()); - break; - case Type::STUB: - config = std::move(internal::getStubConfiguration()); - break; - case Type::USB: - config = std::move(internal::getUsbConfiguration()); - break; - case Type::BLUETOOTH: - config = std::move(internal::getBluetoothConfiguration()); - break; +std::unique_ptr Module::initializeConfig() { + return internal::getConfiguration(getType()); +} + +int32_t Module::getNominalLatencyMs(const AudioPortConfig&) { + // Arbitrary value. Implementations must override this method to provide their actual latency. + static constexpr int32_t kLatencyMs = 5; + return kLatencyMs; +} + +std::vector Module::getAudioRoutesForAudioPortImpl(int32_t portId) { + std::vector result; + auto& routes = getConfig().routes; + for (auto& r : routes) { + const auto& srcs = r.sourcePortIds; + if (r.sinkPortId == portId || std::find(srcs.begin(), srcs.end(), portId) != srcs.end()) { + result.push_back(&r); + } } - return config; + return result; } -internal::Configuration& Module::getConfig() { +Module::Configuration& Module::getConfig() { if (!mConfig) { mConfig = std::move(initializeConfig()); } return *mConfig; } +std::set Module::getRoutableAudioPortIds(int32_t portId, + std::vector* routes) { + std::vector routesStorage; + if (routes == nullptr) { + routesStorage = getAudioRoutesForAudioPortImpl(portId); + routes = &routesStorage; + } + std::set result; + for (AudioRoute* r : *routes) { + if (r->sinkPortId == portId) { + result.insert(r->sourcePortIds.begin(), r->sourcePortIds.end()); + } else { + result.insert(r->sinkPortId); + } + } + return result; +} + void Module::registerPatch(const AudioPatch& patch) { auto& configs = getConfig().portConfigs; auto do_insert = [&](const std::vector& portConfigIds) { @@ -340,21 +427,43 @@ void Module::registerPatch(const AudioPatch& patch) { ndk::ScopedAStatus Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch) { - // Streams from the old patch need to be disconnected, streams from the new - // patch need to be connected. If the stream belongs to both patches, no need - // to update it. + // Notify streams about the new set of devices they are connected to. auto maybeFailure = ndk::ScopedAStatus::ok(); - std::set idsToDisconnect, idsToConnect, idsToDisconnectOnFailure; - idsToDisconnect.insert(oldPatch.sourcePortConfigIds.begin(), - oldPatch.sourcePortConfigIds.end()); - idsToDisconnect.insert(oldPatch.sinkPortConfigIds.begin(), oldPatch.sinkPortConfigIds.end()); - idsToConnect.insert(newPatch.sourcePortConfigIds.begin(), newPatch.sourcePortConfigIds.end()); - idsToConnect.insert(newPatch.sinkPortConfigIds.begin(), newPatch.sinkPortConfigIds.end()); - std::for_each(idsToDisconnect.begin(), idsToDisconnect.end(), [&](const auto& portConfigId) { - if (idsToConnect.count(portConfigId) == 0 && mStreams.count(portConfigId) != 0) { - if (auto status = mStreams.setStreamConnectedDevices(portConfigId, {}); status.isOk()) { + using Connections = + std::map>; + Connections oldConnections, newConnections; + auto fillConnectionsHelper = [&](Connections& connections, + const std::vector& mixPortCfgIds, + const std::vector& devicePortCfgIds) { + for (int32_t mixPortCfgId : mixPortCfgIds) { + connections[mixPortCfgId].insert(devicePortCfgIds.begin(), devicePortCfgIds.end()); + } + }; + auto fillConnections = [&](Connections& connections, const AudioPatch& patch) { + if (std::find_if(patch.sourcePortConfigIds.begin(), patch.sourcePortConfigIds.end(), + [&](int32_t portConfigId) { return mStreams.count(portConfigId) > 0; }) != + patch.sourcePortConfigIds.end()) { + // Sources are mix ports. + fillConnectionsHelper(connections, patch.sourcePortConfigIds, patch.sinkPortConfigIds); + } else if (std::find_if(patch.sinkPortConfigIds.begin(), patch.sinkPortConfigIds.end(), + [&](int32_t portConfigId) { + return mStreams.count(portConfigId) > 0; + }) != patch.sinkPortConfigIds.end()) { + // Sources are device ports. + fillConnectionsHelper(connections, patch.sinkPortConfigIds, patch.sourcePortConfigIds); + } // Otherwise, there are no streams to notify. + }; + fillConnections(oldConnections, oldPatch); + fillConnections(newConnections, newPatch); + + std::for_each(oldConnections.begin(), oldConnections.end(), [&](const auto& connectionPair) { + const int32_t mixPortConfigId = connectionPair.first; + if (auto it = newConnections.find(mixPortConfigId); + it == newConnections.end() || it->second != connectionPair.second) { + if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, {}); + status.isOk()) { LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id " - << portConfigId << " has been disconnected"; + << mixPortConfigId << " has been disconnected"; } else { // Disconnection is tricky to roll back, just register a failure. maybeFailure = std::move(status); @@ -362,23 +471,26 @@ ndk::ScopedAStatus Module::updateStreamsConnectedState(const AudioPatch& oldPatc } }); if (!maybeFailure.isOk()) return maybeFailure; - std::for_each(idsToConnect.begin(), idsToConnect.end(), [&](const auto& portConfigId) { - if (idsToDisconnect.count(portConfigId) == 0 && mStreams.count(portConfigId) != 0) { - const auto connectedDevices = findConnectedDevices(portConfigId); + std::set idsToDisconnectOnFailure; + std::for_each(newConnections.begin(), newConnections.end(), [&](const auto& connectionPair) { + const int32_t mixPortConfigId = connectionPair.first; + if (auto it = oldConnections.find(mixPortConfigId); + it == oldConnections.end() || it->second != connectionPair.second) { + const auto connectedDevices = findConnectedDevices(mixPortConfigId); if (connectedDevices.empty()) { // This is important as workers use the vector size to derive the connection status. LOG(FATAL) << "updateStreamsConnectedState: No connected devices found for port " "config id " - << portConfigId; + << mixPortConfigId; } - if (auto status = mStreams.setStreamConnectedDevices(portConfigId, connectedDevices); + if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, connectedDevices); status.isOk()) { LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id " - << portConfigId << " has been connected to: " + << mixPortConfigId << " has been connected to: " << ::android::internal::ToString(connectedDevices); } else { maybeFailure = std::move(status); - idsToDisconnectOnFailure.insert(portConfigId); + idsToDisconnectOnFailure.insert(mixPortConfigId); } } }); @@ -485,74 +597,99 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA } } - if (connectedPort.profiles.empty()) { - if (!mDebug.simulateDeviceConnections) { - RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort)); - } else { - auto& connectedProfiles = getConfig().connectedProfiles; - if (auto connectedProfilesIt = connectedProfiles.find(templateId); - connectedProfilesIt != connectedProfiles.end()) { - connectedPort.profiles = connectedProfilesIt->second; - } - } - if (connectedPort.profiles.empty()) { - LOG(ERROR) << __func__ - << ": profiles of a connected port still empty after connecting external " - "device " - << connectedPort.toString(); - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + // Two main cases are considered with regard to the profiles of the connected device port: + // + // 1. If the template device port has dynamic profiles, and at least one routable mix + // port also has dynamic profiles, it means that after connecting the device, the + // connected device port must have profiles populated with actual capabilities of + // the connected device, and dynamic of routable mix ports will be filled + // according to these capabilities. An example of this case is connection of an + // HDMI or USB device. For USB handled by ADSP, there can be mix ports with static + // profiles, and one dedicated mix port for "hi-fi" playback. The latter is left with + // dynamic profiles so that they can be populated with actual capabilities of + // the connected device. + // + // 2. If the template device port has dynamic profiles, while all routable mix ports + // have static profiles, it means that after connecting the device, the connected + // device port can be left with dynamic profiles, and profiles of mix ports are + // left untouched. An example of this case is connection of an analog wired + // headset, it should be treated in the same way as a speaker. + // + // Yet another possible case is when both the template device port and all routable + // mix ports have static profiles. This is allowed and handled correctly, however, it + // is not very practical, since these profiles are likely duplicates of each other. + + std::vector routesToMixPorts = getAudioRoutesForAudioPortImpl(templateId); + std::set routableMixPortIds = getRoutableAudioPortIds(templateId, &routesToMixPorts); + if (!mDebug.simulateDeviceConnections) { + // Even if the device port has static profiles, the HAL module might need to update + // them, or abort the connection process. + RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort)); + } else if (hasDynamicProfilesOnly(connectedPort.profiles)) { + auto& connectedProfiles = getConfig().connectedProfiles; + if (auto connectedProfilesIt = connectedProfiles.find(templateId); + connectedProfilesIt != connectedProfiles.end()) { + connectedPort.profiles = connectedProfilesIt->second; } } - - for (auto profile : connectedPort.profiles) { - if (profile.channelMasks.empty()) { - LOG(ERROR) << __func__ << ": the profile " << profile.name << " has no channel masks"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - if (profile.sampleRates.empty()) { - LOG(ERROR) << __func__ << ": the profile " << profile.name << " has no sample rates"; + if (hasDynamicProfilesOnly(connectedPort.profiles)) { + // Possible case 2. Check if all routable mix ports have static profiles. + if (auto dynamicMixPortIt = std::find_if(ports.begin(), ports.end(), + [&routableMixPortIds](const auto& p) { + return routableMixPortIds.count(p.id) > 0 && + hasDynamicProfilesOnly(p.profiles); + }); + dynamicMixPortIt != ports.end()) { + LOG(ERROR) << __func__ << ": connected port only has dynamic profiles after connecting " + << "external device " << connectedPort.toString() << ", and there exist " + << "a routable mix port with dynamic profiles: " + << dynamicMixPortIt->toString(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } - connectedPort.id = ++getConfig().nextPortId; + connectedPort.id = getConfig().nextPortId++; auto [connectedPortsIt, _] = - mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector())); + mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::set())); LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, " << "connected port ID " << connectedPort.id; ports.push_back(connectedPort); onExternalDeviceConnectionChanged(connectedPort, true /*connected*/); - std::vector routablePortIds; + // For routes where the template port is a source, add the connected port to sources, + // otherwise, create a new route by copying from the route for the template port. std::vector newRoutes; - auto& routes = getConfig().routes; - for (auto& r : routes) { - if (r.sinkPortId == templateId) { - AudioRoute newRoute; - newRoute.sourcePortIds = r.sourcePortIds; - newRoute.sinkPortId = connectedPort.id; - newRoute.isExclusive = r.isExclusive; - newRoutes.push_back(std::move(newRoute)); - routablePortIds.insert(routablePortIds.end(), r.sourcePortIds.begin(), - r.sourcePortIds.end()); + for (AudioRoute* r : routesToMixPorts) { + if (r->sinkPortId == templateId) { + newRoutes.push_back(AudioRoute{.sourcePortIds = r->sourcePortIds, + .sinkPortId = connectedPort.id, + .isExclusive = r->isExclusive}); } else { - auto& srcs = r.sourcePortIds; - if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) { - srcs.push_back(connectedPort.id); - routablePortIds.push_back(r.sinkPortId); - } + r->sourcePortIds.push_back(connectedPort.id); } } + auto& routes = getConfig().routes; routes.insert(routes.end(), newRoutes.begin(), newRoutes.end()); - // Note: this is a simplistic approach assuming that a mix port can only be populated - // from a single device port. Implementing support for stuffing dynamic profiles with a superset - // of all profiles from all routable dynamic device ports would be more involved. - for (const auto mixPortId : routablePortIds) { - auto portsIt = findById(ports, mixPortId); - if (portsIt != ports.end() && portsIt->profiles.empty()) { - portsIt->profiles = connectedPort.profiles; - connectedPortsIt->second.push_back(portsIt->id); + if (!hasDynamicProfilesOnly(connectedPort.profiles) && !routableMixPortIds.empty()) { + // Note: this is a simplistic approach assuming that a mix port can only be populated + // from a single device port. Implementing support for stuffing dynamic profiles with + // a superset of all profiles from all routable dynamic device ports would be more involved. + for (auto& port : ports) { + if (routableMixPortIds.count(port.id) == 0) continue; + if (hasDynamicProfilesOnly(port.profiles)) { + port.profiles = connectedPort.profiles; + connectedPortsIt->second.insert(port.id); + } else { + // Check if profiles are not all dynamic because they were populated by + // a previous connection. Otherwise, it means that they are actually static. + for (const auto& cp : mConnectedDevicePorts) { + if (cp.second.count(port.id) > 0) { + connectedPortsIt->second.insert(port.id); + break; + } + } + } } } *_aidl_return = std::move(connectedPort); @@ -607,13 +744,42 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) { } } - for (const auto mixPortId : connectedPortsIt->second) { + // Clear profiles for mix ports that are not connected to any other ports. + std::set mixPortsToClear = std::move(connectedPortsIt->second); + mConnectedDevicePorts.erase(connectedPortsIt); + for (const auto& connectedPort : mConnectedDevicePorts) { + for (int32_t mixPortId : connectedPort.second) { + mixPortsToClear.erase(mixPortId); + } + } + for (int32_t mixPortId : mixPortsToClear) { auto mixPortIt = findById(ports, mixPortId); if (mixPortIt != ports.end()) { mixPortIt->profiles = {}; } } - mConnectedDevicePorts.erase(connectedPortsIt); + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::prepareToDisconnectExternalDevice(int32_t in_portId) { + auto& ports = getConfig().ports; + auto portIt = findById(ports, in_portId); + if (portIt == ports.end()) { + LOG(ERROR) << __func__ << ": port id " << in_portId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (portIt->ext.getTag() != AudioPortExt::Tag::device) { + LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + auto connectedPortsIt = mConnectedDevicePorts.find(in_portId); + if (connectedPortsIt == mConnectedDevicePorts.end()) { + LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + onPrepareToDisconnectExternalDevice(*portIt); return ndk::ScopedAStatus::ok(); } @@ -661,13 +827,9 @@ ndk::ScopedAStatus Module::getAudioRoutesForAudioPort(int32_t in_portId, LOG(ERROR) << __func__ << ": port id " << in_portId << " not found"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - auto& routes = getConfig().routes; - std::copy_if(routes.begin(), routes.end(), std::back_inserter(*_aidl_return), - [&](const auto& r) { - const auto& srcs = r.sourcePortIds; - return r.sinkPortId == in_portId || - std::find(srcs.begin(), srcs.end(), in_portId) != srcs.end(); - }); + std::vector routes = getAudioRoutesForAudioPortImpl(in_portId); + std::transform(routes.begin(), routes.end(), std::back_inserter(*_aidl_return), + [](auto rptr) { return *rptr; }); return ndk::ScopedAStatus::ok(); } @@ -688,7 +850,7 @@ ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_ar context.fillDescriptor(&_aidl_return->desc); std::shared_ptr stream; RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata, - mConfig->microphones, &stream)); + getMicrophoneInfos(), &stream)); StreamWrapper streamWrapper(stream); if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) { RETURN_STATUS_IF_ERROR( @@ -835,17 +997,28 @@ ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPa return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } + // Find the highest sample rate among mix port configs. + std::map sampleRates; + std::vector& mixPortConfigs = + sources[0]->ext.getTag() == AudioPortExt::mix ? sources : sinks; + for (auto mix : mixPortConfigs) { + sampleRates.emplace(mix->sampleRate.value().value, mix); + } *_aidl_return = in_requested; - _aidl_return->minimumStreamBufferSizeFrames = kMinimumStreamBufferSizeFrames; + auto maxSampleRateIt = std::max_element(sampleRates.begin(), sampleRates.end()); + const int32_t latencyMs = getNominalLatencyMs(*(maxSampleRateIt->second)); + _aidl_return->minimumStreamBufferSizeFrames = + calculateBufferSizeFrames(latencyMs, maxSampleRateIt->first); _aidl_return->latenciesMs.clear(); _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(), - _aidl_return->sinkPortConfigIds.size(), kLatencyMs); + _aidl_return->sinkPortConfigIds.size(), latencyMs); AudioPatch oldPatch{}; if (existing == patches.end()) { _aidl_return->id = getConfig().nextPatchId++; patches.push_back(*_aidl_return); } else { oldPatch = *existing; + *existing = *_aidl_return; } patchesBackup = mPatches; registerPatch(*_aidl_return); @@ -880,13 +1053,14 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste const int portId = existing != configs.end() ? existing->portId : in_requested.portId; if (portId == 0) { - LOG(ERROR) << __func__ << ": input port config does not specify portId"; + LOG(ERROR) << __func__ << ": requested port config does not specify portId"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } auto& ports = getConfig().ports; auto portIt = findById(ports, portId); if (portIt == ports.end()) { - LOG(ERROR) << __func__ << ": input port config points to non-existent portId " << portId; + LOG(ERROR) << __func__ << ": requested port config points to non-existent portId " + << portId; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (existing != configs.end()) { @@ -904,6 +1078,10 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste // or a new generated config. Now attempt to update it according to the specified // fields of 'in_requested'. + // Device ports with only dynamic profiles are used for devices that are connected via ADSP, + // which takes care of their actual configuration automatically. + const bool allowDynamicConfig = portIt->ext.getTag() == AudioPortExt::device && + hasDynamicProfilesOnly(portIt->profiles); bool requestedIsValid = true, requestedIsFullySpecified = true; AudioIoFlags portFlags = portIt->flags; @@ -921,17 +1099,19 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste AudioProfile portProfile; if (in_requested.format.has_value()) { const auto& format = in_requested.format.value(); - if (findAudioProfile(*portIt, format, &portProfile)) { + if ((format == AudioFormatDescription{} && allowDynamicConfig) || + findAudioProfile(*portIt, format, &portProfile)) { out_suggested->format = format; } else { LOG(WARNING) << __func__ << ": requested format " << format.toString() - << " is not found in port's " << portId << " profiles"; + << " is not found in the profiles of port " << portId; requestedIsValid = false; } } else { requestedIsFullySpecified = false; } - if (!findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) { + if (!(out_suggested->format.value() == AudioFormatDescription{} && allowDynamicConfig) && + !findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) { LOG(ERROR) << __func__ << ": port " << portId << " does not support format " << out_suggested->format.value().toString() << " anymore"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); @@ -939,8 +1119,9 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste if (in_requested.channelMask.has_value()) { const auto& channelMask = in_requested.channelMask.value(); - if (find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) != - portProfile.channelMasks.end()) { + if ((channelMask == AudioChannelLayout{} && allowDynamicConfig) || + find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) != + portProfile.channelMasks.end()) { out_suggested->channelMask = channelMask; } else { LOG(WARNING) << __func__ << ": requested channel mask " << channelMask.toString() @@ -954,7 +1135,8 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste if (in_requested.sampleRate.has_value()) { const auto& sampleRate = in_requested.sampleRate.value(); - if (find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(), + if ((sampleRate.value == 0 && allowDynamicConfig) || + find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(), sampleRate.value) != portProfile.sampleRates.end()) { out_suggested->sampleRate = sampleRate; } else { @@ -1071,7 +1253,7 @@ ndk::ScopedAStatus Module::setMasterMute(bool in_mute) { // Reset master mute if it failed. onMasterMuteChanged(mMasterMute); } - return std::move(result); + return result; } ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) { @@ -1093,7 +1275,7 @@ ndk::ScopedAStatus Module::setMasterVolume(float in_volume) { << "), error=" << result; onMasterVolumeChanged(mMasterVolume); } - return std::move(result); + return result; } LOG(ERROR) << __func__ << ": invalid master volume value: " << in_volume; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); @@ -1112,7 +1294,7 @@ ndk::ScopedAStatus Module::setMicMute(bool in_mute) { } ndk::ScopedAStatus Module::getMicrophones(std::vector* _aidl_return) { - *_aidl_return = getConfig().microphones; + *_aidl_return = getMicrophoneInfos(); LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return); return ndk::ScopedAStatus::ok(); } @@ -1258,6 +1440,12 @@ ndk::ScopedAStatus Module::getMmapPolicyInfos(AudioMMapPolicyType mmapPolicyType mmapSources.insert(port.id); } } + if (mmapSources.empty() && mmapSinks.empty()) { + AudioMMapPolicyInfo never; + never.mmapPolicy = AudioMMapPolicy::NEVER; + _aidl_return->push_back(never); + return ndk::ScopedAStatus::ok(); + } for (const auto& route : getConfig().routes) { if (mmapSinks.count(route.sinkPortId) != 0) { // The sink is a mix port, add the sources if they are device ports. @@ -1346,7 +1534,18 @@ bool Module::isMmapSupported() { return mIsMmapSupported.value(); } -ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) { +ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort) { + if (audioPort->ext.getTag() != AudioPortExt::device) { + LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + const auto& devicePort = audioPort->ext.get(); + if (!devicePort.device.type.connection.empty()) { + LOG(ERROR) << __func__ + << ": module implementation must override 'populateConnectedDevicePort' " + << "to handle connection of external devices."; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } LOG(VERBOSE) << __func__ << ": do nothing and return ok"; return ndk::ScopedAStatus::ok(); } @@ -1364,6 +1563,11 @@ void Module::onExternalDeviceConnectionChanged( LOG(DEBUG) << __func__ << ": do nothing and return"; } +void Module::onPrepareToDisconnectExternalDevice( + const ::aidl::android::media::audio::common::AudioPort& audioPort __unused) { + LOG(DEBUG) << __func__ << ": do nothing and return"; +} + ndk::ScopedAStatus Module::onMasterMuteChanged(bool mute __unused) { LOG(VERBOSE) << __func__ << ": do nothing and return ok"; return ndk::ScopedAStatus::ok(); @@ -1374,9 +1578,27 @@ ndk::ScopedAStatus Module::onMasterVolumeChanged(float volume __unused) { return ndk::ScopedAStatus::ok(); } -Module::BtProfileHandles Module::getBtProfileManagerHandles() { - return std::make_tuple(std::weak_ptr(), std::weak_ptr(), - std::weak_ptr()); +std::vector Module::getMicrophoneInfos() { + std::vector result; + Configuration& config = getConfig(); + for (const AudioPort& port : config.ports) { + if (port.ext.getTag() == AudioPortExt::Tag::device) { + const AudioDeviceType deviceType = + port.ext.get().device.type.type; + if (deviceType == AudioDeviceType::IN_MICROPHONE || + deviceType == AudioDeviceType::IN_MICROPHONE_BACK) { + // Placeholder values. Vendor implementations must populate MicrophoneInfo + // accordingly based on their physical microphone parameters. + result.push_back(MicrophoneInfo{ + .id = port.name, + .device = port.ext.get().device, + .group = 0, + .indexInTheGroup = 0, + }); + } + } + } + return result; } ndk::ScopedAStatus Module::bluetoothParametersUpdated() { diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp index 9919c7f6cc71f5ab0387477b7351c753a121698b..3da6d48398de31691e02f0cd7885c3104de579d3 100644 --- a/audio/aidl/default/ModulePrimary.cpp +++ b/audio/aidl/default/ModulePrimary.cpp @@ -58,4 +58,12 @@ ndk::ScopedAStatus ModulePrimary::createOutputStream( offloadInfo); } +int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig&) { + // 85 ms is chosen considering 4096 frames @ 48 kHz. This is the value which allows + // the virtual Android device implementation to pass CTS. Hardware implementations + // should have significantly lower latency. + static constexpr int32_t kLatencyMs = 85; + return kLatencyMs; +} + } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/SoundDose.cpp b/audio/aidl/default/SoundDose.cpp index f12ce5d806f7a18f3f2aae06653c29395ea17670..1c9e0813530f39119c85d986ca79371b7fe41a68 100644 --- a/audio/aidl/default/SoundDose.cpp +++ b/audio/aidl/default/SoundDose.cpp @@ -18,7 +18,15 @@ #include "core-impl/SoundDose.h" +#include #include +#include +#include + +using aidl::android::hardware::audio::core::sounddose::ISoundDose; +using aidl::android::media::audio::common::AudioDevice; +using aidl::android::media::audio::common::AudioDeviceDescription; +using aidl::android::media::audio::common::AudioFormatDescription; namespace aidl::android::hardware::audio::core::sounddose { @@ -28,11 +36,16 @@ ndk::ScopedAStatus SoundDose::setOutputRs2UpperBound(float in_rs2ValueDbA) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + ::android::audio_utils::lock_guard l(mMutex); mRs2Value = in_rs2ValueDbA; + if (mMelProcessor != nullptr) { + mMelProcessor->setOutputRs2UpperBound(in_rs2ValueDbA); + } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus SoundDose::getOutputRs2UpperBound(float* _aidl_return) { + ::android::audio_utils::lock_guard l(mMutex); *_aidl_return = mRs2Value; LOG(DEBUG) << __func__ << ": returning " << *_aidl_return; return ndk::ScopedAStatus::ok(); @@ -44,6 +57,8 @@ ndk::ScopedAStatus SoundDose::registerSoundDoseCallback( LOG(ERROR) << __func__ << ": Callback is nullptr"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + + ::android::audio_utils::lock_guard l(mCbMutex); if (mCallback != nullptr) { LOG(ERROR) << __func__ << ": Sound dose callback was already registered"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); @@ -51,7 +66,81 @@ ndk::ScopedAStatus SoundDose::registerSoundDoseCallback( mCallback = in_callback; LOG(DEBUG) << __func__ << ": Registered sound dose callback "; + return ndk::ScopedAStatus::ok(); } +void SoundDose::setAudioDevice(const AudioDevice& audioDevice) { + ::android::audio_utils::lock_guard l(mCbMutex); + mAudioDevice = audioDevice; +} + +void SoundDose::startDataProcessor(uint32_t sampleRate, uint32_t channelCount, + const AudioFormatDescription& aidlFormat) { + ::android::audio_utils::lock_guard l(mMutex); + const auto result = aidl2legacy_AudioFormatDescription_audio_format_t(aidlFormat); + const audio_format_t format = result.value_or(AUDIO_FORMAT_INVALID); + + if (mMelProcessor == nullptr) { + // we don't have the deviceId concept on the vendor side so just pass 0 + mMelProcessor = ::android::sp<::android::audio_utils::MelProcessor>::make( + sampleRate, channelCount, format, mMelCallback, /*deviceId=*/0, mRs2Value); + } else { + mMelProcessor->updateAudioFormat(sampleRate, channelCount, format); + } +} + +void SoundDose::process(const void* buffer, size_t bytes) { + ::android::audio_utils::lock_guard l(mMutex); + if (mMelProcessor != nullptr) { + mMelProcessor->process(buffer, bytes); + } +} + +void SoundDose::onNewMelValues(const std::vector& mels, size_t offset, size_t length, + audio_port_handle_t deviceId __attribute__((__unused__))) const { + ::android::audio_utils::lock_guard l(mCbMutex); + if (!mAudioDevice.has_value()) { + LOG(WARNING) << __func__ << ": New mel values without a registered device"; + return; + } + if (mCallback == nullptr) { + LOG(ERROR) << __func__ << ": New mel values without a registered callback"; + return; + } + + ISoundDose::IHalSoundDoseCallback::MelRecord melRecord; + melRecord.timestamp = nanoseconds_to_seconds(systemTime()); + melRecord.melValues = std::vector(mels.begin() + offset, mels.begin() + offset + length); + + mCallback->onNewMelValues(melRecord, mAudioDevice.value()); +} + +void SoundDose::MelCallback::onNewMelValues(const std::vector& mels, size_t offset, + size_t length, + audio_port_handle_t deviceId + __attribute__((__unused__))) const { + mSoundDose.onNewMelValues(mels, offset, length, deviceId); +} + +void SoundDose::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId + __attribute__((__unused__))) const { + ::android::audio_utils::lock_guard l(mCbMutex); + if (!mAudioDevice.has_value()) { + LOG(WARNING) << __func__ << ": Momentary exposure without a registered device"; + return; + } + if (mCallback == nullptr) { + LOG(ERROR) << __func__ << ": Momentary exposure without a registered callback"; + return; + } + + mCallback->onMomentaryExposureWarning(currentMel, mAudioDevice.value()); +} + +void SoundDose::MelCallback::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId + __attribute__((__unused__))) const { + mSoundDose.onMomentaryExposure(currentMel, deviceId); +} + } // namespace aidl::android::hardware::audio::core::sounddose diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp index f7298c0286776a3807191fc462d314d9f5c71645..a805b872cd6d08658bcc6f4e934933229d95d0fc 100644 --- a/audio/aidl/default/Stream.cpp +++ b/audio/aidl/default/Stream.cpp @@ -90,6 +90,14 @@ bool StreamContext::isValid() const { return true; } +void StreamContext::startStreamDataProcessor() { + auto streamDataProcessor = mStreamDataProcessor.lock(); + if (streamDataProcessor != nullptr) { + streamDataProcessor->startDataProcessor(mSampleRate, getChannelCount(mChannelLayout), + mFormat); + } +} + void StreamContext::reset() { mCommandMQ.reset(); mReplyMQ.reset(); @@ -130,7 +138,7 @@ void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply, reply->status = STATUS_OK; if (isConnected) { reply->observable.frames = mContext->getFrameCount(); - reply->observable.timeNs = ::android::elapsedRealtimeNano(); + reply->observable.timeNs = ::android::uptimeNanos(); if (auto status = mDriver->refinePosition(&reply->observable); status == ::android::OK) { return; } @@ -307,7 +315,7 @@ bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply const size_t frameSize = mContext->getFrameSize(); size_t actualFrameCount = 0; bool fatal = false; - int32_t latency = Module::kLatencyMs; + int32_t latency = mContext->getNominalLatencyMs(); if (isConnected) { if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize, &actualFrameCount, &latency); @@ -573,8 +581,8 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep const size_t readByteCount = dataMQ->availableToRead(); const size_t frameSize = mContext->getFrameSize(); bool fatal = false; - int32_t latency = Module::kLatencyMs; - if (bool success = readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) { + int32_t latency = mContext->getNominalLatencyMs(); + if (readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) { const bool isConnected = mIsConnected; LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ" << " succeeded; connected? " << isConnected; @@ -593,6 +601,10 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep fatal = true; LOG(ERROR) << __func__ << ": write failed: " << status; } + auto streamDataProcessor = mContext->getStreamDataProcessor().lock(); + if (streamDataProcessor != nullptr) { + streamDataProcessor->process(mDataBuffer.get(), actualFrameCount * frameSize); + } } else { if (mContext->getAsyncCallback() == nullptr) { usleep(3000); // Simulate blocking transfer delay. @@ -836,7 +848,7 @@ ndk::ScopedAStatus StreamIn::setHwGain(const std::vector& in_channelGains } StreamInHwGainHelper::StreamInHwGainHelper(const StreamContext* context) - : mChannelCount(getChannelCount(context->getChannelLayout())) {} + : mChannelCount(getChannelCount(context->getChannelLayout())), mHwGains(mChannelCount, 0.0f) {} ndk::ScopedAStatus StreamInHwGainHelper::getHwGainImpl(std::vector* _aidl_return) { *_aidl_return = mHwGains; @@ -967,7 +979,8 @@ ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int3 } StreamOutHwVolumeHelper::StreamOutHwVolumeHelper(const StreamContext* context) - : mChannelCount(getChannelCount(context->getChannelLayout())) {} + : mChannelCount(getChannelCount(context->getChannelLayout())), + mHwVolumes(mChannelCount, 0.0f) {} ndk::ScopedAStatus StreamOutHwVolumeHelper::getHwVolumeImpl(std::vector* _aidl_return) { *_aidl_return = mHwVolumes; diff --git a/audio/aidl/default/Telephony.cpp b/audio/aidl/default/Telephony.cpp index bf05a8da981a8f55a9d61ae7d9d3f0af5269c691..d9da39f0a8812efb9a965ef5fa872afd21792ddf 100644 --- a/audio/aidl/default/Telephony.cpp +++ b/audio/aidl/default/Telephony.cpp @@ -16,9 +16,9 @@ #define LOG_TAG "AHAL_Telephony" #include +#include #include -#include #include "core-impl/Telephony.h" diff --git a/audio/aidl/default/XsdcConversion.cpp b/audio/aidl/default/XsdcConversion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..172094911c969363da826c679afa2203a9384550 --- /dev/null +++ b/audio/aidl/default/XsdcConversion.cpp @@ -0,0 +1,452 @@ +#include + +#include + +#define LOG_TAG "AHAL_Config" +#include +#include + +#include +#include +#include +#include + +#include "core-impl/XmlConverter.h" +#include "core-impl/XsdcConversion.h" + +using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::AudioDevice; +using aidl::android::media::audio::common::AudioDeviceAddress; +using aidl::android::media::audio::common::AudioDeviceDescription; +using aidl::android::media::audio::common::AudioDeviceType; +using aidl::android::media::audio::common::AudioFormatDescription; +using aidl::android::media::audio::common::AudioFormatType; +using aidl::android::media::audio::common::AudioGain; +using aidl::android::media::audio::common::AudioHalCapCriterion; +using aidl::android::media::audio::common::AudioHalCapCriterionType; +using aidl::android::media::audio::common::AudioHalVolumeCurve; +using aidl::android::media::audio::common::AudioIoFlags; +using aidl::android::media::audio::common::AudioPort; +using aidl::android::media::audio::common::AudioPortConfig; +using aidl::android::media::audio::common::AudioPortDeviceExt; +using aidl::android::media::audio::common::AudioPortExt; +using aidl::android::media::audio::common::AudioPortMixExt; +using aidl::android::media::audio::common::AudioProfile; +using ::android::BAD_VALUE; +using ::android::base::unexpected; + +namespace ap_xsd = android::audio::policy::configuration; +namespace eng_xsd = android::audio::policy::engine::configuration; + +namespace aidl::android::hardware::audio::core::internal { + +inline ConversionResult assertNonEmpty(const std::string& s) { + if (s.empty()) { + LOG(ERROR) << __func__ << " Review Audio Policy config: " + << " empty string is not valid."; + return unexpected(BAD_VALUE); + } + return s; +} + +#define NON_EMPTY_STRING_OR_FATAL(s) VALUE_OR_FATAL(assertNonEmpty(s)) + +ConversionResult convertAudioFormatToAidl(const std::string& xsdcFormat) { + audio_format_t legacyFormat = ::android::formatFromString(xsdcFormat, AUDIO_FORMAT_DEFAULT); + ConversionResult result = + legacy2aidl_audio_format_t_AudioFormatDescription(legacyFormat); + if ((legacyFormat == AUDIO_FORMAT_DEFAULT && xsdcFormat.compare("AUDIO_FORMAT_DEFAULT") != 0) || + !result.ok()) { + LOG(ERROR) << __func__ << " Review Audio Policy config: " << xsdcFormat + << " is not a valid audio format."; + return unexpected(BAD_VALUE); + } + return result; +} + +std::unordered_set getAttachedDevices(const ap_xsd::Modules::Module& moduleConfig) { + std::unordered_set attachedDeviceSet; + if (moduleConfig.hasAttachedDevices()) { + for (const ap_xsd::AttachedDevices& attachedDevices : moduleConfig.getAttachedDevices()) { + if (attachedDevices.hasItem()) { + attachedDeviceSet.insert(attachedDevices.getItem().begin(), + attachedDevices.getItem().end()); + } + } + } + return attachedDeviceSet; +} + +ConversionResult convertDeviceTypeToAidl(const std::string& xType) { + audio_devices_t legacyDeviceType = AUDIO_DEVICE_NONE; + ::android::DeviceConverter::fromString(xType, legacyDeviceType); + ConversionResult result = + legacy2aidl_audio_devices_t_AudioDeviceDescription(legacyDeviceType); + if ((legacyDeviceType == AUDIO_DEVICE_NONE) || !result.ok()) { + LOG(ERROR) << __func__ << " Review Audio Policy config: " << xType + << " is not a valid device type."; + return unexpected(BAD_VALUE); + } + return result; +} + +ConversionResult createAudioDevice( + const ap_xsd::DevicePorts::DevicePort& xDevicePort) { + AudioDevice device = { + .type = VALUE_OR_FATAL(convertDeviceTypeToAidl(xDevicePort.getType())), + .address = xDevicePort.hasAddress() + ? AudioDeviceAddress::make( + xDevicePort.getAddress()) + : AudioDeviceAddress{}}; + if (device.type.type == AudioDeviceType::IN_MICROPHONE && device.type.connection.empty()) { + device.address = "bottom"; + } else if (device.type.type == AudioDeviceType::IN_MICROPHONE_BACK && + device.type.connection.empty()) { + device.address = "back"; + } + return device; +} + +ConversionResult createAudioPortExt( + const ap_xsd::DevicePorts::DevicePort& xDevicePort, + const std::string& xDefaultOutputDevice) { + AudioPortDeviceExt deviceExt = { + .device = VALUE_OR_FATAL(createAudioDevice(xDevicePort)), + .flags = (xDevicePort.getTagName() == xDefaultOutputDevice) + ? 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE + : 0, + .encodedFormats = + xDevicePort.hasEncodedFormats() + ? VALUE_OR_FATAL( + (convertCollectionToAidl( + xDevicePort.getEncodedFormats(), + &convertAudioFormatToAidl))) + : std::vector{}, + }; + return AudioPortExt::make(deviceExt); +} + +ConversionResult createAudioPortExt(const ap_xsd::MixPorts::MixPort& xMixPort) { + AudioPortMixExt mixExt = { + .maxOpenStreamCount = + xMixPort.hasMaxOpenCount() ? static_cast(xMixPort.getMaxOpenCount()) : 0, + .maxActiveStreamCount = xMixPort.hasMaxActiveCount() + ? static_cast(xMixPort.getMaxActiveCount()) + : 1, + .recommendedMuteDurationMs = + xMixPort.hasRecommendedMuteDurationMs() + ? static_cast(xMixPort.getRecommendedMuteDurationMs()) + : 0}; + return AudioPortExt::make(mixExt); +} + +ConversionResult convertGainModeToAidl(const std::vector& gainModeVec) { + int gainModeMask = 0; + for (const ap_xsd::AudioGainMode& gainMode : gainModeVec) { + audio_gain_mode_t legacyGainMode; + if (::android::GainModeConverter::fromString(ap_xsd::toString(gainMode), legacyGainMode)) { + gainModeMask |= static_cast(legacyGainMode); + } + } + return gainModeMask; +} + +ConversionResult convertChannelMaskToAidl( + const ap_xsd::AudioChannelMask& xChannelMask) { + std::string xChannelMaskLiteral = ap_xsd::toString(xChannelMask); + audio_channel_mask_t legacyChannelMask = ::android::channelMaskFromString(xChannelMaskLiteral); + ConversionResult result = + legacy2aidl_audio_channel_mask_t_AudioChannelLayout( + legacyChannelMask, + /* isInput= */ xChannelMaskLiteral.find("AUDIO_CHANNEL_IN_") == 0); + if ((legacyChannelMask == AUDIO_CHANNEL_INVALID) || !result.ok()) { + LOG(ERROR) << __func__ << " Review Audio Policy config: " << xChannelMaskLiteral + << " is not a valid audio channel mask."; + return unexpected(BAD_VALUE); + } + return result; +} + +ConversionResult convertGainToAidl(const ap_xsd::Gains::Gain& xGain) { + return AudioGain{ + .mode = VALUE_OR_FATAL(convertGainModeToAidl(xGain.getMode())), + .channelMask = + xGain.hasChannel_mask() + ? VALUE_OR_FATAL(convertChannelMaskToAidl(xGain.getChannel_mask())) + : AudioChannelLayout{}, + .minValue = xGain.hasMinValueMB() ? xGain.getMinValueMB() : 0, + .maxValue = xGain.hasMaxValueMB() ? xGain.getMaxValueMB() : 0, + .defaultValue = xGain.hasDefaultValueMB() ? xGain.getDefaultValueMB() : 0, + .stepValue = xGain.hasStepValueMB() ? xGain.getStepValueMB() : 0, + .minRampMs = xGain.hasMinRampMs() ? xGain.getMinRampMs() : 0, + .maxRampMs = xGain.hasMaxRampMs() ? xGain.getMaxRampMs() : 0, + .useForVolume = xGain.hasUseForVolume() ? xGain.getUseForVolume() : false, + }; +} + +ConversionResult convertAudioProfileToAidl(const ap_xsd::Profile& xProfile) { + return AudioProfile{ + .format = xProfile.hasFormat() + ? VALUE_OR_FATAL(convertAudioFormatToAidl(xProfile.getFormat())) + : AudioFormatDescription{}, + .channelMasks = + xProfile.hasChannelMasks() + ? VALUE_OR_FATAL((convertCollectionToAidl( + xProfile.getChannelMasks(), &convertChannelMaskToAidl))) + : std::vector{}, + .sampleRates = xProfile.hasSamplingRates() + ? VALUE_OR_FATAL((convertCollectionToAidl( + xProfile.getSamplingRates(), + [](const int64_t x) -> int { return x; }))) + : std::vector{}}; +} + +ConversionResult convertIoFlagsToAidl( + const std::vector& flags, const ap_xsd::Role role, + bool flagsForMixPort) { + int legacyFlagMask = 0; + if ((role == ap_xsd::Role::sink && flagsForMixPort) || + (role == ap_xsd::Role::source && !flagsForMixPort)) { + for (const ap_xsd::AudioInOutFlag& flag : flags) { + audio_input_flags_t legacyFlag; + if (::android::InputFlagConverter::fromString(ap_xsd::toString(flag), legacyFlag)) { + legacyFlagMask |= static_cast(legacyFlag); + } + } + return AudioIoFlags::make( + VALUE_OR_FATAL(legacy2aidl_audio_input_flags_t_int32_t_mask( + static_cast(legacyFlagMask)))); + } else { + for (const ap_xsd::AudioInOutFlag& flag : flags) { + audio_output_flags_t legacyFlag; + if (::android::OutputFlagConverter::fromString(ap_xsd::toString(flag), legacyFlag)) { + legacyFlagMask |= static_cast(legacyFlag); + } + } + return AudioIoFlags::make( + VALUE_OR_FATAL(legacy2aidl_audio_output_flags_t_int32_t_mask( + static_cast(legacyFlagMask)))); + } +} + +ConversionResult convertDevicePortToAidl( + const ap_xsd::DevicePorts::DevicePort& xDevicePort, const std::string& xDefaultOutputDevice, + int32_t& nextPortId) { + return AudioPort{ + .id = nextPortId++, + .name = NON_EMPTY_STRING_OR_FATAL(xDevicePort.getTagName()), + .profiles = VALUE_OR_FATAL((convertCollectionToAidl( + xDevicePort.getProfile(), convertAudioProfileToAidl))), + .flags = VALUE_OR_FATAL(convertIoFlagsToAidl({}, xDevicePort.getRole(), false)), + .gains = VALUE_OR_FATAL( + (convertWrappedCollectionToAidl( + xDevicePort.getGains(), &ap_xsd::Gains::getGain, convertGainToAidl))), + + .ext = VALUE_OR_FATAL(createAudioPortExt(xDevicePort, xDefaultOutputDevice))}; +} + +ConversionResult> convertDevicePortsInModuleToAidl( + const ap_xsd::Modules::Module& xModuleConfig, int32_t& nextPortId) { + std::vector audioPortVec; + std::vector xDevicePortsVec = xModuleConfig.getDevicePorts(); + if (xDevicePortsVec.size() > 1) { + LOG(ERROR) << __func__ << "Having multiple '' elements is not allowed, found: " + << xDevicePortsVec.size(); + return unexpected(BAD_VALUE); + } + if (!xDevicePortsVec.empty()) { + const std::string xDefaultOutputDevice = xModuleConfig.hasDefaultOutputDevice() + ? xModuleConfig.getDefaultOutputDevice() + : ""; + audioPortVec.reserve(xDevicePortsVec[0].getDevicePort().size()); + for (const ap_xsd::DevicePorts& xDevicePortsType : xDevicePortsVec) { + for (const ap_xsd::DevicePorts::DevicePort& xDevicePort : + xDevicePortsType.getDevicePort()) { + audioPortVec.push_back(VALUE_OR_FATAL( + convertDevicePortToAidl(xDevicePort, xDefaultOutputDevice, nextPortId))); + } + } + } + const std::unordered_set xAttachedDeviceSet = getAttachedDevices(xModuleConfig); + for (const auto& port : audioPortVec) { + const auto& devicePort = port.ext.get(); + if (xAttachedDeviceSet.count(port.name) != devicePort.device.type.connection.empty()) { + LOG(ERROR) << __func__ << ": Review Audio Policy config: " + << "list is incorrect or devicePort \"" << port.name + << "\" type= " << devicePort.device.type.toString() << " is incorrect."; + return unexpected(BAD_VALUE); + } + } + return audioPortVec; +} + +ConversionResult convertMixPortToAidl(const ap_xsd::MixPorts::MixPort& xMixPort, + int32_t& nextPortId) { + return AudioPort{ + .id = nextPortId++, + .name = NON_EMPTY_STRING_OR_FATAL(xMixPort.getName()), + .profiles = VALUE_OR_FATAL((convertCollectionToAidl( + xMixPort.getProfile(), convertAudioProfileToAidl))), + .flags = xMixPort.hasFlags() + ? VALUE_OR_FATAL(convertIoFlagsToAidl(xMixPort.getFlags(), + xMixPort.getRole(), true)) + : VALUE_OR_FATAL(convertIoFlagsToAidl({}, xMixPort.getRole(), true)), + .gains = VALUE_OR_FATAL( + (convertWrappedCollectionToAidl( + xMixPort.getGains(), &ap_xsd::Gains::getGain, &convertGainToAidl))), + .ext = VALUE_OR_FATAL(createAudioPortExt(xMixPort)), + }; +} + +ConversionResult> convertMixPortsInModuleToAidl( + const ap_xsd::Modules::Module& xModuleConfig, int32_t& nextPortId) { + std::vector audioPortVec; + std::vector xMixPortsVec = xModuleConfig.getMixPorts(); + if (xMixPortsVec.size() > 1) { + LOG(ERROR) << __func__ << "Having multiple '' elements is not allowed, found: " + << xMixPortsVec.size(); + return unexpected(BAD_VALUE); + } + if (!xMixPortsVec.empty()) { + audioPortVec.reserve(xMixPortsVec[0].getMixPort().size()); + for (const ap_xsd::MixPorts& xMixPortsType : xMixPortsVec) { + for (const ap_xsd::MixPorts::MixPort& xMixPort : xMixPortsType.getMixPort()) { + audioPortVec.push_back(VALUE_OR_FATAL(convertMixPortToAidl(xMixPort, nextPortId))); + } + } + } + return audioPortVec; +} + +ConversionResult getSinkPortId(const ap_xsd::Routes::Route& xRoute, + const std::unordered_map& portMap) { + auto portMapIter = portMap.find(xRoute.getSink()); + if (portMapIter == portMap.end()) { + LOG(ERROR) << __func__ << " Review Audio Policy config: audio route" + << "has sink: " << xRoute.getSink() + << " which is neither a device port nor mix port."; + return unexpected(BAD_VALUE); + } + return portMapIter->second; +} + +ConversionResult> getSourcePortIds( + const ap_xsd::Routes::Route& xRoute, + const std::unordered_map& portMap) { + std::vector sourcePortIds; + for (const std::string& rawSource : ::android::base::Split(xRoute.getSources(), ",")) { + const std::string source = ::android::base::Trim(rawSource); + auto portMapIter = portMap.find(source); + if (portMapIter == portMap.end()) { + LOG(ERROR) << __func__ << " Review Audio Policy config: audio route" + << "has source \"" << source + << "\" which is neither a device port nor mix port."; + return unexpected(BAD_VALUE); + } + sourcePortIds.push_back(portMapIter->second); + } + return sourcePortIds; +} + +ConversionResult convertRouteToAidl(const ap_xsd::Routes::Route& xRoute, + const std::vector& aidlAudioPorts) { + std::unordered_map portMap; + for (const AudioPort& port : aidlAudioPorts) { + portMap.insert({port.name, port.id}); + } + return AudioRoute{.sourcePortIds = VALUE_OR_FATAL(getSourcePortIds(xRoute, portMap)), + .sinkPortId = VALUE_OR_FATAL(getSinkPortId(xRoute, portMap)), + .isExclusive = (xRoute.getType() == ap_xsd::MixType::mux)}; +} + +ConversionResult> convertRoutesInModuleToAidl( + const ap_xsd::Modules::Module& xModuleConfig, + const std::vector& aidlAudioPorts) { + std::vector audioRouteVec; + std::vector xRoutesVec = xModuleConfig.getRoutes(); + if (!xRoutesVec.empty()) { + /* + * xRoutesVec likely only contains one element; that is, it's + * likely that all ap_xsd::Routes::MixPort types that we need to convert + * are inside of xRoutesVec[0]. + */ + audioRouteVec.reserve(xRoutesVec[0].getRoute().size()); + for (const ap_xsd::Routes& xRoutesType : xRoutesVec) { + for (const ap_xsd::Routes::Route& xRoute : xRoutesType.getRoute()) { + audioRouteVec.push_back(VALUE_OR_FATAL(convertRouteToAidl(xRoute, aidlAudioPorts))); + } + } + } + return audioRouteVec; +} + +ConversionResult> convertModuleConfigToAidl( + const ap_xsd::Modules::Module& xModuleConfig) { + auto result = std::make_unique(); + auto& aidlModuleConfig = *result; + std::vector devicePorts = VALUE_OR_FATAL( + convertDevicePortsInModuleToAidl(xModuleConfig, aidlModuleConfig.nextPortId)); + + // The XML config does not specify the default input device. + // Assign the first attached input device as the default. + for (auto& port : devicePorts) { + if (port.flags.getTag() != AudioIoFlags::input) continue; + auto& deviceExt = port.ext.get(); + if (!deviceExt.device.type.connection.empty()) continue; + deviceExt.flags |= 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE; + break; + } + + std::vector mixPorts = VALUE_OR_FATAL( + convertMixPortsInModuleToAidl(xModuleConfig, aidlModuleConfig.nextPortId)); + aidlModuleConfig.ports.reserve(devicePorts.size() + mixPorts.size()); + aidlModuleConfig.ports.insert(aidlModuleConfig.ports.end(), devicePorts.begin(), + devicePorts.end()); + aidlModuleConfig.ports.insert(aidlModuleConfig.ports.end(), mixPorts.begin(), mixPorts.end()); + + aidlModuleConfig.routes = + VALUE_OR_FATAL(convertRoutesInModuleToAidl(xModuleConfig, aidlModuleConfig.ports)); + return result; +} + +ConversionResult convertCapCriterionToAidl( + const eng_xsd::CriterionType& xsdcCriterion) { + AudioHalCapCriterion aidlCapCriterion; + aidlCapCriterion.name = xsdcCriterion.getName(); + aidlCapCriterion.criterionTypeName = xsdcCriterion.getType(); + aidlCapCriterion.defaultLiteralValue = xsdcCriterion.get_default(); + return aidlCapCriterion; +} + +ConversionResult convertCriterionTypeValueToAidl( + const eng_xsd::ValueType& xsdcCriterionTypeValue) { + return xsdcCriterionTypeValue.getLiteral(); +} + +ConversionResult convertCapCriterionTypeToAidl( + const eng_xsd::CriterionTypeType& xsdcCriterionType) { + AudioHalCapCriterionType aidlCapCriterionType; + aidlCapCriterionType.name = xsdcCriterionType.getName(); + aidlCapCriterionType.isInclusive = !(static_cast(xsdcCriterionType.getType())); + aidlCapCriterionType.values = VALUE_OR_RETURN( + (convertWrappedCollectionToAidl( + xsdcCriterionType.getValues(), &eng_xsd::ValuesType::getValue, + &convertCriterionTypeValueToAidl))); + return aidlCapCriterionType; +} + +ConversionResult convertCurvePointToAidl( + const std::string& xsdcCurvePoint) { + AudioHalVolumeCurve::CurvePoint aidlCurvePoint{}; + if ((sscanf(xsdcCurvePoint.c_str(), "%" SCNd8 ",%d", &aidlCurvePoint.index, + &aidlCurvePoint.attenuationMb) != 2) || + (aidlCurvePoint.index < AudioHalVolumeCurve::CurvePoint::MIN_INDEX) || + (aidlCurvePoint.index > AudioHalVolumeCurve::CurvePoint::MAX_INDEX)) { + LOG(ERROR) << __func__ << " Review Audio Policy config: volume curve point:" + << "\"" << xsdcCurvePoint << "\" is invalid"; + return unexpected(BAD_VALUE); + } + return aidlCurvePoint; +} +} // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp index 63a014ad674a3eb8dfdb85cf5ae5fec7816aaa66..5e18f1b12f7599d526b0f1378016670d67f66d91 100644 --- a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp +++ b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp @@ -74,9 +74,9 @@ const Descriptor AcousticEchoCancelerSw::kDescriptor = { .common = {.id = {.type = getEffectTypeUuidAcousticEchoCanceler(), .uuid = getEffectImplUuidAcousticEchoCancelerSw(), .proxy = std::nullopt}, - .flags = {.type = Flags::Type::INSERT, + .flags = {.type = Flags::Type::PRE_PROC, .insert = Flags::Insert::FIRST, - .volume = Flags::Volume::CTRL}, + .volume = Flags::Volume::NONE}, .name = AcousticEchoCancelerSw::kEffectName, .implementor = "The Android Open Source Project"}, .capability = AcousticEchoCancelerSw::kCapability}; diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp index 126c033f66e2165c650460873ff8700981a24b3f..e72502b8f7fa65c20877f3a9a231876599a95971 100644 --- a/audio/aidl/default/alsa/Mixer.cpp +++ b/audio/aidl/default/alsa/Mixer.cpp @@ -20,9 +20,24 @@ #define LOG_TAG "AHAL_AlsaMixer" #include #include +#include #include "Mixer.h" +namespace ndk { + +// This enables use of 'error/expected_utils' for ScopedAStatus. + +inline bool errorIsOk(const ScopedAStatus& s) { + return s.isOk(); +} + +inline std::string errorToString(const ScopedAStatus& s) { + return s.getDescription(); +} + +} // namespace ndk + namespace aidl::android::hardware::audio::core::alsa { // static @@ -93,6 +108,36 @@ Mixer::~Mixer() { } } +ndk::ScopedAStatus Mixer::getMasterMute(bool* muted) { + return getMixerControlMute(MASTER_SWITCH, muted); +} + +ndk::ScopedAStatus Mixer::getMasterVolume(float* volume) { + return getMixerControlVolume(MASTER_VOLUME, volume); +} + +ndk::ScopedAStatus Mixer::getMicGain(float* gain) { + return getMixerControlVolume(MIC_GAIN, gain); +} + +ndk::ScopedAStatus Mixer::getMicMute(bool* muted) { + return getMixerControlMute(MIC_SWITCH, muted); +} + +ndk::ScopedAStatus Mixer::getVolumes(std::vector* volumes) { + struct mixer_ctl* mctl; + RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl)); + std::vector percents; + std::lock_guard l(mMixerAccess); + if (int err = getMixerControlPercent(mctl, &percents); err != 0) { + LOG(ERROR) << __func__ << ": failed to get volume, err=" << err; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + std::transform(percents.begin(), percents.end(), std::back_inserter(*volumes), + [](int percent) -> float { return std::clamp(percent / 100.0f, 0.0f, 1.0f); }); + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus Mixer::setMasterMute(bool muted) { return setMixerControlMute(MASTER_SWITCH, muted); } @@ -110,90 +155,143 @@ ndk::ScopedAStatus Mixer::setMicMute(bool muted) { } ndk::ScopedAStatus Mixer::setVolumes(const std::vector& volumes) { - if (!isValid()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - auto it = mMixerControls.find(Mixer::HW_VOLUME); - if (it == mMixerControls.end()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); - } + struct mixer_ctl* mctl; + RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl)); std::vector percents; std::transform( volumes.begin(), volumes.end(), std::back_inserter(percents), [](float volume) -> int { return std::floor(std::clamp(volume, 0.0f, 1.0f) * 100); }); std::lock_guard l(mMixerAccess); - if (int err = setMixerControlPercent(it->second, percents); err != 0) { + if (int err = setMixerControlPercent(mctl, percents); err != 0) { LOG(ERROR) << __func__ << ": failed to set volume, err=" << err; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus Mixer::setMixerControlMute(Mixer::Control ctl, bool muted) { +ndk::ScopedAStatus Mixer::findControl(Control ctl, struct mixer_ctl** result) { if (!isValid()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } - auto it = mMixerControls.find(ctl); - if (it == mMixerControls.end()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + if (auto it = mMixerControls.find(ctl); it != mMixerControls.end()) { + *result = it->second; + return ndk::ScopedAStatus::ok(); } + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Mixer::getMixerControlMute(Control ctl, bool* muted) { + struct mixer_ctl* mctl; + RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl)); std::lock_guard l(mMixerAccess); - if (int err = setMixerControlValue(it->second, muted ? 0 : 1); err != 0) { - LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err; + std::vector mutedValues; + if (int err = getMixerControlValues(mctl, &mutedValues); err != 0) { + LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } + if (mutedValues.empty()) { + LOG(ERROR) << __func__ << ": got no values for " << ctl; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + *muted = mutedValues[0] != 0; return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) { - if (!isValid()) { +ndk::ScopedAStatus Mixer::getMixerControlVolume(Control ctl, float* volume) { + struct mixer_ctl* mctl; + RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl)); + std::lock_guard l(mMixerAccess); + std::vector percents; + if (int err = getMixerControlPercent(mctl, &percents); err != 0) { + LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } - auto it = mMixerControls.find(ctl); - if (it == mMixerControls.end()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + if (percents.empty()) { + LOG(ERROR) << __func__ << ": got no values for " << ctl; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } + *volume = std::clamp(percents[0] / 100.0f, 0.0f, 1.0f); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Mixer::setMixerControlMute(Control ctl, bool muted) { + struct mixer_ctl* mctl; + RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl)); + std::lock_guard l(mMixerAccess); + if (int err = setMixerControlValue(mctl, muted ? 0 : 1); err != 0) { + LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) { + struct mixer_ctl* mctl; + RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl)); volume = std::clamp(volume, 0.0f, 1.0f); std::lock_guard l(mMixerAccess); - if (int err = setMixerControlPercent(it->second, std::floor(volume * 100)); err != 0) { + if (int err = setMixerControlPercent(mctl, std::floor(volume * 100)); err != 0) { LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << volume << ", err=" << err; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } return ndk::ScopedAStatus::ok(); } +int Mixer::getMixerControlPercent(struct mixer_ctl* ctl, std::vector* percents) { + const unsigned int n = mixer_ctl_get_num_values(ctl); + percents->resize(n); + for (unsigned int id = 0; id < n; id++) { + if (int valueOrError = mixer_ctl_get_percent(ctl, id); valueOrError >= 0) { + (*percents)[id] = valueOrError; + } else { + return valueOrError; + } + } + return 0; +} + +int Mixer::getMixerControlValues(struct mixer_ctl* ctl, std::vector* values) { + const unsigned int n = mixer_ctl_get_num_values(ctl); + values->resize(n); + for (unsigned int id = 0; id < n; id++) { + if (int valueOrError = mixer_ctl_get_value(ctl, id); valueOrError >= 0) { + (*values)[id] = valueOrError; + } else { + return valueOrError; + } + } + return 0; +} + int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, int percent) { - int ret = 0; const unsigned int n = mixer_ctl_get_num_values(ctl); for (unsigned int id = 0; id < n; id++) { if (int error = mixer_ctl_set_percent(ctl, id, percent); error != 0) { - ret = error; + return error; } } - return ret; + return 0; } int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, const std::vector& percents) { - int ret = 0; const unsigned int n = mixer_ctl_get_num_values(ctl); for (unsigned int id = 0; id < n; id++) { if (int error = mixer_ctl_set_percent(ctl, id, id < percents.size() ? percents[id] : 0); error != 0) { - ret = error; + return error; } } - return ret; + return 0; } int Mixer::setMixerControlValue(struct mixer_ctl* ctl, int value) { - int ret = 0; const unsigned int n = mixer_ctl_get_num_values(ctl); for (unsigned int id = 0; id < n; id++) { if (int error = mixer_ctl_set_value(ctl, id, value); error != 0) { - ret = error; + return error; } } - return ret; + return 0; } } // namespace aidl::android::hardware::audio::core::alsa diff --git a/audio/aidl/default/alsa/Mixer.h b/audio/aidl/default/alsa/Mixer.h index 8fba1e0753f9612eba39f17aac348e728d56d4b3..41f19a853b150d488a12c1ec03c08677f64c489d 100644 --- a/audio/aidl/default/alsa/Mixer.h +++ b/audio/aidl/default/alsa/Mixer.h @@ -39,6 +39,11 @@ class Mixer { bool isValid() const { return mMixer != nullptr; } + ndk::ScopedAStatus getMasterMute(bool* muted); + ndk::ScopedAStatus getMasterVolume(float* volume); + ndk::ScopedAStatus getMicGain(float* gain); + ndk::ScopedAStatus getMicMute(bool* muted); + ndk::ScopedAStatus getVolumes(std::vector* volumes); ndk::ScopedAStatus setMasterMute(bool muted); ndk::ScopedAStatus setMasterVolume(float volume); ndk::ScopedAStatus setMicGain(float gain); @@ -60,9 +65,16 @@ class Mixer { static const std::map> kPossibleControls; static Controls initializeMixerControls(struct mixer* mixer); + ndk::ScopedAStatus findControl(Control ctl, struct mixer_ctl** result); + ndk::ScopedAStatus getMixerControlMute(Control ctl, bool* muted); + ndk::ScopedAStatus getMixerControlVolume(Control ctl, float* volume); ndk::ScopedAStatus setMixerControlMute(Control ctl, bool muted); ndk::ScopedAStatus setMixerControlVolume(Control ctl, float volume); + int getMixerControlPercent(struct mixer_ctl* ctl, std::vector* percents) + REQUIRES(mMixerAccess); + int getMixerControlValues(struct mixer_ctl* ctl, std::vector* values) + REQUIRES(mMixerAccess); int setMixerControlPercent(struct mixer_ctl* ctl, int percent) REQUIRES(mMixerAccess); int setMixerControlPercent(struct mixer_ctl* ctl, const std::vector& percents) REQUIRES(mMixerAccess); diff --git a/audio/aidl/default/alsa/ModuleAlsa.cpp b/audio/aidl/default/alsa/ModuleAlsa.cpp index 8e75d56843bcd8da8d2ce86fa77408ff32ec9482..8512631268b7180fd1a2453ea8d27465945987c7 100644 --- a/audio/aidl/default/alsa/ModuleAlsa.cpp +++ b/audio/aidl/default/alsa/ModuleAlsa.cpp @@ -39,13 +39,14 @@ ndk::ScopedAStatus ModuleAlsa::populateConnectedDevicePort(AudioPort* audioPort) if (!deviceProfile.has_value()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - auto profile = alsa::readAlsaDeviceInfo(*deviceProfile); - if (!profile.has_value()) { + auto proxy = alsa::readAlsaDeviceInfo(*deviceProfile); + if (proxy.get() == nullptr) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } - std::vector channels = alsa::getChannelMasksFromProfile(&profile.value()); - std::vector sampleRates = alsa::getSampleRatesFromProfile(&profile.value()); + alsa_device_profile* profile = proxy.getProfile(); + std::vector channels = alsa::getChannelMasksFromProfile(profile); + std::vector sampleRates = alsa::getSampleRatesFromProfile(profile); for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) && profile->formats[i] != PCM_FORMAT_INVALID; diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp index 0605d6f464c93d76a742622e8b614b767208c60e..e57d538cf94e82b43c0f44927a4314b006083d89 100644 --- a/audio/aidl/default/alsa/StreamAlsa.cpp +++ b/audio/aidl/default/alsa/StreamAlsa.cpp @@ -83,7 +83,7 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea proxy = alsa::openProxyForAttachedDevice( device, const_cast(&mConfig.value()), mBufferSizeFrames); } - if (!proxy) { + if (proxy.get() == nullptr) { return ::android::NO_INIT; } alsaDeviceProxies.push_back(std::move(proxy)); @@ -119,7 +119,7 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea ::android::status_t StreamAlsa::refinePosition(StreamDescriptor::Position* position) { if (mAlsaDeviceProxies.empty()) { - LOG(FATAL) << __func__ << ": no opened devices"; + LOG(WARNING) << __func__ << ": no opened devices"; return ::android::NO_INIT; } // Since the proxy can only count transferred frames since its creation, diff --git a/audio/aidl/default/alsa/Utils.cpp b/audio/aidl/default/alsa/Utils.cpp index 9dcd024d1c6dcb86b92161e1d36d5f7ef0353efc..c08836c3d66ad152b9afa47c301db2e3abc6faa6 100644 --- a/audio/aidl/default/alsa/Utils.cpp +++ b/audio/aidl/default/alsa/Utils.cpp @@ -37,6 +37,23 @@ using aidl::android::media::audio::common::PcmType; namespace aidl::android::hardware::audio::core::alsa { +DeviceProxy::DeviceProxy() : mProfile(nullptr), mProxy(nullptr, alsaProxyDeleter) {} + +DeviceProxy::DeviceProxy(const DeviceProfile& deviceProfile) + : mProfile(new alsa_device_profile), mProxy(new alsa_device_proxy, alsaProxyDeleter) { + profile_init(mProfile.get(), deviceProfile.direction); + mProfile->card = deviceProfile.card; + mProfile->device = deviceProfile.device; + memset(mProxy.get(), 0, sizeof(alsa_device_proxy)); +} + +void DeviceProxy::alsaProxyDeleter(alsa_device_proxy* proxy) { + if (proxy != nullptr) { + proxy_close(proxy); + delete proxy; + } +} + namespace { using AudioChannelCountToMaskMap = std::map; @@ -261,39 +278,24 @@ std::vector getSampleRatesFromProfile(const alsa_device_profile* profile) { return sampleRates; } -DeviceProxy makeDeviceProxy() { - DeviceProxy proxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) { - if (proxy != nullptr) { - proxy_close(proxy); - delete proxy; - } - }); - memset(proxy.get(), 0, sizeof(alsa_device_proxy)); - return proxy; -} - DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile, struct pcm_config* pcmConfig, size_t bufferFrameCount) { if (deviceProfile.isExternal) { LOG(FATAL) << __func__ << ": called for an external device, address=" << deviceProfile; } - alsa_device_profile profile; - profile_init(&profile, deviceProfile.direction); - profile.card = deviceProfile.card; - profile.device = deviceProfile.device; - if (!profile_fill_builtin_device_info(&profile, pcmConfig, bufferFrameCount)) { + DeviceProxy proxy(deviceProfile); + if (!profile_fill_builtin_device_info(proxy.getProfile(), pcmConfig, bufferFrameCount)) { LOG(FATAL) << __func__ << ": failed to init for built-in device, address=" << deviceProfile; } - auto proxy = makeDeviceProxy(); - if (int err = proxy_prepare_from_default_config(proxy.get(), &profile); err != 0) { + if (int err = proxy_prepare_from_default_config(proxy.get(), proxy.getProfile()); err != 0) { LOG(FATAL) << __func__ << ": fail to prepare for device address=" << deviceProfile << " error=" << err; - return nullptr; + return DeviceProxy(); } if (int err = proxy_open(proxy.get()); err != 0) { LOG(ERROR) << __func__ << ": failed to open device, address=" << deviceProfile << " error=" << err; - return nullptr; + return DeviceProxy(); } return proxy; } @@ -303,42 +305,36 @@ DeviceProxy openProxyForExternalDevice(const DeviceProfile& deviceProfile, if (!deviceProfile.isExternal) { LOG(FATAL) << __func__ << ": called for an attached device, address=" << deviceProfile; } - auto profile = readAlsaDeviceInfo(deviceProfile); - if (!profile.has_value()) { - LOG(ERROR) << __func__ << ": unable to read device info, device address=" << deviceProfile; - return nullptr; + auto proxy = readAlsaDeviceInfo(deviceProfile); + if (proxy.get() == nullptr) { + return proxy; } - auto proxy = makeDeviceProxy(); - if (int err = proxy_prepare(proxy.get(), &profile.value(), pcmConfig, requireExactMatch); + if (int err = proxy_prepare(proxy.get(), proxy.getProfile(), pcmConfig, requireExactMatch); err != 0) { LOG(ERROR) << __func__ << ": fail to prepare for device address=" << deviceProfile << " error=" << err; - return nullptr; + return DeviceProxy(); } if (int err = proxy_open(proxy.get()); err != 0) { LOG(ERROR) << __func__ << ": failed to open device, address=" << deviceProfile << " error=" << err; - return nullptr; + return DeviceProxy(); } return proxy; } -std::optional readAlsaDeviceInfo(const DeviceProfile& deviceProfile) { - alsa_device_profile profile; - profile_init(&profile, deviceProfile.direction); - profile.card = deviceProfile.card; - profile.device = deviceProfile.device; - if (!profile_read_device_info(&profile)) { - LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card - << ", device=" << profile.device; - return std::nullopt; +DeviceProxy readAlsaDeviceInfo(const DeviceProfile& deviceProfile) { + DeviceProxy proxy(deviceProfile); + if (!profile_read_device_info(proxy.getProfile())) { + LOG(ERROR) << __func__ << ": unable to read device info, device address=" << deviceProfile; + return DeviceProxy(); } - return profile; + return proxy; } void resetTransferredFrames(DeviceProxy& proxy, uint64_t frames) { - if (proxy != nullptr) { - proxy->transferred = frames; + if (proxy.get() != nullptr) { + proxy.get()->transferred = frames; } } diff --git a/audio/aidl/default/alsa/Utils.h b/audio/aidl/default/alsa/Utils.h index 37414b3d75c39959edfbcb009ec6f8724877ea58..980f685548beb1f876196834485204198e9e2e4e 100644 --- a/audio/aidl/default/alsa/Utils.h +++ b/audio/aidl/default/alsa/Utils.h @@ -43,8 +43,21 @@ struct DeviceProfile { bool isExternal; }; std::ostream& operator<<(std::ostream& os, const DeviceProfile& device); -using DeviceProxyDeleter = std::function; -using DeviceProxy = std::unique_ptr; + +class DeviceProxy { + public: + DeviceProxy(); // Constructs a "null" proxy. + explicit DeviceProxy(const DeviceProfile& deviceProfile); + alsa_device_profile* getProfile() { return mProfile.get(); } + alsa_device_proxy* get() { return mProxy.get(); } + + private: + static void alsaProxyDeleter(alsa_device_proxy* proxy); + using AlsaProxy = std::unique_ptr; + + std::unique_ptr mProfile; + AlsaProxy mProxy; +}; ::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount( unsigned int channelCount, int isInput); @@ -60,12 +73,11 @@ std::optional getDeviceProfile( const ::aidl::android::media::audio::common::AudioPort& audioPort); std::optional getPcmConfig(const StreamContext& context, bool isInput); std::vector getSampleRatesFromProfile(const alsa_device_profile* profile); -DeviceProxy makeDeviceProxy(); DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile, struct pcm_config* pcmConfig, size_t bufferFrameCount); DeviceProxy openProxyForExternalDevice(const DeviceProfile& deviceProfile, struct pcm_config* pcmConfig, bool requireExactMatch); -std::optional readAlsaDeviceInfo(const DeviceProfile& deviceProfile); +DeviceProxy readAlsaDeviceInfo(const DeviceProfile& deviceProfile); void resetTransferredFrames(DeviceProxy& proxy, uint64_t frames); ::aidl::android::media::audio::common::AudioFormatDescription diff --git a/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml b/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml index fdc53a339c58c48142f001c23f6da2bd51357df2..05a825db1be208808c9a3d62163494f833d6428b 100644 --- a/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml +++ b/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml @@ -1,7 +1,7 @@ android.hardware.audio.effect - 1 + 2 IFactory/default diff --git a/audio/aidl/default/android.hardware.audio.service-aidl.xml b/audio/aidl/default/android.hardware.audio.service-aidl.xml index 9db606157a38a4b6c71613560ff2677f025b0428..2a518763f19847989ce2b93e5cd7ed83c6ee6e9b 100644 --- a/audio/aidl/default/android.hardware.audio.service-aidl.xml +++ b/audio/aidl/default/android.hardware.audio.service-aidl.xml @@ -1,32 +1,34 @@ android.hardware.audio.core - 1 + 2 IModule/default android.hardware.audio.core - 1 + 2 IModule/r_submix android.hardware.audio.core - 1 - IModule/stub + 2 + IModule/bluetooth android.hardware.audio.core - 1 - IModule/usb + 2 + IConfig/default + diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml index 00de797764a8858a5a7f45730c081eab856a42a5..6f0af21ffce63468db08758708dfe1cba6b64d29 100644 --- a/audio/aidl/default/audio_effects_config.xml +++ b/audio/aidl/default/audio_effects_config.xml @@ -96,8 +96,8 @@ - - + + diff --git a/audio/aidl/default/bluetooth/DevicePortProxy.cpp b/audio/aidl/default/bluetooth/DevicePortProxy.cpp index 12e204a07607cd32c1b33a225c0a765580b28768..1be0875a55b851f1f752371e03ad95350ef3df24 100644 --- a/audio/aidl/default/bluetooth/DevicePortProxy.cpp +++ b/audio/aidl/default/bluetooth/DevicePortProxy.cpp @@ -19,11 +19,25 @@ #include #include #include -#include #include +#include "BluetoothAudioSessionControl.h" #include "core-impl/DevicePortProxy.h" +using aidl::android::hardware::audio::common::SinkMetadata; +using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::hardware::bluetooth::audio::AudioConfiguration; +using aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl; +using aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus; +using aidl::android::hardware::bluetooth::audio::ChannelMode; +using aidl::android::hardware::bluetooth::audio::PcmConfiguration; +using aidl::android::hardware::bluetooth::audio::PortStatusCallbacks; +using aidl::android::hardware::bluetooth::audio::PresentationPosition; +using aidl::android::hardware::bluetooth::audio::SessionType; +using aidl::android::media::audio::common::AudioDeviceDescription; +using aidl::android::media::audio::common::AudioDeviceType; +using android::base::StringPrintf; + namespace android::bluetooth::audio::aidl { namespace { @@ -33,20 +47,6 @@ constexpr unsigned int kMaxWaitingTimeMs = 4500; } // namespace -using ::aidl::android::hardware::audio::common::SinkMetadata; -using ::aidl::android::hardware::audio::common::SourceMetadata; -using ::aidl::android::hardware::bluetooth::audio::AudioConfiguration; -using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl; -using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus; -using ::aidl::android::hardware::bluetooth::audio::ChannelMode; -using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration; -using ::aidl::android::hardware::bluetooth::audio::PortStatusCallbacks; -using ::aidl::android::hardware::bluetooth::audio::PresentationPosition; -using ::aidl::android::hardware::bluetooth::audio::SessionType; -using ::aidl::android::media::audio::common::AudioDeviceDescription; -using ::aidl::android::media::audio::common::AudioDeviceType; -using ::android::base::StringPrintf; - std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state) { switch (state) { case BluetoothStreamState::DISABLED: diff --git a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp index bfe7ca038415c915f004aa08d90f73426beb5b8c..8a1cbbfe1c9e2d8667feac4f19b33454b1c2cd61 100644 --- a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp +++ b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp @@ -18,42 +18,71 @@ #include +#include "BluetoothAudioSession.h" #include "core-impl/ModuleBluetooth.h" #include "core-impl/StreamBluetooth.h" -namespace aidl::android::hardware::audio::core { - using aidl::android::hardware::audio::common::SinkMetadata; using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::media::audio::common::AudioDeviceDescription; +using aidl::android::media::audio::common::AudioDeviceType; using aidl::android::media::audio::common::AudioOffloadInfo; +using aidl::android::media::audio::common::AudioPort; +using aidl::android::media::audio::common::AudioPortExt; using aidl::android::media::audio::common::MicrophoneInfo; +using android::bluetooth::audio::aidl::BluetoothAudioPortAidl; +using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut; + +// TODO(b/312265159) bluetooth audio should be in its own process +// Remove this and the shared_libs when that happens +extern "C" binder_status_t createIBluetoothAudioProviderFactory(); + +namespace aidl::android::hardware::audio::core { + +ModuleBluetooth::ModuleBluetooth(std::unique_ptr&& config) + : Module(Type::BLUETOOTH, std::move(config)) { + // TODO(b/312265159) bluetooth audio should be in its own process + // Remove this and the shared_libs when that happens + binder_status_t status = createIBluetoothAudioProviderFactory(); + if (status != STATUS_OK) { + LOG(ERROR) << "Failed to create bluetooth audio provider factory. Status: " + << ::android::statusToString(status); + } +} ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp( std::shared_ptr* _aidl_return) { + *_aidl_return = getBtA2dp().getInstance(); + LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus ModuleBluetooth::getBluetoothLe(std::shared_ptr* _aidl_return) { + *_aidl_return = getBtLe().getInstance(); + LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get(); + return ndk::ScopedAStatus::ok(); +} + +ChildInterface& ModuleBluetooth::getBtA2dp() { if (!mBluetoothA2dp) { auto handle = ndk::SharedRefBase::make(); handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this)); mBluetoothA2dp = handle; } - *_aidl_return = mBluetoothA2dp.getInstance(); - LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get(); - return ndk::ScopedAStatus::ok(); + return mBluetoothA2dp; } -ndk::ScopedAStatus ModuleBluetooth::getBluetoothLe(std::shared_ptr* _aidl_return) { +ChildInterface& ModuleBluetooth::getBtLe() { if (!mBluetoothLe) { auto handle = ndk::SharedRefBase::make(); handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this)); mBluetoothLe = handle; } - *_aidl_return = mBluetoothLe.getInstance(); - LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get(); - return ndk::ScopedAStatus::ok(); + return mBluetoothLe; } -Module::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() { - return std::make_tuple(std::weak_ptr(), mBluetoothA2dp.getInstance(), - mBluetoothLe.getInstance()); +ModuleBluetooth::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() { + return std::make_tuple(std::weak_ptr(), getBtA2dp().getPtr(), getBtLe().getPtr()); } ndk::ScopedAStatus ModuleBluetooth::getMicMute(bool* _aidl_return __unused) { @@ -80,6 +109,54 @@ ndk::ScopedAStatus ModuleBluetooth::createOutputStream( offloadInfo, getBtProfileManagerHandles()); } +ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort) { + if (audioPort->ext.getTag() != AudioPortExt::device) { + LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + const auto& devicePort = audioPort->ext.get(); + const auto& description = devicePort.device.type; + // Since the configuration of the BT module is static, there is nothing to populate here. + // However, this method must return an error when the device can not be connected, + // this is determined by the status of BT profiles. + if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP) { + bool isA2dpEnabled = false; + if (!!mBluetoothA2dp) { + RETURN_STATUS_IF_ERROR((*mBluetoothA2dp).isEnabled(&isA2dpEnabled)); + } + LOG(DEBUG) << __func__ << ": isA2dpEnabled: " << isA2dpEnabled; + return isA2dpEnabled ? ndk::ScopedAStatus::ok() + : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE) { + bool isLeEnabled = false; + if (!!mBluetoothLe) { + RETURN_STATUS_IF_ERROR((*mBluetoothLe).isEnabled(&isLeEnabled)); + } + LOG(DEBUG) << __func__ << ": isLeEnabled: " << isLeEnabled; + return isLeEnabled ? ndk::ScopedAStatus::ok() + : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS && + description.type == AudioDeviceType::OUT_HEARING_AID) { + // Hearing aids can use a number of profiles, thus the only way to check + // connectivity is to try to talk to the BT HAL. + if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession:: + IsAidlAvailable()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + std::shared_ptr proxy = std::shared_ptr( + std::make_shared()); + if (proxy->registerPort(description)) { + LOG(DEBUG) << __func__ << ": registered hearing aid port"; + proxy->unregisterPort(); + return ndk::ScopedAStatus::ok(); + } + LOG(DEBUG) << __func__ << ": failed to register hearing aid port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + LOG(ERROR) << __func__ << ": unsupported device type: " << audioPort->toString(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) { LOG(DEBUG) << __func__ << ": is not supported"; return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); diff --git a/audio/aidl/default/bluetooth/StreamBluetooth.cpp b/audio/aidl/default/bluetooth/StreamBluetooth.cpp index 91a33c2f5de037e82fb84a958e7397b0e025b7c5..0cee7f4002cbb4acc83dd842d3921290ff751def 100644 --- a/audio/aidl/default/bluetooth/StreamBluetooth.cpp +++ b/audio/aidl/default/bluetooth/StreamBluetooth.cpp @@ -20,52 +20,49 @@ #include #include -#include "BluetoothAudioSessionControl.h" +#include "BluetoothAudioSession.h" #include "core-impl/StreamBluetooth.h" -namespace aidl::android::hardware::audio::core { +using aidl::android::hardware::audio::common::frameCountFromDurationUs; +using aidl::android::hardware::audio::common::SinkMetadata; +using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::hardware::audio::core::VendorParameter; +using aidl::android::hardware::bluetooth::audio::ChannelMode; +using aidl::android::hardware::bluetooth::audio::PcmConfiguration; +using aidl::android::hardware::bluetooth::audio::PresentationPosition; +using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::AudioDevice; +using aidl::android::media::audio::common::AudioDeviceAddress; +using aidl::android::media::audio::common::AudioFormatDescription; +using aidl::android::media::audio::common::AudioFormatType; +using aidl::android::media::audio::common::AudioOffloadInfo; +using aidl::android::media::audio::common::MicrophoneDynamicInfo; +using aidl::android::media::audio::common::MicrophoneInfo; +using android::bluetooth::audio::aidl::BluetoothAudioPortAidl; +using android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn; +using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut; +using android::bluetooth::audio::aidl::BluetoothStreamState; -using ::aidl::android::hardware::audio::common::SinkMetadata; -using ::aidl::android::hardware::audio::common::SourceMetadata; -using ::aidl::android::hardware::audio::core::VendorParameter; -using ::aidl::android::hardware::bluetooth::audio::ChannelMode; -using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration; -using ::aidl::android::hardware::bluetooth::audio::PresentationPosition; -using ::aidl::android::media::audio::common::AudioChannelLayout; -using ::aidl::android::media::audio::common::AudioDevice; -using ::aidl::android::media::audio::common::AudioDeviceAddress; -using ::aidl::android::media::audio::common::AudioFormatDescription; -using ::aidl::android::media::audio::common::AudioFormatType; -using ::aidl::android::media::audio::common::AudioOffloadInfo; -using ::aidl::android::media::audio::common::MicrophoneDynamicInfo; -using ::aidl::android::media::audio::common::MicrophoneInfo; -using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidl; -using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn; -using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut; -using ::android::bluetooth::audio::aidl::BluetoothStreamState; +namespace aidl::android::hardware::audio::core { constexpr int kBluetoothDefaultInputBufferMs = 20; constexpr int kBluetoothDefaultOutputBufferMs = 10; // constexpr int kBluetoothSpatializerOutputBufferMs = 10; -size_t getFrameCount(uint64_t durationUs, uint32_t sampleRate) { - return (durationUs * sampleRate) / 1000000; -} - // pcm configuration params are not really used by the module StreamBluetooth::StreamBluetooth(StreamContext* context, const Metadata& metadata, - Module::BtProfileHandles&& btHandles) + ModuleBluetooth::BtProfileHandles&& btHandles) : StreamCommonImpl(context, metadata), mSampleRate(getContext().getSampleRate()), mChannelLayout(getContext().getChannelLayout()), mFormat(getContext().getFormat()), mFrameSizeBytes(getContext().getFrameSize()), mIsInput(isInput(metadata)), - mBluetoothA2dp(std::move(std::get(btHandles))), - mBluetoothLe(std::move(std::get(btHandles))) { + mBluetoothA2dp(std::move(std::get(btHandles))), + mBluetoothLe(std::move(std::get(btHandles))) { mPreferredDataIntervalUs = - mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs; - mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate); + (mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs) * 1000; + mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate); mIsInitialized = false; mIsReadyToClose = false; } @@ -226,7 +223,7 @@ bool StreamBluetooth::checkConfigParams( if (config.dataIntervalUs > 0) { mPreferredDataIntervalUs = std::min((int32_t)mPreferredDataIntervalUs, config.dataIntervalUs); - mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate); + mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate); } return true; } @@ -318,7 +315,7 @@ ndk::ScopedAStatus StreamBluetooth::bluetoothParametersUpdated() { StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata, const std::vector& microphones, - Module::BtProfileHandles&& btProfileHandles) + ModuleBluetooth::BtProfileHandles&& btProfileHandles) : StreamIn(std::move(context), microphones), StreamBluetooth(&mContextInstance, sinkMetadata, std::move(btProfileHandles)) {} @@ -331,7 +328,7 @@ ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones( StreamOutBluetooth::StreamOutBluetooth(StreamContext&& context, const SourceMetadata& sourceMetadata, const std::optional& offloadInfo, - Module::BtProfileHandles&& btProfileHandles) + ModuleBluetooth::BtProfileHandles&& btProfileHandles) : StreamOut(std::move(context), offloadInfo), StreamBluetooth(&mContextInstance, sourceMetadata, std::move(btProfileHandles)) {} diff --git a/audio/aidl/default/config/audioPolicy/api/current.txt b/audio/aidl/default/config/audioPolicy/api/current.txt index e2bc833d43dab66fcf9162a87604950877175da3..3547f54d3bff41727bd2482c7028268d17e8e597 100644 --- a/audio/aidl/default/config/audioPolicy/api/current.txt +++ b/audio/aidl/default/config/audioPolicy/api/current.txt @@ -85,16 +85,6 @@ package android.audio.policy.configuration { enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_OUT_TRI_BACK; } - public enum AudioContentType { - method @NonNull public String getRawName(); - enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_MOVIE; - enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_MUSIC; - enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_SONIFICATION; - enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_SPEECH; - enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_ULTRASOUND; - enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_UNKNOWN; - } - public enum AudioDevice { method @NonNull public String getRawName(); enum_constant public static final android.audio.policy.configuration.AudioDevice AUDIO_DEVICE_IN_AMBIENT; @@ -168,13 +158,6 @@ package android.audio.policy.configuration { enum_constant public static final android.audio.policy.configuration.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADSET; } - public enum AudioEncapsulationType { - method @NonNull public String getRawName(); - enum_constant public static final android.audio.policy.configuration.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_IEC61937; - enum_constant public static final android.audio.policy.configuration.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_NONE; - enum_constant public static final android.audio.policy.configuration.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_PCM; - } - public enum AudioFormat { method @NonNull public String getRawName(); enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_AAC; @@ -359,29 +342,6 @@ package android.audio.policy.configuration { enum_constant public static final android.audio.policy.configuration.AudioStreamType AUDIO_STREAM_VOICE_CALL; } - public enum AudioUsage { - method @NonNull public String getRawName(); - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ALARM; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ANNOUNCEMENT; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ASSISTANCE_SONIFICATION; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ASSISTANT; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_CALL_ASSISTANT; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_EMERGENCY; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_GAME; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_MEDIA; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_NOTIFICATION; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_NOTIFICATION_EVENT; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_SAFETY; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_UNKNOWN; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_VEHICLE_STATUS; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_VIRTUAL_SOURCE; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION; - enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - } - public enum DeviceCategory { method @NonNull public String getRawName(); enum_constant public static final android.audio.policy.configuration.DeviceCategory DEVICE_CATEGORY_EARPIECE; @@ -435,7 +395,6 @@ package android.audio.policy.configuration { method @Nullable public int getMinRampMs(); method @Nullable public int getMinValueMB(); method @Nullable public java.util.List getMode(); - method @Nullable public String getName(); method @Nullable public int getStepValueMB(); method @Nullable public boolean getUseForVolume(); method public void setChannel_mask(@Nullable android.audio.policy.configuration.AudioChannelMask); @@ -445,7 +404,6 @@ package android.audio.policy.configuration { method public void setMinRampMs(@Nullable int); method public void setMinValueMB(@Nullable int); method public void setMode(@Nullable java.util.List); - method public void setName(@Nullable String); method public void setStepValueMB(@Nullable int); method public void setUseForVolume(@Nullable boolean); } @@ -478,7 +436,6 @@ package android.audio.policy.configuration { method @Nullable public long getMaxActiveCount(); method @Nullable public long getMaxOpenCount(); method @Nullable public String getName(); - method @Nullable public java.util.List getPreferredUsage(); method @Nullable public java.util.List getProfile(); method @Nullable public long getRecommendedMuteDurationMs(); method @Nullable public android.audio.policy.configuration.Role getRole(); @@ -487,7 +444,6 @@ package android.audio.policy.configuration { method public void setMaxActiveCount(@Nullable long); method public void setMaxOpenCount(@Nullable long); method public void setName(@Nullable String); - method public void setPreferredUsage(@Nullable java.util.List); method public void setRecommendedMuteDurationMs(@Nullable long); method public void setRole(@Nullable android.audio.policy.configuration.Role); } @@ -524,14 +480,10 @@ package android.audio.policy.configuration { public class Profile { ctor public Profile(); method @Nullable public java.util.List getChannelMasks(); - method @Nullable public android.audio.policy.configuration.AudioEncapsulationType getEncapsulationType(); method @Nullable public String getFormat(); - method @Nullable public String getName(); method @Nullable public java.util.List getSamplingRates(); method public void setChannelMasks(@Nullable java.util.List); - method public void setEncapsulationType(@Nullable android.audio.policy.configuration.AudioEncapsulationType); method public void setFormat(@Nullable String); - method public void setName(@Nullable String); method public void setSamplingRates(@Nullable java.util.List); } diff --git a/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd b/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd index 9a3a447b5f137643d69d230a422612b2131ac42b..d93f6972e1352e63627dd6f68aa3e7eb3dc18da3 100644 --- a/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd +++ b/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd @@ -217,20 +217,6 @@ - - - - When choosing the mixPort of an audio track, the audioPolicy - first considers the mixPorts with a preferredUsage including - the track AudioUsage preferred . - If non support the track format, the other mixPorts are considered. - Eg: a will receive - the audio of all apps playing with a MEDIA usage. - It may receive audio from ALARM if there are no audio compatible - . - - - @@ -434,56 +420,6 @@ - - - - Audio usage specifies the intended use case for the sound being played. - Please consult frameworks/base/media/java/android/media/AudioAttributes.java - for the description of each value. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Audio content type expresses the general category of the content. - Please consult frameworks/base/media/java/android/media/AudioAttributes.java - for the description of each value. - - - - - - - - - - - @@ -579,19 +515,10 @@ - - - - - - - - - @@ -612,7 +539,6 @@ - diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp index ed6cfa0b397ad8dcedab091de9fee5cef9dfc3db..e8f90b216bc7236ebc3f146c2ce0fa6dfc44dfda 100644 --- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp +++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp @@ -93,7 +93,7 @@ const Descriptor DynamicsProcessingSw::kDescriptor = { .common = {.id = {.type = getEffectTypeUuidDynamicsProcessing(), .uuid = getEffectImplUuidDynamicsProcessingSw(), .proxy = std::nullopt}, - .flags = {.type = Flags::Type::INSERT, + .flags = {.type = Flags::Type::POST_PROC, .insert = Flags::Insert::FIRST, .volume = Flags::Volume::CTRL}, .name = DynamicsProcessingSw::kEffectName, diff --git a/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h b/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h index 090d58585f4e774399c186cbdcd04ba95c8d72b3..bff4b4a5d46f6c77cdcbed6228cb6536ff8546c8 100644 --- a/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h +++ b/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h @@ -16,27 +16,42 @@ #pragma once +#include +#include #include +#include +#include +#include #include #include #include #include +#include +#include "core-impl/Module.h" #include "core-impl/XmlConverter.h" namespace aidl::android::hardware::audio::core::internal { class AudioPolicyConfigXmlConverter { public: + using ModuleConfiguration = std::pair>; + using ModuleConfigs = std::vector; + explicit AudioPolicyConfigXmlConverter(const std::string& configFilePath) - : mConverter(configFilePath, &::android::audio::policy::configuration::read) {} + : mConverter(configFilePath, &::android::audio::policy::configuration::read) { + if (mConverter.getXsdcConfig()) { + init(); + } + } std::string getError() const { return mConverter.getError(); } ::android::status_t getStatus() const { return mConverter.getStatus(); } const ::aidl::android::media::audio::common::AudioHalEngineConfig& getAidlEngineConfig(); const SurroundSoundConfig& getSurroundSoundConfig(); + std::unique_ptr releaseModuleConfigs(); // Public for testing purposes. static const SurroundSoundConfig& getDefaultSurroundSoundConfig(); @@ -47,13 +62,13 @@ class AudioPolicyConfigXmlConverter { return mConverter.getXsdcConfig(); } void addVolumeGroupstoEngineConfig(); + void init(); void mapStreamToVolumeCurve( const ::android::audio::policy::configuration::Volume& xsdcVolumeCurve); void mapStreamsToVolumeCurves(); void parseVolumes(); - ::aidl::android::media::audio::common::AudioHalVolumeCurve::CurvePoint convertCurvePointToAidl( - const std::string& xsdcCurvePoint); - ::aidl::android::media::audio::common::AudioHalVolumeCurve convertVolumeCurveToAidl( + ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeCurve> + convertVolumeCurveToAidl( const ::android::audio::policy::configuration::Volume& xsdcVolumeCurve); ::aidl::android::media::audio::common::AudioHalEngineConfig mAidlEngineConfig; @@ -63,6 +78,7 @@ class AudioPolicyConfigXmlConverter { std::unordered_map<::android::audio::policy::configuration::AudioStreamType, std::vector<::aidl::android::media::audio::common::AudioHalVolumeCurve>> mStreamToVolumeCurvesMap; + std::unique_ptr mModuleConfigurations = std::make_unique(); }; } // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/include/core-impl/Bluetooth.h b/audio/aidl/default/include/core-impl/Bluetooth.h index 44899bcee2d4db2dbac76c4289f48bff092c7d87..002cb19599fb3a6aa66ea068be9575538ab93c3c 100644 --- a/audio/aidl/default/include/core-impl/Bluetooth.h +++ b/audio/aidl/default/include/core-impl/Bluetooth.h @@ -46,9 +46,9 @@ class Bluetooth : public BnBluetooth { class BluetoothA2dp : public BnBluetoothA2dp, public ParamChangeHandler { public: BluetoothA2dp() = default; + ndk::ScopedAStatus isEnabled(bool* _aidl_return) override; private: - ndk::ScopedAStatus isEnabled(bool* _aidl_return) override; ndk::ScopedAStatus setEnabled(bool in_enabled) override; ndk::ScopedAStatus supportsOffloadReconfiguration(bool* _aidl_return) override; ndk::ScopedAStatus reconfigureOffload( @@ -61,9 +61,9 @@ class BluetoothA2dp : public BnBluetoothA2dp, public ParamChangeHandler { class BluetoothLe : public BnBluetoothLe, public ParamChangeHandler { public: BluetoothLe() = default; + ndk::ScopedAStatus isEnabled(bool* _aidl_return) override; private: - ndk::ScopedAStatus isEnabled(bool* _aidl_return) override; ndk::ScopedAStatus setEnabled(bool in_enabled) override; ndk::ScopedAStatus supportsOffloadReconfiguration(bool* _aidl_return) override; ndk::ScopedAStatus reconfigureOffload( diff --git a/audio/aidl/default/include/core-impl/ChildInterface.h b/audio/aidl/default/include/core-impl/ChildInterface.h index 2421b59a1aa2999c333400e678b8b97c0f9ba0f3..f5f1855aabd0f4dd4fad134b4547315e7fc0ad38 100644 --- a/audio/aidl/default/include/core-impl/ChildInterface.h +++ b/audio/aidl/default/include/core-impl/ChildInterface.h @@ -40,14 +40,19 @@ struct ChildInterface : private std::pair, ndk::SpAIBinder> { explicit operator bool() const { return !!this->first; } C& operator*() const { return *(this->first); } C* operator->() const { return this->first; } + std::shared_ptr getPtr() { return this->first; } // Use 'getInstance' when returning the interface instance. std::shared_ptr getInstance() { + (void)getBinder(); + return this->first; + } + AIBinder* getBinder() { if (this->second.get() == nullptr) { this->second = this->first->asBinder(); AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO); } - return this->first; + return this->second.get(); } }; diff --git a/audio/aidl/default/include/core-impl/Config.h b/audio/aidl/default/include/core-impl/Config.h index 96a6cb9e8e54979147ba26b3573511d1586e0710..63d4b3d0c2a33fa384c353bb7c7b1752e424516b 100644 --- a/audio/aidl/default/include/core-impl/Config.h +++ b/audio/aidl/default/include/core-impl/Config.h @@ -26,11 +26,16 @@ namespace aidl::android::hardware::audio::core { static const std::string kEngineConfigFileName = "audio_policy_engine_configuration.xml"; class Config : public BnConfig { + public: + explicit Config(internal::AudioPolicyConfigXmlConverter& apConverter) + : mAudioPolicyConverter(apConverter) {} + + private: ndk::ScopedAStatus getSurroundSoundConfig(SurroundSoundConfig* _aidl_return) override; ndk::ScopedAStatus getEngineConfig( aidl::android::media::audio::common::AudioHalEngineConfig* _aidl_return) override; - internal::AudioPolicyConfigXmlConverter mAudioPolicyConverter{ - ::android::audio_get_audio_policy_config_file()}; + + internal::AudioPolicyConfigXmlConverter& mAudioPolicyConverter; internal::EngineConfigXmlConverter mEngConfigConverter{ ::android::audio_find_readable_configuration_file(kEngineConfigFileName.c_str())}; }; diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h index 6277c38db26666193d5c2e9048be2d1d75a227fd..a56c8c9f9803ed14f9f7ba26a911cf536ab92636 100644 --- a/audio/aidl/default/include/core-impl/Configuration.h +++ b/audio/aidl/default/include/core-impl/Configuration.h @@ -16,37 +16,14 @@ #pragma once -#include #include -#include -#include -#include -#include -#include -#include +#include "Module.h" namespace aidl::android::hardware::audio::core::internal { -struct Configuration { - std::vector<::aidl::android::media::audio::common::MicrophoneInfo> microphones; - std::vector<::aidl::android::media::audio::common::AudioPort> ports; - std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs; - std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs; - // Port id -> List of profiles to use when the device port state is set to 'connected' - // in connection simulation mode. - std::map> - connectedProfiles; - std::vector routes; - std::vector patches; - int32_t nextPortId = 1; - int32_t nextPatchId = 1; -}; - -std::unique_ptr getPrimaryConfiguration(); -std::unique_ptr getRSubmixConfiguration(); -std::unique_ptr getStubConfiguration(); -std::unique_ptr getUsbConfiguration(); -std::unique_ptr getBluetoothConfiguration(); +std::unique_ptr getConfiguration(Module::Type moduleType); +std::vector +getStandard16And24BitPcmAudioProfiles(); } // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/include/core-impl/DevicePortProxy.h b/audio/aidl/default/include/core-impl/DevicePortProxy.h index 13b8c9136ebde6fddbbbc548518529adba3d2b39..17a8cf3d9a17e50f30db466e9e480435598219e1 100644 --- a/audio/aidl/default/include/core-impl/DevicePortProxy.h +++ b/audio/aidl/default/include/core-impl/DevicePortProxy.h @@ -25,11 +25,10 @@ #include #include #include +#include #include #include -#include "BluetoothAudioSessionControl.h" - namespace android::bluetooth::audio::aidl { enum class BluetoothStreamState : uint8_t { @@ -239,4 +238,4 @@ class BluetoothAudioPortAidlIn : public BluetoothAudioPortAidl { size_t readData(void* buffer, size_t bytes) const override; }; -} // namespace android::bluetooth::audio::aidl \ No newline at end of file +} // namespace android::bluetooth::audio::aidl diff --git a/audio/aidl/default/include/core-impl/EngineConfigXmlConverter.h b/audio/aidl/default/include/core-impl/EngineConfigXmlConverter.h index b34441d9ace5547deb407cd8c10c162c69b882ba..22ac8cb42c3ab25c5b7ad5b6e54e0c72a3a17972 100644 --- a/audio/aidl/default/include/core-impl/EngineConfigXmlConverter.h +++ b/audio/aidl/default/include/core-impl/EngineConfigXmlConverter.h @@ -19,10 +19,9 @@ #include #include -#include - #include #include +#include #include "core-impl/XmlConverter.h" @@ -49,29 +48,24 @@ class EngineConfigXmlConverter { } void init(); void initProductStrategyMap(); - ::aidl::android::media::audio::common::AudioAttributes convertAudioAttributesToAidl( + ConversionResult<::aidl::android::media::audio::common::AudioAttributes> + convertAudioAttributesToAidl( const ::android::audio::policy::engine::configuration::AttributesType& xsdcAudioAttributes); - ::aidl::android::media::audio::common::AudioHalAttributesGroup convertAttributesGroupToAidl( + ConversionResult<::aidl::android::media::audio::common::AudioHalAttributesGroup> + convertAttributesGroupToAidl( const ::android::audio::policy::engine::configuration::AttributesGroup& xsdcAttributesGroup); - ::aidl::android::media::audio::common::AudioHalCapCriterion convertCapCriterionToAidl( - const ::android::audio::policy::engine::configuration::CriterionType& xsdcCriterion); - ::aidl::android::media::audio::common::AudioHalCapCriterionType convertCapCriterionTypeToAidl( - const ::android::audio::policy::engine::configuration::CriterionTypeType& - xsdcCriterionType); - std::string convertCriterionTypeValueToAidl( - const ::android::audio::policy::engine::configuration::ValueType& - xsdcCriterionTypeValue); - ::aidl::android::media::audio::common::AudioHalVolumeCurve::CurvePoint convertCurvePointToAidl( - const std::string& xsdcCurvePoint); - ::aidl::android::media::audio::common::AudioHalProductStrategy convertProductStrategyToAidl( - const ::android::audio::policy::engine::configuration::ProductStrategies:: - ProductStrategy& xsdcProductStrategy); - int convertProductStrategyNameToAidl(const std::string& xsdcProductStrategyName); - ::aidl::android::media::audio::common::AudioHalVolumeCurve convertVolumeCurveToAidl( + ConversionResult<::aidl::android::media::audio::common::AudioHalProductStrategy> + convertProductStrategyToAidl(const ::android::audio::policy::engine::configuration:: + ProductStrategies::ProductStrategy& xsdcProductStrategy); + ConversionResult convertProductStrategyNameToAidl( + const std::string& xsdcProductStrategyName); + ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeCurve> + convertVolumeCurveToAidl( const ::android::audio::policy::engine::configuration::Volume& xsdcVolumeCurve); - ::aidl::android::media::audio::common::AudioHalVolumeGroup convertVolumeGroupToAidl( + ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeGroup> + convertVolumeGroupToAidl( const ::android::audio::policy::engine::configuration::VolumeGroupsType::VolumeGroup& xsdcVolumeGroup); diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h index fb3eef214fbdfe3ccce201b9595dca8c45f3244d..572b5979cc56b6fb3d82c3f831470b8b8a7913ff 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -19,30 +19,42 @@ #include #include #include +#include #include +#include #include #include "core-impl/ChildInterface.h" -#include "core-impl/Configuration.h" #include "core-impl/Stream.h" namespace aidl::android::hardware::audio::core { class Module : public BnModule { public: - // This value is used for all AudioPatches and reported by all streams. - static constexpr int32_t kLatencyMs = 10; + struct Configuration { + std::vector<::aidl::android::media::audio::common::AudioPort> ports; + std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs; + std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs; + // Port id -> List of profiles to use when the device port state is set to 'connected' + // in connection simulation mode. + std::map> + connectedProfiles; + std::vector routes; + std::vector patches; + int32_t nextPortId = 1; + int32_t nextPatchId = 1; + }; enum Type : int { DEFAULT, R_SUBMIX, STUB, USB, BLUETOOTH }; - enum BtInterface : int { BTCONF, BTA2DP, BTLE }; - - static std::shared_ptr createInstance(Type type); - explicit Module(Type type) : mType(type) {} + static std::shared_ptr createInstance(Type type) { + return createInstance(type, std::make_unique()); + } + static std::shared_ptr createInstance(Type type, + std::unique_ptr&& config); + static std::optional typeFromString(const std::string& type); - typedef std::tuple, std::weak_ptr, - std::weak_ptr> - BtProfileHandles; + Module(Type type, std::unique_ptr&& config); protected: // The vendor extension done via inheritance can override interface methods and augment @@ -58,6 +70,7 @@ class Module : public BnModule { const ::aidl::android::media::audio::common::AudioPort& in_templateIdAndAdditionalData, ::aidl::android::media::audio::common::AudioPort* _aidl_return) override; ndk::ScopedAStatus disconnectExternalDevice(int32_t in_portId) override; + ndk::ScopedAStatus prepareToDisconnectExternalDevice(int32_t in_portId) override; ndk::ScopedAStatus getAudioPatches(std::vector* _aidl_return) override; ndk::ScopedAStatus getAudioPort( int32_t in_portId, @@ -127,8 +140,6 @@ class Module : public BnModule { ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t* _aidl_return) override; ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override; - // This value is used for all AudioPatches. - static constexpr int32_t kMinimumStreamBufferSizeFrames = 256; // The maximum stream buffer size is 1 GiB = 2 ** 30 bytes; static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30; @@ -142,13 +153,13 @@ class Module : public BnModule { // ids of device ports created at runtime via 'connectExternalDevice'. // Also stores a list of ids of mix ports with dynamic profiles that were populated from // the connected port. This list can be empty, thus an int->int multimap can't be used. - using ConnectedDevicePorts = std::map>; + using ConnectedDevicePorts = std::map>; // Maps port ids and port config ids to patch ids. // Multimap because both ports and configs can be used by multiple patches. using Patches = std::multimap; const Type mType; - std::unique_ptr mConfig; + std::unique_ptr mConfig; ModuleDebug mDebug; VendorDebug mVendorDebug; ConnectedDevicePorts mConnectedDevicePorts; @@ -157,7 +168,7 @@ class Module : public BnModule { bool mMicMute = false; bool mMasterMute = false; float mMasterVolume = 1.0f; - ChildInterface mSoundDose; + ChildInterface mSoundDose; std::optional mIsMmapSupported; protected: @@ -185,11 +196,25 @@ class Module : public BnModule { const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks); virtual void onExternalDeviceConnectionChanged( const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected); + virtual void onPrepareToDisconnectExternalDevice( + const ::aidl::android::media::audio::common::AudioPort& audioPort); virtual ndk::ScopedAStatus onMasterMuteChanged(bool mute); virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume); - virtual std::unique_ptr initializeConfig(); + virtual std::vector<::aidl::android::media::audio::common::MicrophoneInfo> getMicrophoneInfos(); + virtual std::unique_ptr initializeConfig(); + virtual int32_t getNominalLatencyMs( + const ::aidl::android::media::audio::common::AudioPortConfig& portConfig); // Utility and helper functions accessible to subclasses. + static int32_t calculateBufferSizeFrames(int32_t latencyMs, int32_t sampleRateHz) { + const int32_t rawSizeFrames = + aidl::android::hardware::audio::common::frameCountFromDurationMs(latencyMs, + sampleRateHz); + int32_t powerOf2 = 1; + while (powerOf2 < rawSizeFrames) powerOf2 <<= 1; + return powerOf2; + } + ndk::ScopedAStatus bluetoothParametersUpdated(); void cleanUpPatch(int32_t patchId); ndk::ScopedAStatus createStreamContext( @@ -202,16 +227,19 @@ class Module : public BnModule { std::set findConnectedPortConfigIds(int32_t portConfigId); ndk::ScopedAStatus findPortIdForNewStream( int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port); - virtual BtProfileHandles getBtProfileManagerHandles(); - internal::Configuration& getConfig(); + std::vector getAudioRoutesForAudioPortImpl(int32_t portId); + Configuration& getConfig(); const ConnectedDevicePorts& getConnectedDevicePorts() const { return mConnectedDevicePorts; } bool getMasterMute() const { return mMasterMute; } bool getMasterVolume() const { return mMasterVolume; } bool getMicMute() const { return mMicMute; } const Patches& getPatches() const { return mPatches; } + std::set getRoutableAudioPortIds(int32_t portId, + std::vector* routes = nullptr); const Streams& getStreams() const { return mStreams; } Type getType() const { return mType; } bool isMmapSupported(); + void populateConnectedProfiles(); template std::set portIdsFromPortConfigIds(C portConfigIds); void registerPatch(const AudioPatch& patch); diff --git a/audio/aidl/default/include/core-impl/ModuleAlsa.h b/audio/aidl/default/include/core-impl/ModuleAlsa.h index 5815961f7c9bb771ef7af3c74c23e57746d57aa3..2774fe5b9e7235fdc1350ddacf63c4be80cb7c00 100644 --- a/audio/aidl/default/include/core-impl/ModuleAlsa.h +++ b/audio/aidl/default/include/core-impl/ModuleAlsa.h @@ -27,7 +27,8 @@ namespace aidl::android::hardware::audio::core { // provide necessary overrides for all interface methods omitted here. class ModuleAlsa : public Module { public: - explicit ModuleAlsa(Module::Type type) : Module(type) {} + ModuleAlsa(Type type, std::unique_ptr&& config) + : Module(type, std::move(config)) {} protected: // Extension methods of 'Module'. diff --git a/audio/aidl/default/include/core-impl/ModuleBluetooth.h b/audio/aidl/default/include/core-impl/ModuleBluetooth.h index 68b4e6b058f32ca79058df508a685f71ca209864..631b08854cfe839f86f0f0e9fc1384b8f7625e59 100644 --- a/audio/aidl/default/include/core-impl/ModuleBluetooth.h +++ b/audio/aidl/default/include/core-impl/ModuleBluetooth.h @@ -23,10 +23,17 @@ namespace aidl::android::hardware::audio::core { class ModuleBluetooth final : public Module { public: - ModuleBluetooth() : Module(Type::BLUETOOTH) {} + enum BtInterface : int { BTSCO, BTA2DP, BTLE }; + typedef std::tuple, std::weak_ptr, + std::weak_ptr> + BtProfileHandles; + + ModuleBluetooth(std::unique_ptr&& config); private: - BtProfileHandles getBtProfileManagerHandles() override; + ChildInterface& getBtA2dp(); + ChildInterface& getBtLe(); + BtProfileHandles getBtProfileManagerHandles(); ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr* _aidl_return) override; ndk::ScopedAStatus getBluetoothLe(std::shared_ptr* _aidl_return) override; @@ -44,11 +51,13 @@ class ModuleBluetooth final : public Module { const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& offloadInfo, std::shared_ptr* result) override; + ndk::ScopedAStatus populateConnectedDevicePort( + ::aidl::android::media::audio::common::AudioPort* audioPort) override; ndk::ScopedAStatus onMasterMuteChanged(bool mute) override; ndk::ScopedAStatus onMasterVolumeChanged(float volume) override; - ChildInterface mBluetoothA2dp; - ChildInterface mBluetoothLe; + ChildInterface mBluetoothA2dp; + ChildInterface mBluetoothLe; }; -} // namespace aidl::android::hardware::audio::core \ No newline at end of file +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/ModulePrimary.h b/audio/aidl/default/include/core-impl/ModulePrimary.h index 6264237fe3d1efdca5781bafd1d5dc642d562026..82c8a0366186546f3bd8381888658baf7133850d 100644 --- a/audio/aidl/default/include/core-impl/ModulePrimary.h +++ b/audio/aidl/default/include/core-impl/ModulePrimary.h @@ -22,7 +22,8 @@ namespace aidl::android::hardware::audio::core { class ModulePrimary final : public Module { public: - ModulePrimary() : Module(Type::DEFAULT) {} + ModulePrimary(std::unique_ptr&& config) + : Module(Type::DEFAULT, std::move(config)) {} protected: ndk::ScopedAStatus getTelephony(std::shared_ptr* _aidl_return) override; @@ -38,6 +39,8 @@ class ModulePrimary final : public Module { const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& offloadInfo, std::shared_ptr* result) override; + int32_t getNominalLatencyMs( + const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override; private: ChildInterface mTelephony; diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h index c4bf7b99c2187812bc37b31f5c0a66fc7c41baf1..9f8acc9e1d5d85166732c96d8b9bb6412a7f102d 100644 --- a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h +++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h @@ -22,7 +22,8 @@ namespace aidl::android::hardware::audio::core { class ModuleRemoteSubmix : public Module { public: - ModuleRemoteSubmix() : Module(Type::R_SUBMIX) {} + ModuleRemoteSubmix(std::unique_ptr&& config) + : Module(Type::R_SUBMIX, std::move(config)) {} private: // IModule interfaces @@ -49,6 +50,9 @@ class ModuleRemoteSubmix : public Module { override; ndk::ScopedAStatus onMasterMuteChanged(bool mute) override; ndk::ScopedAStatus onMasterVolumeChanged(float volume) override; + int32_t getNominalLatencyMs( + const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override; + // TODO(b/307586684): Report proper minimum stream buffer size by overriding 'setAudioPatch'. }; } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/ModuleStub.h b/audio/aidl/default/include/core-impl/ModuleStub.h index 4f771611ab7a5f59e7b252e046a2461672a2cac8..e9b7db428609cc1728765adbf0257d3d78723a3f 100644 --- a/audio/aidl/default/include/core-impl/ModuleStub.h +++ b/audio/aidl/default/include/core-impl/ModuleStub.h @@ -22,7 +22,7 @@ namespace aidl::android::hardware::audio::core { class ModuleStub final : public Module { public: - ModuleStub() : Module(Type::STUB) {} + ModuleStub(std::unique_ptr&& config) : Module(Type::STUB, std::move(config)) {} protected: ndk::ScopedAStatus getBluetooth(std::shared_ptr* _aidl_return) override; diff --git a/audio/aidl/default/include/core-impl/ModuleUsb.h b/audio/aidl/default/include/core-impl/ModuleUsb.h index a296b8c04249891c1bbfea6696eb622d31166a7b..6ee8f8a6913a88f6d98c50c08ced7cb7b8293b7e 100644 --- a/audio/aidl/default/include/core-impl/ModuleUsb.h +++ b/audio/aidl/default/include/core-impl/ModuleUsb.h @@ -22,7 +22,7 @@ namespace aidl::android::hardware::audio::core { class ModuleUsb final : public ModuleAlsa { public: - ModuleUsb() : ModuleAlsa(Type::USB) {} + ModuleUsb(std::unique_ptr&& config) : ModuleAlsa(Type::USB, std::move(config)) {} private: // IModule interfaces diff --git a/audio/aidl/default/include/core-impl/SoundDose.h b/audio/aidl/default/include/core-impl/SoundDose.h index 2a069d9bace5f166da579fbde4c600bafa679b0c..82c1077f1b97919dba093f16bc959b5b55c3cf2e 100644 --- a/audio/aidl/default/include/core-impl/SoundDose.h +++ b/audio/aidl/default/include/core-impl/SoundDose.h @@ -20,23 +20,68 @@ #include #include - -using aidl::android::media::audio::common::AudioDevice; +#include +#include +#include namespace aidl::android::hardware::audio::core::sounddose { -class SoundDose : public BnSoundDose { +// Interface used for processing the data received by a stream. +class StreamDataProcessorInterface { + public: + virtual ~StreamDataProcessorInterface() = default; + + virtual void startDataProcessor( + uint32_t samplerate, uint32_t channelCount, + const ::aidl::android::media::audio::common::AudioFormatDescription& format) = 0; + virtual void setAudioDevice( + const ::aidl::android::media::audio::common::AudioDevice& audioDevice) = 0; + virtual void process(const void* buffer, size_t size) = 0; +}; + +class SoundDose final : public BnSoundDose, public StreamDataProcessorInterface { public: - SoundDose() : mRs2Value(DEFAULT_MAX_RS2){}; + SoundDose() : mMelCallback(::android::sp::make(this)){}; + // -------------------------------------- BnSoundDose ------------------------------------------ ndk::ScopedAStatus setOutputRs2UpperBound(float in_rs2ValueDbA) override; ndk::ScopedAStatus getOutputRs2UpperBound(float* _aidl_return) override; ndk::ScopedAStatus registerSoundDoseCallback( const std::shared_ptr& in_callback) override; + // ----------------------------- StreamDataProcessorInterface ---------------------------------- + void setAudioDevice( + const ::aidl::android::media::audio::common::AudioDevice& audioDevice) override; + void startDataProcessor( + uint32_t samplerate, uint32_t channelCount, + const ::aidl::android::media::audio::common::AudioFormatDescription& format) override; + void process(const void* buffer, size_t size) override; + private: - std::shared_ptr mCallback; - float mRs2Value; + class MelCallback : public ::android::audio_utils::MelProcessor::MelCallback { + public: + explicit MelCallback(SoundDose* soundDose) : mSoundDose(*soundDose) {} + + // ------------------------------------ MelCallback ---------------------------------------- + void onNewMelValues(const std::vector& mels, size_t offset, size_t length, + audio_port_handle_t deviceId) const override; + void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override; + + SoundDose& mSoundDose; // must outlive MelCallback, not owning + }; + + void onNewMelValues(const std::vector& mels, size_t offset, size_t length, + audio_port_handle_t deviceId) const; + void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const; + + mutable ::android::audio_utils::mutex mCbMutex; + std::shared_ptr mCallback GUARDED_BY(mCbMutex); + std::optional<::aidl::android::media::audio::common::AudioDevice> mAudioDevice + GUARDED_BY(mCbMutex); + mutable ::android::audio_utils::mutex mMutex; + float mRs2Value GUARDED_BY(mMutex) = DEFAULT_MAX_RS2; + ::android::sp<::android::audio_utils::MelProcessor> mMelProcessor GUARDED_BY(mMutex); + ::android::sp mMelCallback GUARDED_BY(mMutex); }; } // namespace aidl::android::hardware::audio::core::sounddose diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h index 88fddec23316d63fad8514428dd2872031c6e61a..aa9fb19a5bc251be0ba5fa8aff0fcf4a7ee6db84 100644 --- a/audio/aidl/default/include/core-impl/Stream.h +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -44,6 +44,7 @@ #include #include "core-impl/ChildInterface.h" +#include "core-impl/SoundDose.h" #include "core-impl/utils.h" namespace aidl::android::hardware::audio::core { @@ -80,59 +81,28 @@ class StreamContext { StreamContext() = default; StreamContext(std::unique_ptr commandMQ, std::unique_ptr replyMQ, - int portId, const ::aidl::android::media::audio::common::AudioFormatDescription& format, const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout, int sampleRate, const ::aidl::android::media::audio::common::AudioIoFlags& flags, - int32_t mixPortHandle, std::unique_ptr dataMQ, + int32_t nominalLatencyMs, int32_t mixPortHandle, std::unique_ptr dataMQ, std::shared_ptr asyncCallback, std::shared_ptr outEventCallback, + std::weak_ptr streamDataProcessor, DebugParameters debugParameters) : mCommandMQ(std::move(commandMQ)), mInternalCommandCookie(std::rand()), mReplyMQ(std::move(replyMQ)), - mPortId(portId), mFormat(format), mChannelLayout(channelLayout), mSampleRate(sampleRate), mFlags(flags), + mNominalLatencyMs(nominalLatencyMs), mMixPortHandle(mixPortHandle), mDataMQ(std::move(dataMQ)), mAsyncCallback(asyncCallback), mOutEventCallback(outEventCallback), + mStreamDataProcessor(streamDataProcessor), mDebugParameters(debugParameters) {} - StreamContext(StreamContext&& other) - : mCommandMQ(std::move(other.mCommandMQ)), - mInternalCommandCookie(other.mInternalCommandCookie), - mReplyMQ(std::move(other.mReplyMQ)), - mPortId(other.mPortId), - mFormat(other.mFormat), - mChannelLayout(other.mChannelLayout), - mSampleRate(other.mSampleRate), - mFlags(std::move(other.mFlags)), - mMixPortHandle(other.mMixPortHandle), - mDataMQ(std::move(other.mDataMQ)), - mAsyncCallback(std::move(other.mAsyncCallback)), - mOutEventCallback(std::move(other.mOutEventCallback)), - mDebugParameters(std::move(other.mDebugParameters)), - mFrameCount(other.mFrameCount) {} - StreamContext& operator=(StreamContext&& other) { - mCommandMQ = std::move(other.mCommandMQ); - mInternalCommandCookie = other.mInternalCommandCookie; - mReplyMQ = std::move(other.mReplyMQ); - mPortId = std::move(other.mPortId); - mFormat = std::move(other.mFormat); - mChannelLayout = std::move(other.mChannelLayout); - mSampleRate = other.mSampleRate; - mFlags = std::move(other.mFlags); - mMixPortHandle = other.mMixPortHandle; - mDataMQ = std::move(other.mDataMQ); - mAsyncCallback = std::move(other.mAsyncCallback); - mOutEventCallback = std::move(other.mOutEventCallback); - mDebugParameters = std::move(other.mDebugParameters); - mFrameCount = other.mFrameCount; - return *this; - } void fillDescriptor(StreamDescriptor* desc); std::shared_ptr getAsyncCallback() const { return mAsyncCallback; } @@ -151,10 +121,14 @@ class StreamContext { size_t getFrameSize() const; int getInternalCommandCookie() const { return mInternalCommandCookie; } int32_t getMixPortHandle() const { return mMixPortHandle; } + int32_t getNominalLatencyMs() const { return mNominalLatencyMs; } std::shared_ptr getOutEventCallback() const { return mOutEventCallback; } - int getPortId() const { return mPortId; } + std::weak_ptr getStreamDataProcessor() const { + return mStreamDataProcessor; + } + void startStreamDataProcessor(); ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); } int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; } int getSampleRate() const { return mSampleRate; } @@ -167,18 +141,20 @@ class StreamContext { long getFrameCount() const { return mFrameCount; } private: + // Fields are non const to allow move assignment. std::unique_ptr mCommandMQ; int mInternalCommandCookie; // The value used to confirm that the command was posted internally std::unique_ptr mReplyMQ; - int mPortId; ::aidl::android::media::audio::common::AudioFormatDescription mFormat; ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout; int mSampleRate; ::aidl::android::media::audio::common::AudioIoFlags mFlags; + int32_t mNominalLatencyMs; int32_t mMixPortHandle; std::unique_ptr mDataMQ; std::shared_ptr mAsyncCallback; std::shared_ptr mOutEventCallback; // Only used by output streams + std::weak_ptr mStreamDataProcessor; DebugParameters mDebugParameters; long mFrameCount = 0; }; diff --git a/audio/aidl/default/include/core-impl/StreamBluetooth.h b/audio/aidl/default/include/core-impl/StreamBluetooth.h index c2f8c1d75bf376e25d845fbd8e7fbbdf3d1507c5..1258d3860c024ee1bd23a9b910b2780739255919 100644 --- a/audio/aidl/default/include/core-impl/StreamBluetooth.h +++ b/audio/aidl/default/include/core-impl/StreamBluetooth.h @@ -24,7 +24,7 @@ #include #include "core-impl/DevicePortProxy.h" -#include "core-impl/Module.h" +#include "core-impl/ModuleBluetooth.h" #include "core-impl/Stream.h" namespace aidl::android::hardware::audio::core { @@ -32,7 +32,7 @@ namespace aidl::android::hardware::audio::core { class StreamBluetooth : public StreamCommonImpl { public: StreamBluetooth(StreamContext* context, const Metadata& metadata, - Module::BtProfileHandles&& btHandles); + ModuleBluetooth::BtProfileHandles&& btHandles); // Methods of 'DriverInterface'. ::android::status_t init() override; ::android::status_t drain(StreamDescriptor::DrainMode) override; @@ -80,7 +80,7 @@ class StreamInBluetooth final : public StreamIn, public StreamBluetooth { StreamContext&& context, const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata, const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones, - Module::BtProfileHandles&& btHandles); + ModuleBluetooth::BtProfileHandles&& btHandles); private: void onClose(StreamDescriptor::State) override { defaultOnClose(); } @@ -97,7 +97,7 @@ class StreamOutBluetooth final : public StreamOut, public StreamBluetooth { const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata, const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& offloadInfo, - Module::BtProfileHandles&& btHandles); + ModuleBluetooth::BtProfileHandles&& btHandles); private: void onClose(StreamDescriptor::State) override { defaultOnClose(); } diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h index b3ddd0bd5335148557927f9b790bf43b787edf99..145c3c44cff17eb8c9c61946c9c5881ad442a898 100644 --- a/audio/aidl/default/include/core-impl/StreamPrimary.h +++ b/audio/aidl/default/include/core-impl/StreamPrimary.h @@ -27,10 +27,18 @@ class StreamPrimary : public StreamAlsa { public: StreamPrimary(StreamContext* context, const Metadata& metadata); + ::android::status_t start() override; + ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, + int32_t* latencyMs) override; + ::android::status_t refinePosition(StreamDescriptor::Position* position) override; + protected: std::vector getDeviceProfiles() override; - const bool mIsInput; + const bool mIsAsynchronous; + long mStartTimeNs = 0; + long mFramesSinceStart = 0; + bool mSkipNextTransfer = false; }; class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper { @@ -79,6 +87,10 @@ class StreamOutPrimary final : public StreamOut, ndk::ScopedAStatus getHwVolume(std::vector* _aidl_return) override; ndk::ScopedAStatus setHwVolume(const std::vector& in_channelVolumes) override; + + ndk::ScopedAStatus setConnectedDevices( + const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) + override; }; } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h index 94404a1c7ee4fb35731c988cdb980c3477c698bf..ee10abf0876cb29e919736645d38023639604ba7 100644 --- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h +++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h @@ -46,7 +46,7 @@ class StreamRemoteSubmix : public StreamCommonImpl { ndk::ScopedAStatus prepareToClose() override; private: - size_t getPipeSizeInFrames(); + long getDelayInUsForFrameCount(size_t frameCount); size_t getStreamPipeSizeInFrames(); ::android::status_t outWrite(void* buffer, size_t frameCount, size_t* actualFrameCount); ::android::status_t inRead(void* buffer, size_t frameCount, size_t* actualFrameCount); @@ -71,6 +71,10 @@ class StreamRemoteSubmix : public StreamCommonImpl { static constexpr int kMaxReadFailureAttempts = 3; // 5ms between two read attempts when pipe is empty static constexpr int kReadAttemptSleepUs = 5000; + + long mStartTimeNs = 0; + long mFramesSinceStart = 0; + int mReadErrorCount = 0; }; class StreamInRemoteSubmix final : public StreamIn, public StreamSwitcher { diff --git a/audio/aidl/default/include/core-impl/XmlConverter.h b/audio/aidl/default/include/core-impl/XmlConverter.h index 383ea248361f602c5b76aa6d2d712b2b56b4573d..68e6b8e3a3cc8cc969db9c265f4ed8f591d246ab 100644 --- a/audio/aidl/default/include/core-impl/XmlConverter.h +++ b/audio/aidl/default/include/core-impl/XmlConverter.h @@ -22,7 +22,6 @@ #include #include -#include namespace aidl::android::hardware::audio::core::internal { @@ -85,10 +84,10 @@ class XmlConverter { * */ template -std::vector convertWrappedCollectionToAidlUnchecked( +static ConversionResult> convertWrappedCollectionToAidl( const std::vector& xsdcWrapperTypeVec, std::function&(const W&)> getInnerTypeVec, - std::function convertToAidl) { + std::function(const X&)> convertToAidl) { std::vector resultAidlTypeVec; if (!xsdcWrapperTypeVec.empty()) { /* @@ -98,21 +97,23 @@ std::vector convertWrappedCollectionToAidlUnchecked( */ resultAidlTypeVec.reserve(getInnerTypeVec(xsdcWrapperTypeVec[0]).size()); for (const W& xsdcWrapperType : xsdcWrapperTypeVec) { - std::transform(getInnerTypeVec(xsdcWrapperType).begin(), - getInnerTypeVec(xsdcWrapperType).end(), - std::back_inserter(resultAidlTypeVec), convertToAidl); + for (const X& xsdcType : getInnerTypeVec(xsdcWrapperType)) { + resultAidlTypeVec.push_back(VALUE_OR_FATAL(convertToAidl(xsdcType))); + } } } return resultAidlTypeVec; } template -std::vector convertCollectionToAidlUnchecked(const std::vector& xsdcTypeVec, - std::function itemConversion) { +static ConversionResult> convertCollectionToAidl( + const std::vector& xsdcTypeVec, + std::function(const X&)> convertToAidl) { std::vector resultAidlTypeVec; resultAidlTypeVec.reserve(xsdcTypeVec.size()); - std::transform(xsdcTypeVec.begin(), xsdcTypeVec.end(), std::back_inserter(resultAidlTypeVec), - itemConversion); + for (const X& xsdcType : xsdcTypeVec) { + resultAidlTypeVec.push_back(VALUE_OR_FATAL(convertToAidl(xsdcType))); + } return resultAidlTypeVec; } diff --git a/audio/aidl/default/include/core-impl/XsdcConversion.h b/audio/aidl/default/include/core-impl/XsdcConversion.h new file mode 100644 index 0000000000000000000000000000000000000000..30dc8b635be0f7fde6d3e97d6a1572541d3d01bc --- /dev/null +++ b/audio/aidl/default/include/core-impl/XsdcConversion.h @@ -0,0 +1,29 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core-impl/Module.h" + +namespace aidl::android::hardware::audio::core::internal { + +ConversionResult<::aidl::android::media::audio::common::AudioHalCapCriterion> +convertCapCriterionToAidl( + const ::android::audio::policy::engine::configuration::CriterionType& xsdcCriterion); +ConversionResult<::aidl::android::media::audio::common::AudioHalCapCriterionType> +convertCapCriterionTypeToAidl( + const ::android::audio::policy::engine::configuration::CriterionTypeType& + xsdcCriterionType); +ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeCurve::CurvePoint> +convertCurvePointToAidl(const std::string& xsdcCurvePoint); +ConversionResult> convertModuleConfigToAidl( + const ::android::audio::policy::configuration::Modules::Module& moduleConfig); +} // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp index a0c0fab40a33beea1492d650abdd95e8c7b3432a..6ab747db32b8df9b6b14cb3a2f099df1793dcaf7 100644 --- a/audio/aidl/default/main.cpp +++ b/audio/aidl/default/main.cpp @@ -16,21 +16,51 @@ #include #include -#include #include #include +#define LOG_TAG "AHAL_Main" #include #include #include #include #include +#include "core-impl/AudioPolicyConfigXmlConverter.h" +#include "core-impl/ChildInterface.h" #include "core-impl/Config.h" #include "core-impl/Module.h" +using aidl::android::hardware::audio::core::ChildInterface; using aidl::android::hardware::audio::core::Config; using aidl::android::hardware::audio::core::Module; +using aidl::android::hardware::audio::core::internal::AudioPolicyConfigXmlConverter; + +namespace { + +ChildInterface createModule(const std::string& name, + std::unique_ptr&& config) { + ChildInterface result; + { + auto moduleType = Module::typeFromString(name); + if (!moduleType.has_value()) { + LOG(ERROR) << __func__ << ": module type \"" << name << "\" is not supported"; + return result; + } + auto module = Module::createInstance(*moduleType, std::move(config)); + if (module == nullptr) return result; + result = std::move(module); + } + const std::string moduleFqn = std::string().append(Module::descriptor).append("/").append(name); + binder_status_t status = AServiceManager_addService(result.getBinder(), moduleFqn.c_str()); + if (status != STATUS_OK) { + LOG(ERROR) << __func__ << ": failed to register service for \"" << moduleFqn << "\""; + return ChildInterface(); + } + return result; +}; + +} // namespace int main() { // Random values are used in the implementation. @@ -45,29 +75,27 @@ int main() { // Guaranteed log for b/210919187 and logd_integration_test LOG(INFO) << "Init for Audio AIDL HAL"; + AudioPolicyConfigXmlConverter audioPolicyConverter{ + ::android::audio_get_audio_policy_config_file()}; + // Make the default config service - auto config = ndk::SharedRefBase::make(); - const std::string configName = std::string() + Config::descriptor + "/default"; + auto config = ndk::SharedRefBase::make(audioPolicyConverter); + const std::string configFqn = std::string().append(Config::descriptor).append("/default"); binder_status_t status = - AServiceManager_addService(config->asBinder().get(), configName.c_str()); - CHECK_EQ(STATUS_OK, status); + AServiceManager_addService(config->asBinder().get(), configFqn.c_str()); + if (status != STATUS_OK) { + LOG(ERROR) << "failed to register service for \"" << configFqn << "\""; + } // Make modules - auto createModule = [](Module::Type type) { - auto module = Module::createInstance(type); - ndk::SpAIBinder moduleBinder = module->asBinder(); - std::stringstream moduleName; - moduleName << Module::descriptor << "/" << type; - AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO); - binder_status_t status = - AServiceManager_addService(moduleBinder.get(), moduleName.str().c_str()); - CHECK_EQ(STATUS_OK, status); - return std::make_pair(module, moduleBinder); - }; - auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX), - createModule(Module::Type::USB), createModule(Module::Type::STUB), - createModule(Module::Type::BLUETOOTH)}; - (void)modules; + std::vector> moduleInstances; + auto configs(audioPolicyConverter.releaseModuleConfigs()); + for (std::pair>& configPair : *configs) { + std::string name = configPair.first; + if (auto instance = createModule(name, std::move(configPair.second)); instance) { + moduleInstances.push_back(std::move(instance)); + } + } ABinderProcess_joinThreadPool(); return EXIT_FAILURE; // should not reach diff --git a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp index 9b2cb7ca6c7782e2bd1ae941386f118d9c34dde6..a3208df8651ad5f6a527a715f41cca0e22054f7a 100644 --- a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp +++ b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp @@ -65,9 +65,9 @@ const Descriptor NoiseSuppressionSw::kDescriptor = { .common = {.id = {.type = getEffectTypeUuidNoiseSuppression(), .uuid = getEffectImplUuidNoiseSuppressionSw(), .proxy = std::nullopt}, - .flags = {.type = Flags::Type::INSERT, + .flags = {.type = Flags::Type::PRE_PROC, .insert = Flags::Insert::FIRST, - .volume = Flags::Volume::CTRL}, + .volume = Flags::Volume::NONE}, .name = NoiseSuppressionSw::kEffectName, .implementor = "The Android Open Source Project"}}; diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp index e01be8a3c635e9c4f6393a3d81aea17e3bddc08f..7325a91b49bf99d5a6e78dacf7106c4e43b8bdff 100644 --- a/audio/aidl/default/primary/StreamPrimary.cpp +++ b/audio/aidl/default/primary/StreamPrimary.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include - #define LOG_TAG "AHAL_StreamPrimary" #include #include +#include +#include #include #include "PrimaryMixer.h" @@ -37,7 +37,59 @@ using android::base::GetBoolProperty; namespace aidl::android::hardware::audio::core { StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata) - : StreamAlsa(context, metadata, 3 /*readWriteRetries*/), mIsInput(isInput(metadata)) {} + : StreamAlsa(context, metadata, 3 /*readWriteRetries*/), + mIsAsynchronous(!!getContext().getAsyncCallback()) { + context->startStreamDataProcessor(); +} + +::android::status_t StreamPrimary::start() { + RETURN_STATUS_IF_ERROR(StreamAlsa::start()); + mStartTimeNs = ::android::uptimeNanos(); + mFramesSinceStart = 0; + mSkipNextTransfer = false; + return ::android::OK; +} + +::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount, + size_t* actualFrameCount, int32_t* latencyMs) { + // This is a workaround for the emulator implementation which has a host-side buffer + // and is not being able to achieve real-time behavior similar to ADSPs (b/302587331). + if (!mSkipNextTransfer) { + RETURN_STATUS_IF_ERROR( + StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs)); + } else { + LOG(DEBUG) << __func__ << ": skipping transfer (" << frameCount << " frames)"; + *actualFrameCount = frameCount; + if (mIsInput) memset(buffer, 0, frameCount * mFrameSizeBytes); + mSkipNextTransfer = false; + } + if (!mIsAsynchronous) { + const long bufferDurationUs = + (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate(); + const auto totalDurationUs = + (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND; + mFramesSinceStart += *actualFrameCount; + const long totalOffsetUs = + mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs; + LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs; + if (totalOffsetUs > 0) { + const long sleepTimeUs = std::min(totalOffsetUs, bufferDurationUs); + LOG(VERBOSE) << __func__ << ": sleeping for " << sleepTimeUs << " us"; + usleep(sleepTimeUs); + } else { + mSkipNextTransfer = true; + } + } else { + LOG(VERBOSE) << __func__ << ": asynchronous transfer"; + } + return ::android::OK; +} + +::android::status_t StreamPrimary::refinePosition(StreamDescriptor::Position*) { + // Since not all data is actually sent to the HAL, use the position maintained by Stream class + // which accounts for all frames passed from / to the client. + return ::android::OK; +} std::vector StreamPrimary::getDeviceProfiles() { static const std::vector kBuiltInSource{ @@ -64,7 +116,8 @@ bool StreamInPrimary::useStubStream(const AudioDevice& device) { GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false); return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX || device.type.type == AudioDeviceType::IN_FM_TUNER || - device.type.connection == AudioDeviceDescription::CONNECTION_BUS; + device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated */ || + (device.type.type == AudioDeviceType::IN_BUS && device.type.connection.empty()); } StreamSwitcher::DeviceSwitchBehavior StreamInPrimary::switchCurrentStream( @@ -99,6 +152,11 @@ ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector* _aidl_return) if (isStubStream()) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } + float gain; + RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getMicGain(&gain)); + _aidl_return->resize(0); + _aidl_return->resize(mChannelCount, gain); + RETURN_STATUS_IF_ERROR(setHwGainImpl(*_aidl_return)); return getHwGainImpl(_aidl_return); } @@ -130,7 +188,8 @@ bool StreamOutPrimary::useStubStream(const AudioDevice& device) { static const bool kSimulateOutput = GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false); return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX || - device.type.connection == AudioDeviceDescription::CONNECTION_BUS; + device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated*/ || + (device.type.type == AudioDeviceType::OUT_BUS && device.type.connection.empty()); } StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream( @@ -165,6 +224,9 @@ ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector* _aidl_retur if (isStubStream()) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } + RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(_aidl_return)); + _aidl_return->resize(mChannelCount); + RETURN_STATUS_IF_ERROR(setHwVolumeImpl(*_aidl_return)); return getHwVolumeImpl(_aidl_return); } @@ -183,4 +245,15 @@ ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector& in_ch return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices( + const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) { + if (!devices.empty()) { + auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock(); + if (streamDataProcessor != nullptr) { + streamDataProcessor->setAudioDevice(devices[0]); + } + } + return StreamSwitcher::setConnectedDevices(devices); +} + } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp index adea877be6e229c86ee61eed5cf2e7919c397ad3..2f4288976ff6a86151ac42189a2dfd5773aa52d9 100644 --- a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp +++ b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp @@ -21,6 +21,7 @@ #include #include +#include "SubmixRoute.h" #include "core-impl/ModuleRemoteSubmix.h" #include "core-impl/StreamRemoteSubmix.h" @@ -59,23 +60,22 @@ ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream( ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort) { // Find the corresponding mix port and copy its profiles. - std::vector routes; // At this moment, the port has the same ID as the template port, see connectExternalDevice. - RETURN_STATUS_IF_ERROR(getAudioRoutesForAudioPort(audioPort->id, &routes)); + std::vector routes = getAudioRoutesForAudioPortImpl(audioPort->id); if (routes.empty()) { LOG(ERROR) << __func__ << ": no routes found for the port " << audioPort->toString(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } const auto& route = *routes.begin(); AudioPort mixPort; - if (route.sinkPortId == audioPort->id) { - if (route.sourcePortIds.empty()) { - LOG(ERROR) << __func__ << ": invalid route " << route.toString(); + if (route->sinkPortId == audioPort->id) { + if (route->sourcePortIds.empty()) { + LOG(ERROR) << __func__ << ": invalid route " << route->toString(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - RETURN_STATUS_IF_ERROR(getAudioPort(*route.sourcePortIds.begin(), &mixPort)); + RETURN_STATUS_IF_ERROR(getAudioPort(*route->sourcePortIds.begin(), &mixPort)); } else { - RETURN_STATUS_IF_ERROR(getAudioPort(route.sinkPortId, &mixPort)); + RETURN_STATUS_IF_ERROR(getAudioPort(route->sinkPortId, &mixPort)); } audioPort->profiles = mixPort.profiles; return ndk::ScopedAStatus::ok(); @@ -107,4 +107,12 @@ ndk::ScopedAStatus ModuleRemoteSubmix::onMasterVolumeChanged(float __unused) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } +int32_t ModuleRemoteSubmix::getNominalLatencyMs(const AudioPortConfig&) { + // See the note on kDefaultPipePeriodCount. + static constexpr int32_t kMaxLatencyMs = + (r_submix::kDefaultPipeSizeInFrames * 1000) / r_submix::kDefaultSampleRateHz; + static constexpr int32_t kMinLatencyMs = kMaxLatencyMs / r_submix::kDefaultPipePeriodCount; + return kMinLatencyMs; +} + } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp index 9c9c08b04801ba88d75e156e57b236fad8cb439f..d238aa42b30dd799ba03ab64eb02eed25cf9976d 100644 --- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp +++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp @@ -16,8 +16,9 @@ #define LOG_TAG "AHAL_StreamRemoteSubmix" #include - -#include +#include +#include +#include #include "core-impl/StreamRemoteSubmix.h" @@ -52,38 +53,32 @@ std::map> StreamRemoteSubmix::s if (routeItr != sSubmixRoutes.end()) { mCurrentRoute = routeItr->second; } - } - // If route is not available for this port, add it. - if (mCurrentRoute == nullptr) { - // Initialize the pipe. - mCurrentRoute = std::make_shared(); - if (::android::OK != mCurrentRoute->createPipe(mStreamConfig)) { - LOG(ERROR) << __func__ << ": create pipe failed"; - return ::android::NO_INIT; - } - { - std::lock_guard guard(sSubmixRoutesLock); + // If route is not available for this port, add it. + if (mCurrentRoute == nullptr) { + // Initialize the pipe. + mCurrentRoute = std::make_shared(); + if (::android::OK != mCurrentRoute->createPipe(mStreamConfig)) { + LOG(ERROR) << __func__ << ": create pipe failed"; + return ::android::NO_INIT; + } sSubmixRoutes.emplace(mDeviceAddress, mCurrentRoute); } - } else { - if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) { - LOG(ERROR) << __func__ << ": invalid stream config"; - return ::android::NO_INIT; - } - sp sink = mCurrentRoute->getSink(); - if (sink == nullptr) { - LOG(ERROR) << __func__ << ": nullptr sink when opening stream"; + } + if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) { + LOG(ERROR) << __func__ << ": invalid stream config"; + return ::android::NO_INIT; + } + sp sink = mCurrentRoute->getSink(); + if (sink == nullptr) { + LOG(ERROR) << __func__ << ": nullptr sink when opening stream"; + return ::android::NO_INIT; + } + if ((!mIsInput || mCurrentRoute->isStreamInOpen()) && sink->isShutdown()) { + LOG(DEBUG) << __func__ << ": Shut down sink when opening stream"; + if (::android::OK != mCurrentRoute->resetPipe()) { + LOG(ERROR) << __func__ << ": reset pipe failed"; return ::android::NO_INIT; } - // If the sink has been shutdown or pipe recreation is forced, delete the pipe and - // recreate it. - if (sink->isShutdown()) { - LOG(DEBUG) << __func__ << ": Non-nullptr shut down sink when opening stream"; - if (::android::OK != mCurrentRoute->resetPipe()) { - LOG(ERROR) << __func__ << ": reset pipe failed"; - return ::android::NO_INIT; - } - } } mCurrentRoute->openStream(mIsInput); @@ -112,6 +107,8 @@ std::map> StreamRemoteSubmix::s ::android::status_t StreamRemoteSubmix::start() { mCurrentRoute->exitStandby(mIsInput); + mStartTimeNs = ::android::uptimeNanos(); + mFramesSinceStart = 0; return ::android::OK; } @@ -133,6 +130,8 @@ ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() { LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink"; sink->shutdown(true); + // The client already considers this stream as closed, release the output end. + route->closeStream(mIsInput); } else { LOG(DEBUG) << __func__ << ": stream already closed."; ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); @@ -158,30 +157,24 @@ void StreamRemoteSubmix::shutdown() { ::android::status_t StreamRemoteSubmix::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) { - *latencyMs = (getStreamPipeSizeInFrames() * MILLIS_PER_SECOND) / mStreamConfig.sampleRate; + *latencyMs = getDelayInUsForFrameCount(getStreamPipeSizeInFrames()) / 1000; LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms"; - - sp sink = mCurrentRoute->getSink(); - if (sink != nullptr) { - if (sink->isShutdown()) { - sink.clear(); - LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the transfer."; - // the pipe has already been shutdown, this buffer will be lost but we must simulate - // timing so we don't drain the output faster than realtime - const size_t delayUs = static_cast( - std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate)); - usleep(delayUs); - - *actualFrameCount = frameCount; - return ::android::OK; - } - } else { - LOG(ERROR) << __func__ << ": transfer without a pipe!"; - return ::android::UNEXPECTED_NULL; - } mCurrentRoute->exitStandby(mIsInput); - return (mIsInput ? inRead(buffer, frameCount, actualFrameCount) - : outWrite(buffer, frameCount, actualFrameCount)); + RETURN_STATUS_IF_ERROR(mIsInput ? inRead(buffer, frameCount, actualFrameCount) + : outWrite(buffer, frameCount, actualFrameCount)); + const long bufferDurationUs = + (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate(); + const long totalDurationUs = (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND; + mFramesSinceStart += *actualFrameCount; + const long totalOffsetUs = + mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs; + LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs; + if (totalOffsetUs > 0) { + const long sleepTimeUs = std::min(totalOffsetUs, bufferDurationUs); + LOG(VERBOSE) << __func__ << ": sleeping for " << sleepTimeUs << " us"; + usleep(sleepTimeUs); + } + return ::android::OK; } ::android::status_t StreamRemoteSubmix::refinePosition(StreamDescriptor::Position* position) { @@ -202,6 +195,10 @@ void StreamRemoteSubmix::shutdown() { return ::android::OK; } +long StreamRemoteSubmix::getDelayInUsForFrameCount(size_t frameCount) { + return frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate; +} + // Calculate the maximum size of the pipe buffer in frames for the specified stream. size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { auto pipeConfig = mCurrentRoute->mPipeConfig; @@ -215,12 +212,7 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { if (sink != nullptr) { if (sink->isShutdown()) { sink.clear(); - LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the write."; - // the pipe has already been shutdown, this buffer will be lost but we must - // simulate timing so we don't drain the output faster than realtime - const size_t delayUs = static_cast( - std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate)); - usleep(delayUs); + LOG(DEBUG) << __func__ << ": pipe shutdown, ignoring the write"; *actualFrameCount = frameCount; return ::android::OK; } @@ -229,17 +221,21 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { return ::android::UNKNOWN_ERROR; } - const size_t availableToWrite = sink->availableToWrite(); + LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount + << " frames"; + + const bool shouldBlockWrite = mCurrentRoute->shouldBlockWrite(); + size_t availableToWrite = sink->availableToWrite(); // NOTE: sink has been checked above and sink and source life cycles are synchronized sp source = mCurrentRoute->getSource(); // If the write to the sink should be blocked, flush enough frames from the pipe to make space // to write the most recent data. - if (!mCurrentRoute->shouldBlockWrite() && availableToWrite < frameCount) { + if (!shouldBlockWrite && availableToWrite < frameCount) { static uint8_t flushBuffer[64]; const size_t flushBufferSizeFrames = sizeof(flushBuffer) / mStreamConfig.frameSize; size_t framesToFlushFromSource = frameCount - availableToWrite; - LOG(VERBOSE) << __func__ << ": flushing " << framesToFlushFromSource - << " frames from the pipe to avoid blocking"; + LOG(DEBUG) << __func__ << ": flushing " << framesToFlushFromSource + << " frames from the pipe to avoid blocking"; while (framesToFlushFromSource) { const size_t flushSize = std::min(framesToFlushFromSource, flushBufferSizeFrames); framesToFlushFromSource -= flushSize; @@ -247,7 +243,14 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { source->read(flushBuffer, flushSize); } } + availableToWrite = sink->availableToWrite(); + if (!shouldBlockWrite && frameCount > availableToWrite) { + LOG(WARNING) << __func__ << ": writing " << availableToWrite << " vs. requested " + << frameCount; + // Truncate the request to avoid blocking. + frameCount = availableToWrite; + } ssize_t writtenFrames = sink->write(buffer, frameCount); if (writtenFrames < 0) { if (writtenFrames == (ssize_t)::android::NEGOTIATE) { @@ -261,97 +264,65 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { writtenFrames = sink->write(buffer, frameCount); } } - sink.clear(); if (writtenFrames < 0) { LOG(ERROR) << __func__ << ": failed writing to pipe with " << writtenFrames; *actualFrameCount = 0; return ::android::UNKNOWN_ERROR; } - LOG(VERBOSE) << __func__ << ": wrote " << writtenFrames << "frames"; + if (writtenFrames > 0 && frameCount > (size_t)writtenFrames) { + LOG(WARNING) << __func__ << ": wrote " << writtenFrames << " vs. requested " << frameCount; + } *actualFrameCount = writtenFrames; return ::android::OK; } ::android::status_t StreamRemoteSubmix::inRead(void* buffer, size_t frameCount, size_t* actualFrameCount) { + // in any case, it is emulated that data for the entire buffer was available + memset(buffer, 0, mStreamConfig.frameSize * frameCount); + *actualFrameCount = frameCount; + // about to read from audio source sp source = mCurrentRoute->getSource(); if (source == nullptr) { - int readErrorCount = mCurrentRoute->notifyReadError(); - if (readErrorCount < kMaxReadErrorLogs) { + if (++mReadErrorCount < kMaxReadErrorLogs) { LOG(ERROR) << __func__ << ": no audio pipe yet we're trying to read! (not all errors will be " "logged)"; - } else { - LOG(ERROR) << __func__ << ": Read errors " << readErrorCount; } - const size_t delayUs = static_cast( - std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate)); - usleep(delayUs); - memset(buffer, 0, mStreamConfig.frameSize * frameCount); - *actualFrameCount = frameCount; return ::android::OK; } + LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount + << " frames"; // read the data from the pipe - int attempts = 0; - const size_t delayUs = static_cast(std::roundf(kReadAttemptSleepUs)); char* buff = (char*)buffer; - size_t remainingFrames = frameCount; - int availableToRead = source->availableToRead(); - - while ((remainingFrames > 0) && (availableToRead > 0) && (attempts < kMaxReadFailureAttempts)) { - LOG(VERBOSE) << __func__ << ": frames available to read " << availableToRead; - + size_t actuallyRead = 0; + long remainingFrames = frameCount; + const long deadlineTimeNs = ::android::uptimeNanos() + + getDelayInUsForFrameCount(frameCount) * NANOS_PER_MICROSECOND; + while (remainingFrames > 0) { ssize_t framesRead = source->read(buff, remainingFrames); - LOG(VERBOSE) << __func__ << ": frames read " << framesRead; - if (framesRead > 0) { remainingFrames -= framesRead; buff += framesRead * mStreamConfig.frameSize; - availableToRead -= framesRead; - LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead - << " frames, remaining=" << remainingFrames; - } else { - attempts++; - LOG(WARNING) << __func__ << ": read returned " << framesRead - << " , read failure attempts = " << attempts; - usleep(delayUs); + LOG(VERBOSE) << __func__ << ": got " << framesRead + << " frames, remaining =" << remainingFrames; + actuallyRead += framesRead; + } + if (::android::uptimeNanos() >= deadlineTimeNs) break; + if (framesRead <= 0) { + LOG(VERBOSE) << __func__ << ": read returned " << framesRead + << ", read failure, sleeping for " << kReadAttemptSleepUs << " us"; + usleep(kReadAttemptSleepUs); } } - // done using the source - source.clear(); - - if (remainingFrames > 0) { - const size_t remainingBytes = remainingFrames * mStreamConfig.frameSize; - LOG(VERBOSE) << __func__ << ": clearing remaining_frames = " << remainingFrames; - memset(((char*)buffer) + (mStreamConfig.frameSize * frameCount) - remainingBytes, 0, - remainingBytes); - } - - long readCounterFrames = mCurrentRoute->updateReadCounterFrames(frameCount); - *actualFrameCount = frameCount; - - // compute how much we need to sleep after reading the data by comparing the wall clock with - // the projected time at which we should return. - // wall clock after reading from the pipe - auto recordDurationUs = std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime(); - - // readCounterFrames contains the number of frames that have been read since the beginning of - // recording (including this call): it's converted to usec and compared to how long we've been - // recording for, which gives us how long we must wait to sync the projected recording time, and - // the observed recording time. - const int projectedVsObservedOffsetUs = - std::roundf((readCounterFrames * MICROS_PER_SECOND / mStreamConfig.sampleRate) - - recordDurationUs.count()); - - LOG(VERBOSE) << __func__ << ": record duration " << recordDurationUs.count() - << " microseconds, will wait: " << projectedVsObservedOffsetUs << " microseconds"; - if (projectedVsObservedOffsetUs > 0) { - usleep(projectedVsObservedOffsetUs); + if (actuallyRead < frameCount) { + LOG(WARNING) << __func__ << ": read " << actuallyRead << " vs. requested " << frameCount; } + mCurrentRoute->updateReadCounterFrames(*actualFrameCount); return ::android::OK; } diff --git a/audio/aidl/default/r_submix/SubmixRoute.cpp b/audio/aidl/default/r_submix/SubmixRoute.cpp index ddac64d38768b499084daabfc15966782c65c6cb..235c9a3f32bf4da27272c94c5ff9ab837922ad69 100644 --- a/audio/aidl/default/r_submix/SubmixRoute.cpp +++ b/audio/aidl/default/r_submix/SubmixRoute.cpp @@ -81,11 +81,6 @@ bool SubmixRoute::shouldBlockWrite() { return (mStreamInOpen || (mStreamInStandby && (mReadCounterFrames != 0))); } -int SubmixRoute::notifyReadError() { - std::lock_guard guard(mLock); - return ++mReadErrorCount; -} - long SubmixRoute::updateReadCounterFrames(size_t frameCount) { std::lock_guard guard(mLock); mReadCounterFrames += frameCount; @@ -103,7 +98,9 @@ void SubmixRoute::openStream(bool isInput) { } mStreamInStandby = true; mReadCounterFrames = 0; - mReadErrorCount = 0; + if (mSink != nullptr) { + mSink->shutdown(false); + } } else { mStreamOutOpen = true; } @@ -112,8 +109,7 @@ void SubmixRoute::openStream(bool isInput) { void SubmixRoute::closeStream(bool isInput) { std::lock_guard guard(mLock); if (isInput) { - mInputRefCount--; - if (mInputRefCount == 0) { + if (--mInputRefCount == 0) { mStreamInOpen = false; if (mSink != nullptr) { mSink->shutdown(true); @@ -214,9 +210,6 @@ void SubmixRoute::exitStandby(bool isInput) { if (mStreamInStandby || mStreamOutStandbyTransition) { mStreamInStandby = false; mStreamOutStandbyTransition = false; - // keep track of when we exit input standby (== first read == start "real recording") - // or when we start recording silence, and reset projected time - mRecordStartTime = std::chrono::steady_clock::now(); mReadCounterFrames = 0; } } else { diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h index 1a98df234066046c8564b2fddb3a15be0e6c9834..252b1c9524882fcbc73093d40f401fe885038673 100644 --- a/audio/aidl/default/r_submix/SubmixRoute.h +++ b/audio/aidl/default/r_submix/SubmixRoute.h @@ -14,16 +14,18 @@ * limitations under the License. */ +#pragma once + #include +#include #include #include #include #include - -#include "core-impl/Stream.h" +#include using aidl::android::media::audio::common::AudioChannelLayout; using aidl::android::media::audio::common::AudioFormatDescription; @@ -36,9 +38,13 @@ using ::android::sp; namespace aidl::android::hardware::audio::core::r_submix { static constexpr int kDefaultSampleRateHz = 48000; -// Size at default sample rate -// NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe(). -static constexpr int kDefaultPipeSizeInFrames = (1024 * 4); +// Value used to divide the MonoPipe buffer into segments that are written to the source and +// read from the sink. The maximum latency of the device is the size of the MonoPipe's buffer +// the minimum latency is the MonoPipe buffer size divided by this value. +static constexpr int kDefaultPipePeriodCount = 4; +// Size at the default sample rate +// NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe. +static constexpr int kDefaultPipeSizeInFrames = 1024 * kDefaultPipePeriodCount; // Configuration of the audio stream. struct AudioConfig { @@ -76,14 +82,6 @@ class SubmixRoute { std::lock_guard guard(mLock); return mReadCounterFrames; } - int getReadErrorCount() { - std::lock_guard guard(mLock); - return mReadErrorCount; - } - std::chrono::time_point getRecordStartTime() { - std::lock_guard guard(mLock); - return mRecordStartTime; - } sp getSink() { std::lock_guard guard(mLock); return mSink; @@ -119,9 +117,6 @@ class SubmixRoute { bool mStreamOutStandby GUARDED_BY(mLock) = true; // how many frames have been requested to be read since standby long mReadCounterFrames GUARDED_BY(mLock) = 0; - int mReadErrorCount GUARDED_BY(mLock) = 0; - // wall clock when recording starts - std::chrono::time_point mRecordStartTime GUARDED_BY(mLock); // Pipe variables: they handle the ring buffer that "pipes" audio: // - from the submix virtual audio output == what needs to be played diff --git a/audio/aidl/default/stub/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp index 660a51e2e0c4800bdf1924d458a8a4eb2730f9ce..2422fe4b3e670cd6f791c81a38e1dcb06bdfb31e 100644 --- a/audio/aidl/default/stub/StreamStub.cpp +++ b/audio/aidl/default/stub/StreamStub.cpp @@ -94,7 +94,7 @@ StreamStub::StreamStub(StreamContext* context, const Metadata& metadata) } ::android::status_t StreamStub::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, - int32_t* latencyMs) { + int32_t*) { if (!mIsInitialized) { LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver"; } @@ -117,7 +117,6 @@ StreamStub::StreamStub(StreamContext* context, const Metadata& metadata) } } *actualFrameCount = frameCount; - *latencyMs = Module::kLatencyMs; return ::android::OK; } diff --git a/audio/aidl/default/usb/StreamUsb.cpp b/audio/aidl/default/usb/StreamUsb.cpp index b60b4fd020cf9cc25040ac20dc7af63abeb86c24..9b10432b183c634f1b9bd967eb69c71a96499fdd 100644 --- a/audio/aidl/default/usb/StreamUsb.cpp +++ b/audio/aidl/default/usb/StreamUsb.cpp @@ -52,7 +52,7 @@ ndk::ScopedAStatus StreamUsb::setConnectedDevices( } connectedDeviceProfiles.push_back(*profile); } - RETURN_STATUS_IF_ERROR(setConnectedDevices(connectedDevices)); + RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices)); std::lock_guard guard(mLock); mConnectedDeviceProfiles = std::move(connectedDeviceProfiles); mConnectedDevicesUpdated.store(true, std::memory_order_release); diff --git a/audio/aidl/default/visualizer/VisualizerSw.cpp b/audio/aidl/default/visualizer/VisualizerSw.cpp index 0909f251444160f366df003c2b88e881d157449b..285c102b6ba3807aa219a53e61dce14e6b822092 100644 --- a/audio/aidl/default/visualizer/VisualizerSw.cpp +++ b/audio/aidl/default/visualizer/VisualizerSw.cpp @@ -73,7 +73,7 @@ const Descriptor VisualizerSw::kDescriptor = { .proxy = std::nullopt}, .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST, - .volume = Flags::Volume::CTRL}, + .volume = Flags::Volume::NONE}, .name = VisualizerSw::kEffectName, .implementor = "The Android Open Source Project"}, .capability = VisualizerSw::kCapability}; diff --git a/audio/aidl/sounddose/Android.bp b/audio/aidl/sounddose/Android.bp index 6f2f79083b483313634813bf8645ffced13cc766..c65e4ff041778e2390d263a2d10568bd5cf4128b 100644 --- a/audio/aidl/sounddose/Android.bp +++ b/audio/aidl/sounddose/Android.bp @@ -52,11 +52,11 @@ aidl_interface { // IMPORTANT: Update latest_android_hardware_audio_sounddose every time you // add the latest frozen version to versions_with_info ], - frozen: true, + frozen: false, } // Note: This should always be one version ahead of the last frozen version -latest_android_hardware_audio_sounddose = "android.hardware.audio.sounddose-V1" +latest_android_hardware_audio_sounddose = "android.hardware.audio.sounddose-V2" // Modules that depend on android.hardware.audio.sounddose directly can include // the following cc_defaults to avoid explicitly managing dependency versions diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp index f7cf4cece31d124a34f865ea6f4e60fbff2d0144..191f928a53b275608271b28e9f899ce02f1ac908 100644 --- a/audio/aidl/vts/Android.bp +++ b/audio/aidl/vts/Android.bp @@ -11,6 +11,7 @@ cc_defaults { name: "VtsHalAudioTargetTestDefaults", defaults: [ "latest_android_hardware_audio_common_ndk_static", + "latest_android_hardware_audio_effect_ndk_static", "latest_android_media_audio_common_types_ndk_static", "use_libaidlvintf_gtest_helper_static", "VtsHalTargetTestDefaults", @@ -20,13 +21,15 @@ cc_defaults { "libfmq", ], static_libs: [ - "android.hardware.audio.effect-V1-ndk", "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", "libaudioaidlcommon", "libaidlcommonsupport", ], - header_libs: ["libaudioaidl_headers"], + header_libs: [ + "libaudioaidl_headers", + "libexpectedutils_headers", + ], cflags: [ "-Wall", "-Wextra", @@ -37,7 +40,10 @@ cc_defaults { "general-tests", "vts", ], - srcs: [":effectCommonFile"], + srcs: [ + ":effectCommonFile", + "TestUtils.cpp", + ], } cc_test { @@ -80,6 +86,9 @@ cc_test { name: "VtsHalDownmixTargetTest", defaults: ["VtsHalAudioTargetTestDefaults"], srcs: ["VtsHalDownmixTargetTest.cpp"], + shared_libs: [ + "libaudioutils", + ], } cc_test { diff --git a/audio/aidl/vts/AudioHalBinderServiceUtil.h b/audio/aidl/vts/AudioHalBinderServiceUtil.h index b4b46326a6f99ff9844d35731ebca0c34c115d2f..4ebc1b18b532ecd9bd94f87fece65f3bf971f382 100644 --- a/audio/aidl/vts/AudioHalBinderServiceUtil.h +++ b/audio/aidl/vts/AudioHalBinderServiceUtil.h @@ -42,20 +42,9 @@ class AudioHalBinderServiceUtil { ndk::SpAIBinder restartService( std::chrono::milliseconds timeoutMs = std::chrono::milliseconds(3000)) { - mDeathHandler.reset(new AidlDeathRecipient(mBinder)); - if (STATUS_OK != mDeathHandler->linkToDeath()) { - LOG(ERROR) << "linkToDeath failed"; - return nullptr; - } - if (!android::base::SetProperty("sys.audio.restart.hal", "1")) { - LOG(ERROR) << "SetProperty failed"; - return nullptr; + if (!stopService(timeoutMs)) { + return {}; } - if (!mDeathHandler->waitForFired(timeoutMs)) { - LOG(ERROR) << "Timeout wait for death"; - return nullptr; - } - mDeathHandler.reset(); return connectToService(mServiceName); } @@ -71,8 +60,7 @@ class AudioHalBinderServiceUtil { bool waitForFired(std::chrono::milliseconds timeoutMs) { std::unique_lock lock(mutex); - condition.wait_for(lock, timeoutMs, [this]() { return fired; }); - return fired; + return condition.wait_for(lock, timeoutMs, [this]() { return fired; }); } private: @@ -94,7 +82,23 @@ class AudioHalBinderServiceUtil { } }; + bool stopService(std::chrono::milliseconds timeoutMs) { + AidlDeathRecipient deathHandler(mBinder); + if (STATUS_OK != deathHandler.linkToDeath()) { + LOG(ERROR) << "linkToDeath failed"; + return false; + } + if (!android::base::SetProperty("sys.audio.restart.hal", "1")) { + LOG(ERROR) << "SetProperty failed"; + return false; + } + if (!deathHandler.waitForFired(timeoutMs)) { + LOG(ERROR) << "Timeout wait for death of " << mServiceName; + return false; + } + return true; + } + std::string mServiceName; ndk::SpAIBinder mBinder; - std::unique_ptr mDeathHandler; }; diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h index 2c8edf28fb09c3a966e6188e5523849e97f471df..d813554221796da8c4a036aca00c6a0b3ab81385 100644 --- a/audio/aidl/vts/EffectHelper.h +++ b/audio/aidl/vts/EffectHelper.h @@ -283,4 +283,32 @@ class EffectHelper { } return functor(result); } + + static void processAndWriteToOutput(std::vector& inputBuffer, + std::vector& outputBuffer, + const std::shared_ptr& mEffect, + IEffect::OpenEffectReturn* mOpenEffectReturn) { + // Initialize AidlMessagequeues + auto statusMQ = std::make_unique(mOpenEffectReturn->statusMQ); + ASSERT_TRUE(statusMQ->isValid()); + auto inputMQ = std::make_unique(mOpenEffectReturn->inputDataMQ); + ASSERT_TRUE(inputMQ->isValid()); + auto outputMQ = std::make_unique(mOpenEffectReturn->outputDataMQ); + ASSERT_TRUE(outputMQ->isValid()); + + // Enabling the process + ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START)); + ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING)); + + // Write from buffer to message queues and calling process + EXPECT_NO_FATAL_FAILURE(EffectHelper::writeToFmq(statusMQ, inputMQ, inputBuffer)); + + // Read the updated message queues into buffer + EXPECT_NO_FATAL_FAILURE(EffectHelper::readFromFmq(statusMQ, 1, outputMQ, + outputBuffer.size(), outputBuffer)); + + // Disable the process + ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET)); + ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE)); + } }; diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp index 8fdb1552df6008e58b9730fdda1f0dcf2a082bd2..2b86271361b576d3d15734d4e49d2f9d279362aa 100644 --- a/audio/aidl/vts/ModuleConfig.cpp +++ b/audio/aidl/vts/ModuleConfig.cpp @@ -17,10 +17,14 @@ #include #include +#define LOG_TAG "VtsHalAudio.ModuleConfig" +#include + #include #include #include #include +#include #include "ModuleConfig.h" @@ -66,20 +70,7 @@ std::optional ModuleConfig::generateOffloadInfoIfNeeded( return {}; } -std::vector -ModuleConfig::getAudioPortsForDeviceTypes(const std::vector& deviceTypes, - const std::string& connection) { - return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection); -} - // static -std::vector ModuleConfig::getBuiltInMicPorts( - const std::vector& ports) { - return getAudioPortsForDeviceTypes( - ports, std::vector{AudioDeviceType::IN_MICROPHONE, - AudioDeviceType::IN_MICROPHONE_BACK}); -} - std::vector ModuleConfig::getAudioPortsForDeviceTypes( const std::vector& ports, @@ -99,6 +90,14 @@ ModuleConfig::getAudioPortsForDeviceTypes( return result; } +// static +std::vector ModuleConfig::getBuiltInMicPorts( + const std::vector& ports) { + return getAudioPortsForDeviceTypes( + ports, std::vector{AudioDeviceType::IN_MICROPHONE, + AudioDeviceType::IN_MICROPHONE_BACK}); +} + template auto findById(const std::vector& v, int32_t id) { return std::find_if(v.begin(), v.end(), [&](const auto& p) { return p.id == id; }); @@ -118,10 +117,7 @@ ModuleConfig::ModuleConfig(IModule* module) { } else { mAttachedSinkDevicePorts.insert(port.id); } - } else if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_VIRTUAL - // The "virtual" connection is used for remote submix which is a dynamic - // device but it can be connected and used w/o external hardware. - && port.profiles.empty()) { + } else { mExternalDevicePorts.insert(port.id); } } @@ -140,6 +136,12 @@ std::vector ModuleConfig::getAttachedDevicePorts() const { return result; } +std::vector +ModuleConfig::getAudioPortsForDeviceTypes(const std::vector& deviceTypes, + const std::string& connection) const { + return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection); +} + std::vector ModuleConfig::getConnectedExternalDevicePorts() const { std::vector result; std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) { @@ -228,6 +230,16 @@ std::vector ModuleConfig::getMmapInMixPorts(bool connectedOnly, bool }); } +std::vector ModuleConfig::getRemoteSubmixPorts(bool isInput, bool singlePort) const { + AudioDeviceType deviceType = isInput ? AudioDeviceType::IN_SUBMIX : AudioDeviceType::OUT_SUBMIX; + auto ports = getAudioPortsForDeviceTypes(std::vector{deviceType}, + AudioDeviceDescription::CONNECTION_VIRTUAL); + if (singlePort) { + if (!ports.empty()) ports.resize(1); + } + return ports; +} + std::vector ModuleConfig::getConnectedDevicesPortsForMixPort( bool isInput, const AudioPortConfig& mixPortConfig) const { const auto mixPortIt = findById(mPorts, mixPortConfig.portId); @@ -280,6 +292,32 @@ std::optional ModuleConfig::getSourceMixPortForConnectedDevice() cons return {}; } +std::vector ModuleConfig::getRoutableDevicePortsForMixPort(const AudioPort& port, + bool connectedOnly) const { + std::set portIds = findRoutablePortIds(port.id); + const bool isInput = port.flags.getTag() == AudioIoFlags::input; + std::set devicePortIds; + if (connectedOnly) { + devicePortIds = isInput ? getConnectedSourceDevicePorts() : getConnectedSinkDevicePorts(); + } else { + devicePortIds = portIds; + } + std::vector result; + std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) { + return port.ext.getTag() == AudioPortExt::Tag::device && portIds.count(port.id) > 0 && + devicePortIds.count(port.id) > 0; + }); + return result; +} + +std::vector ModuleConfig::getRoutableMixPortsForDevicePort(const AudioPort& port, + bool connectedOnly) const { + std::set portIds = findRoutablePortIds(port.id); + const bool isInput = port.flags.getTag() == AudioIoFlags::input; + return findMixPorts(isInput, connectedOnly, false /*singlePort*/, + [&portIds](const AudioPort& p) { return portIds.count(p.id) > 0; }); +} + std::optional ModuleConfig::getNonRoutableSrcSinkPair( bool isInput) const { const auto mixPorts = getMixPorts(isInput, false /*connectedOnly*/); @@ -453,6 +491,20 @@ std::vector ModuleConfig::findMixPorts( return result; } +std::set ModuleConfig::findRoutablePortIds(int32_t portId) const { + std::set portIds; + for (const auto& route : mRoutes) { + if (portId == route.sinkPortId) { + portIds.insert(route.sourcePortIds.begin(), route.sourcePortIds.end()); + } else if (auto it = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(), + portId); + it != route.sourcePortIds.end()) { + portIds.insert(route.sinkPortId); + } + } + return portIds; +} + std::vector ModuleConfig::generateAudioMixPortConfigs( const std::vector& ports, bool isInput, bool singleProfile) const { std::vector result; @@ -499,18 +551,13 @@ std::vector ModuleConfig::generateAudioDevicePortConfigs( return result; } -const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceConnected(IModule* module, - const AudioPort& port) { - // Update ports and routes - mStatus = module->getAudioPorts(&mPorts); - if (!mStatus.isOk()) return mStatus; - mStatus = module->getAudioRoutes(&mRoutes); - if (!mStatus.isOk()) return mStatus; +ndk::ScopedAStatus ModuleConfig::onExternalDeviceConnected(IModule* module, const AudioPort& port) { + RETURN_STATUS_IF_ERROR(module->getAudioPorts(&mPorts)); + RETURN_STATUS_IF_ERROR(module->getAudioRoutes(&mRoutes)); // Validate port is present in module if (std::find(mPorts.begin(), mPorts.end(), port) == mPorts.end()) { - mStatus = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - return mStatus; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) { @@ -518,23 +565,20 @@ const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceConnected(IModule* modul } else { mConnectedExternalSinkDevicePorts.insert(port.id); } - return mStatus; + return ndk::ScopedAStatus::ok(); } -const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceDisconnected(IModule* module, - const AudioPort& port) { - // Update ports and routes - mStatus = module->getAudioPorts(&mPorts); - if (!mStatus.isOk()) return mStatus; - mStatus = module->getAudioRoutes(&mRoutes); - if (!mStatus.isOk()) return mStatus; +ndk::ScopedAStatus ModuleConfig::onExternalDeviceDisconnected(IModule* module, + const AudioPort& port) { + RETURN_STATUS_IF_ERROR(module->getAudioPorts(&mPorts)); + RETURN_STATUS_IF_ERROR(module->getAudioRoutes(&mRoutes)); if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) { mConnectedExternalSourceDevicePorts.erase(port.id); } else { mConnectedExternalSinkDevicePorts.erase(port.id); } - return mStatus; + return ndk::ScopedAStatus::ok(); } bool ModuleConfig::isMmapSupported() const { diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h index bce1de175ff8b0e4458fdf734f65f510a17435f5..4a87f8cbd213614545dc941afb23a3594fa2f970 100644 --- a/audio/aidl/vts/ModuleConfig.h +++ b/audio/aidl/vts/ModuleConfig.h @@ -38,9 +38,6 @@ class ModuleConfig { generateOffloadInfoIfNeeded( const aidl::android::media::audio::common::AudioPortConfig& portConfig); - std::vector getAudioPortsForDeviceTypes( - const std::vector& deviceTypes, - const std::string& connection = ""); static std::vector getAudioPortsForDeviceTypes( const std::vector& ports, const std::vector& deviceTypes, @@ -53,6 +50,9 @@ class ModuleConfig { std::string getError() const { return mStatus.getMessage(); } std::vector getAttachedDevicePorts() const; + std::vector getAudioPortsForDeviceTypes( + const std::vector& deviceTypes, + const std::string& connection = "") const; std::vector getConnectedExternalDevicePorts() const; std::set getConnectedSinkDevicePorts() const; @@ -85,6 +85,8 @@ class ModuleConfig { std::vector getMmapInMixPorts( bool connectedOnly /*Permanently attached and connected external devices*/, bool singlePort) const; + std::vector getRemoteSubmixPorts( + bool isInput, bool singlePort) const; std::vector getConnectedDevicesPortsForMixPort( bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const { @@ -103,6 +105,13 @@ class ModuleConfig { std::optional getSourceMixPortForConnectedDevice() const; + std::vector getRoutableDevicePortsForMixPort( + const aidl::android::media::audio::common::AudioPort& port, + bool connectedOnly /*Permanently attached and connected external devices*/) const; + std::vector getRoutableMixPortsForDevicePort( + const aidl::android::media::audio::common::AudioPort& port, + bool connectedOnly /*Permanently attached and connected external devices*/) const; + std::optional getNonRoutableSrcSinkPair(bool isInput) const; std::optional getRoutableSrcSinkPair(bool isInput) const; std::vector getRoutableSrcSinkGroups(bool isInput) const; @@ -157,10 +166,10 @@ class ModuleConfig { return *config.begin(); } - const ndk::ScopedAStatus& onExternalDeviceConnected( + ndk::ScopedAStatus onExternalDeviceConnected( aidl::android::hardware::audio::core::IModule* module, const aidl::android::media::audio::common::AudioPort& port); - const ndk::ScopedAStatus& onExternalDeviceDisconnected( + ndk::ScopedAStatus onExternalDeviceDisconnected( aidl::android::hardware::audio::core::IModule* module, const aidl::android::media::audio::common::AudioPort& port); @@ -173,6 +182,7 @@ class ModuleConfig { bool isInput, bool connectedOnly, bool singlePort, const std::function& pred) const; + std::set findRoutablePortIds(int32_t portId) const; std::vector generateAudioMixPortConfigs( const std::vector& ports, bool isInput, bool singleProfile) const; diff --git a/audio/aidl/vts/TestUtils.cpp b/audio/aidl/vts/TestUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0184683f9e9bbb6c6cbd469969e4f0939aa85bc --- /dev/null +++ b/audio/aidl/vts/TestUtils.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 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 "TestUtils.h" + +#define LOG_TAG "VtsHalAudio_TestUtils" + +#include + +namespace android::hardware::audio::common::testing { + +namespace detail { +void TestExecutionTracer::OnTestStart(const ::testing::TestInfo& test_info) { + TraceTestState("Started", test_info); +} + +void TestExecutionTracer::OnTestEnd(const ::testing::TestInfo& test_info) { + TraceTestState("Completed", test_info); +} + +void TestExecutionTracer::OnTestPartResult(const ::testing::TestPartResult& result) { + LOG(INFO) << result; +} + +void TestExecutionTracer::TraceTestState(const std::string& state, + const ::testing::TestInfo& test_info) { + LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name(); +} +} +} \ No newline at end of file diff --git a/audio/aidl/vts/TestUtils.h b/audio/aidl/vts/TestUtils.h index b55966985634ad68d792000bcd426ef1c71974a6..515b8a2a2f895da77d77dde6e206a9b2b711d96d 100644 --- a/audio/aidl/vts/TestUtils.h +++ b/audio/aidl/vts/TestUtils.h @@ -18,15 +18,24 @@ #include #include -#include #include #include +#include namespace android::hardware::audio::common::testing { namespace detail { +class TestExecutionTracer : public ::testing::EmptyTestEventListener { + public: + void OnTestStart(const ::testing::TestInfo& test_info) override; + void OnTestEnd(const ::testing::TestInfo& test_info) override; + void OnTestPartResult(const ::testing::TestPartResult& result) override; + private: + static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info); +}; + inline ::testing::AssertionResult assertIsOk(const char* expr, const ::ndk::ScopedAStatus& status) { if (status.isOk()) { return ::testing::AssertionSuccess(); @@ -60,6 +69,23 @@ inline ::testing::AssertionResult assertResult(const char* exp_expr, const char* << "\n but is has completed with: " << status; } +inline ::testing::AssertionResult assertIsOkOrUnknownTransaction( + const char* expr, const ::ndk::ScopedAStatus& status) { + if (status.getStatus() == STATUS_UNKNOWN_TRANSACTION) { + return ::testing::AssertionSuccess(); + } + return assertIsOk(expr, status); +} + +inline ::testing::AssertionResult assertResultOrUnknownTransaction( + const char* exp_expr, const char* act_expr, int32_t expected, + const ::ndk::ScopedAStatus& status) { + if (status.getStatus() == STATUS_UNKNOWN_TRANSACTION) { + return ::testing::AssertionSuccess(); + } + return assertResult(exp_expr, act_expr, expected, status); +} + } // namespace detail } // namespace android::hardware::audio::common::testing @@ -83,4 +109,16 @@ inline ::testing::AssertionResult assertResult(const char* exp_expr, const char* if ((flags).hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL || (flags).bypass) { \ GTEST_SKIP() << "Skip data path for offload"; \ } \ - }) \ No newline at end of file + }) + +// Test that the transaction status 'isOk' if it is a known transaction +#define EXPECT_IS_OK_OR_UNKNOWN_TRANSACTION(ret) \ + EXPECT_PRED_FORMAT1( \ + ::android::hardware::audio::common::testing::detail::assertIsOkOrUnknownTransaction, \ + ret) + +// Test that the transaction status is as expected if it is a known transaction +#define EXPECT_STATUS_OR_UNKNOWN_TRANSACTION(expected, ret) \ + EXPECT_PRED_FORMAT2( \ + ::android::hardware::audio::common::testing::detail::assertResultOrUnknownTransaction, \ + expected, ret) diff --git a/audio/aidl/vts/VtsHalAECTargetTest.cpp b/audio/aidl/vts/VtsHalAECTargetTest.cpp index 0354e3ce2368e7f265970194efe840707b398af5..39168b1bf1a917820c7ec80de27455e008ff99d7 100644 --- a/audio/aidl/vts/VtsHalAECTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAECTargetTest.cpp @@ -34,6 +34,7 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::Range; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; enum ParamName { PARAM_INSTANCE_NAME, PARAM_ECHO_DELAY, PARAM_MOBILE_MODE }; using AECParamTestParam = std::tuple, Descriptor>, @@ -178,6 +179,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AECParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalAGC1TargetTest.cpp b/audio/aidl/vts/VtsHalAGC1TargetTest.cpp index 65c6a8f5fa7c0305c22e0b553c51b5915d4e1ecd..6066025efd5ede3f2c2d2c19752f85ce026d8c9e 100644 --- a/audio/aidl/vts/VtsHalAGC1TargetTest.cpp +++ b/audio/aidl/vts/VtsHalAGC1TargetTest.cpp @@ -28,6 +28,7 @@ using aidl::android::hardware::audio::effect::getEffectTypeUuidAutomaticGainCont using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; enum ParamName { PARAM_INSTANCE_NAME, @@ -189,6 +190,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AGC1ParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalAGC2TargetTest.cpp b/audio/aidl/vts/VtsHalAGC2TargetTest.cpp index 46a569ef9255b0413f4ba7b4d3ec118e9f4429f9..8793e4c86258d992d7e46cc16c4e48e442a4ede3 100644 --- a/audio/aidl/vts/VtsHalAGC2TargetTest.cpp +++ b/audio/aidl/vts/VtsHalAGC2TargetTest.cpp @@ -29,6 +29,7 @@ using aidl::android::hardware::audio::effect::getEffectTypeUuidAutomaticGainCont using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; enum ParamName { PARAM_INSTANCE_NAME, @@ -194,6 +195,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AGC2ParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp index b1eecd21416ce1ae64917bfd3ca41f3f511bc559..f91795bc1349cb60ba06956ccf43635e063a4845 100644 --- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include "AudioHalBinderServiceUtil.h" @@ -86,6 +87,7 @@ using aidl::android::media::audio::common::AudioDualMonoMode; using aidl::android::media::audio::common::AudioFormatType; using aidl::android::media::audio::common::AudioIoFlags; using aidl::android::media::audio::common::AudioLatencyMode; +using aidl::android::media::audio::common::AudioMMapPolicy; using aidl::android::media::audio::common::AudioMMapPolicyInfo; using aidl::android::media::audio::common::AudioMMapPolicyType; using aidl::android::media::audio::common::AudioMode; @@ -106,14 +108,28 @@ using aidl::android::media::audio::common::MicrophoneInfo; using aidl::android::media::audio::common::Void; using android::hardware::audio::common::StreamLogic; using android::hardware::audio::common::StreamWorker; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; using ndk::enum_range; using ndk::ScopedAStatus; template -auto findById(std::vector& v, int32_t id) { +std::set extractIds(const std::vector& v) { + std::set ids; + std::transform(v.begin(), v.end(), std::inserter(ids, ids.begin()), + [](const auto& entity) { return entity.id; }); + return ids; +} + +template +auto findById(const std::vector& v, int32_t id) { return std::find_if(v.begin(), v.end(), [&](const auto& e) { return e.id == id; }); } +template +auto findAny(const std::vector& v, const std::set& ids) { + return std::find_if(v.begin(), v.end(), [&](const auto& e) { return ids.count(e.id) > 0; }); +} + template std::vector GetNonExistentIds(const C& allIds) { if (allIds.empty()) { @@ -152,8 +168,10 @@ AudioPort GenerateUniqueDeviceAddress(const AudioPort& port) { static int nextId = 0; using Tag = AudioDeviceAddress::Tag; const auto& deviceDescription = port.ext.get().device.type; - AudioDeviceAddress address; - if (kPointToPointConnections.count(deviceDescription.connection) == 0) { + AudioDeviceAddress address = port.ext.get().device.address; + // If the address is already set, do not re-generate. + if (address == AudioDeviceAddress() && + kPointToPointConnections.count(deviceDescription.connection) == 0) { switch (suggestDeviceAddressTag(deviceDescription)) { case Tag::id: address = AudioDeviceAddress::make(std::to_string(++nextId)); @@ -414,31 +432,51 @@ void TestSetVendorParameters(Instance* inst, bool* isSupported) { // Can be used as a base for any test here, does not depend on the fixture GTest parameters. class AudioCoreModuleBase { public: - // Default buffer sizes are used mostly for negative tests. - static constexpr int kDefaultBufferSizeFrames = 256; + // Fixed buffer size are used for negative tests only. For any tests involving stream + // opening that must success, the minimum buffer size must be obtained from a patch. + // This is implemented by the 'StreamFixture' utility class. + static constexpr int kNegativeTestBufferSizeFrames = 256; static constexpr int kDefaultLargeBufferSizeFrames = 48000; - void SetUpImpl(const std::string& moduleName) { - ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName)); + void SetUpImpl(const std::string& moduleName, bool setUpDebug = true) { + ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName, setUpDebug)); + ASSERT_IS_OK(module->getAudioPorts(&initialPorts)); + ASSERT_IS_OK(module->getAudioRoutes(&initialRoutes)); } - void TearDownImpl() { debug.reset(); } + void TearDownImpl() { + debug.reset(); + ASSERT_NE(module, nullptr); + std::vector finalPorts; + ASSERT_IS_OK(module->getAudioPorts(&finalPorts)); + EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual(initialPorts, finalPorts)) + << "The list of audio ports was not restored to the initial state"; + std::vector finalRoutes; + ASSERT_IS_OK(module->getAudioRoutes(&finalRoutes)); + EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual(initialRoutes, finalRoutes)) + << "The list of audio routes was not restored to the initial state"; + } - void ConnectToService(const std::string& moduleName) { + void ConnectToService(const std::string& moduleName, bool setUpDebug) { ASSERT_EQ(module, nullptr); ASSERT_EQ(debug, nullptr); module = IModule::fromBinder(binderUtil.connectToService(moduleName)); ASSERT_NE(module, nullptr); - ASSERT_NO_FATAL_FAILURE(SetUpDebug()); + if (setUpDebug) { + ASSERT_NO_FATAL_FAILURE(SetUpDebug()); + } } void RestartService() { ASSERT_NE(module, nullptr); moduleConfig.reset(); + const bool setUpDebug = !!debug; debug.reset(); module = IModule::fromBinder(binderUtil.restartService()); ASSERT_NE(module, nullptr); - ASSERT_NO_FATAL_FAILURE(SetUpDebug()); + if (setUpDebug) { + ASSERT_NO_FATAL_FAILURE(SetUpDebug()); + } } void SetUpDebug() { @@ -478,9 +516,7 @@ class AudioCoreModuleBase { const std::string& errorMessage) { std::vector entities; { ASSERT_IS_OK((module.get()->*getter)(&entities)); } - std::transform(entities.begin(), entities.end(), - std::inserter(*entityIds, entityIds->begin()), - [](const auto& entity) { return entity.id; }); + *entityIds = extractIds(entities); EXPECT_EQ(entities.size(), entityIds->size()) << errorMessage; } @@ -510,17 +546,24 @@ class AudioCoreModuleBase { } } + // Warning: modifies the vectors! + template + void VerifyVectorsAreEqual(std::vector& v1, std::vector& v2) { + ASSERT_EQ(v1.size(), v2.size()); + std::sort(v1.begin(), v1.end()); + std::sort(v2.begin(), v2.end()); + if (v1 != v2) { + FAIL() << "Vectors are not equal: v1 = " << ::android::internal::ToString(v1) + << ", v2 = " << ::android::internal::ToString(v2); + } + } + std::shared_ptr module; std::unique_ptr moduleConfig; AudioHalBinderServiceUtil binderUtil; std::unique_ptr debug; -}; - -class AudioCoreModule : public AudioCoreModuleBase, public testing::TestWithParam { - public: - void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam())); } - - void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); } + std::vector initialPorts; + std::vector initialRoutes; }; class WithDevicePortConnectedState { @@ -530,6 +573,8 @@ class WithDevicePortConnectedState { WithDevicePortConnectedState& operator=(const WithDevicePortConnectedState&) = delete; ~WithDevicePortConnectedState() { if (mModule != nullptr) { + EXPECT_IS_OK_OR_UNKNOWN_TRANSACTION(mModule->prepareToDisconnectExternalDevice(getId())) + << "when preparing to disconnect device port ID " << getId(); EXPECT_IS_OK(mModule->disconnectExternalDevice(getId())) << "when disconnecting device port ID " << getId(); } @@ -538,16 +583,19 @@ class WithDevicePortConnectedState { << "when external device disconnected"; } } + ScopedAStatus SetUpNoChecks(IModule* module, ModuleConfig* moduleConfig) { + RETURN_STATUS_IF_ERROR(module->connectExternalDevice(mIdAndData, &mConnectedPort)); + RETURN_STATUS_IF_ERROR(moduleConfig->onExternalDeviceConnected(module, mConnectedPort)); + mModule = module; + mModuleConfig = moduleConfig; + return ScopedAStatus::ok(); + } void SetUp(IModule* module, ModuleConfig* moduleConfig) { - ASSERT_IS_OK(module->connectExternalDevice(mIdAndData, &mConnectedPort)) + ASSERT_NE(moduleConfig, nullptr); + ASSERT_IS_OK(SetUpNoChecks(module, moduleConfig)) << "when connecting device port ID & data " << mIdAndData.toString(); ASSERT_NE(mIdAndData.id, getId()) << "ID of the connected port must not be the same as the ID of the template port"; - ASSERT_NE(moduleConfig, nullptr); - ASSERT_IS_OK(moduleConfig->onExternalDeviceConnected(module, mConnectedPort)) - << "when external device connected"; - mModule = module; - mModuleConfig = moduleConfig; } int32_t getId() const { return mConnectedPort.id; } const AudioPort& get() { return mConnectedPort; } @@ -559,6 +607,13 @@ class WithDevicePortConnectedState { AudioPort mConnectedPort; }; +class AudioCoreModule : public AudioCoreModuleBase, public testing::TestWithParam { + public: + void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam())); } + + void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); } +}; + class StreamContext { public: typedef AidlMessageQueue CommandMQ; @@ -1080,6 +1135,7 @@ class DefaultStreamCallback : public ::aidl::android::hardware::audio::core::BnS template struct IOTraits { static constexpr bool is_input = std::is_same_v; + static constexpr const char* directionStr = is_input ? "input" : "output"; using Worker = std::conditional_t; }; @@ -1111,8 +1167,7 @@ class WithStream { } ScopedAStatus SetUpNoChecks(IModule* module, const AudioPortConfig& portConfig, long bufferSizeFrames); - void SetUp(IModule* module, long bufferSizeFrames) { - ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module)); + void SetUpStream(IModule* module, long bufferSizeFrames) { ASSERT_IS_OK(SetUpNoChecks(module, bufferSizeFrames)) << "port config id " << getPortId(); ASSERT_NE(nullptr, mStream) << "port config id " << getPortId(); EXPECT_GE(mDescriptor.bufferSizeFrames, bufferSizeFrames) @@ -1120,6 +1175,10 @@ class WithStream { mContext.emplace(mDescriptor); ASSERT_NO_FATAL_FAILURE(mContext.value().checkIsValid()); } + void SetUp(IModule* module, long bufferSizeFrames) { + ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module)); + ASSERT_NO_FATAL_FAILURE(SetUpStream(module, bufferSizeFrames)); + } Stream* get() const { return mStream.get(); } const StreamContext* getContext() const { return mContext ? &(mContext.value()) : nullptr; } StreamEventReceiver* getEventReceiver() { return mStreamCallback->getEventReceiver(); } @@ -1207,11 +1266,25 @@ class WithAudioPatch { const AudioPortConfig& portConfig2) : mSrcPortConfig(sinkIsCfg1 ? portConfig2 : portConfig1), mSinkPortConfig(sinkIsCfg1 ? portConfig1 : portConfig2) {} + WithAudioPatch(const WithAudioPatch& patch, const AudioPortConfig& srcPortConfig, + const AudioPortConfig& sinkPortConfig) + : mInitialPatch(patch.mPatch), + mSrcPortConfig(srcPortConfig), + mSinkPortConfig(sinkPortConfig), + mModule(patch.mModule), + mPatch(patch.mPatch) {} WithAudioPatch(const WithAudioPatch&) = delete; WithAudioPatch& operator=(const WithAudioPatch&) = delete; ~WithAudioPatch() { if (mModule != nullptr && mPatch.id != 0) { - EXPECT_IS_OK(mModule->resetAudioPatch(mPatch.id)) << "patch id " << getId(); + if (mInitialPatch.has_value()) { + AudioPatch ignored; + // This releases our port configs so that they can be reset. + EXPECT_IS_OK(mModule->setAudioPatch(*mInitialPatch, &ignored)) + << "patch id " << mInitialPatch->id; + } else { + EXPECT_IS_OK(mModule->resetAudioPatch(mPatch.id)) << "patch id " << getId(); + } } } void SetUpPortConfigs(IModule* module) { @@ -1233,8 +1306,21 @@ class WithAudioPatch { EXPECT_GT(latencyMs, 0) << "patch id " << getId(); } } + void VerifyAgainstAllPatches(IModule* module) { + std::vector allPatches; + ASSERT_IS_OK(module->getAudioPatches(&allPatches)); + const auto& patchIt = findById(allPatches, getId()); + ASSERT_NE(patchIt, allPatches.end()) << "patch id " << getId(); + if (get() != *patchIt) { + FAIL() << "Stored patch: " << get().toString() << " is not the same as returned " + << "by the HAL module: " << patchIt->toString(); + } + } int32_t getId() const { return mPatch.id; } const AudioPatch& get() const { return mPatch; } + int32_t getMinimumStreamBufferSizeFrames() const { + return mPatch.minimumStreamBufferSizeFrames; + } const AudioPortConfig& getSinkPortConfig() const { return mSinkPortConfig.get(); } const AudioPortConfig& getSrcPortConfig() const { return mSrcPortConfig.get(); } const AudioPortConfig& getPortConfig(bool getSink) const { @@ -1242,6 +1328,7 @@ class WithAudioPatch { } private: + std::optional mInitialPatch; WithAudioPortConfig mSrcPortConfig; WithAudioPortConfig mSinkPortConfig; IModule* mModule = nullptr; @@ -1266,11 +1353,8 @@ TEST_P(AudioCoreModule, GetAudioPortsIsStable) { ASSERT_IS_OK(module->getAudioPorts(&ports1)); std::vector ports2; ASSERT_IS_OK(module->getAudioPorts(&ports2)); - ASSERT_EQ(ports1.size(), ports2.size()) - << "Sizes of audio port arrays do not match across consequent calls to getAudioPorts"; - std::sort(ports1.begin(), ports1.end()); - std::sort(ports2.begin(), ports2.end()); - EXPECT_EQ(ports1, ports2); + EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual(ports1, ports2)) + << "Audio port arrays do not match across consequent calls to getAudioPorts"; } TEST_P(AudioCoreModule, GetAudioRoutesIsStable) { @@ -1278,11 +1362,8 @@ TEST_P(AudioCoreModule, GetAudioRoutesIsStable) { ASSERT_IS_OK(module->getAudioRoutes(&routes1)); std::vector routes2; ASSERT_IS_OK(module->getAudioRoutes(&routes2)); - ASSERT_EQ(routes1.size(), routes2.size()) - << "Sizes of audio route arrays do not match across consequent calls to getAudioRoutes"; - std::sort(routes1.begin(), routes1.end()); - std::sort(routes2.begin(), routes2.end()); - EXPECT_EQ(routes1, routes2); + EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual(routes1, routes2)) + << " Audio route arrays do not match across consequent calls to getAudioRoutes"; } TEST_P(AudioCoreModule, GetAudioRoutesAreValid) { @@ -1451,8 +1532,25 @@ TEST_P(AudioCoreModule, GetAudioPortWithExternalDevices) { << "port ID " << connectedPortId; EXPECT_EQ(portConnected.get(), connectedPort); const auto& portProfiles = connectedPort.profiles; - EXPECT_NE(0UL, portProfiles.size()) - << "Connected port has no profiles: " << connectedPort.toString(); + if (portProfiles.empty()) { + const auto routableMixPorts = moduleConfig->getRoutableMixPortsForDevicePort( + connectedPort, true /*connectedOnly*/); + bool hasMixPortWithStaticProfile = false; + for (const auto& mixPort : routableMixPorts) { + const auto& mixPortProfiles = mixPort.profiles; + if (!mixPortProfiles.empty() && + !std::all_of(mixPortProfiles.begin(), mixPortProfiles.end(), + [](const auto& profile) { + return profile.format.type == AudioFormatType::DEFAULT; + })) { + hasMixPortWithStaticProfile = true; + break; + } + } + EXPECT_TRUE(hasMixPortWithStaticProfile) + << "Connected port has no profiles and no routable mix ports with profiles: " + << connectedPort.toString(); + } const auto dynamicProfileIt = std::find_if(portProfiles.begin(), portProfiles.end(), [](const auto& profile) { return profile.format.type == AudioFormatType::DEFAULT; @@ -1477,7 +1575,7 @@ TEST_P(AudioCoreModule, OpenStreamInvalidPortConfigId) { { aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args; args.portConfigId = portConfigId; - args.bufferSizeFrames = kDefaultBufferSizeFrames; + args.bufferSizeFrames = kNegativeTestBufferSizeFrames; aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openInputStream(args, &ret)) << "port config ID " << portConfigId; @@ -1486,7 +1584,7 @@ TEST_P(AudioCoreModule, OpenStreamInvalidPortConfigId) { { aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args; args.portConfigId = portConfigId; - args.bufferSizeFrames = kDefaultBufferSizeFrames; + args.bufferSizeFrames = kNegativeTestBufferSizeFrames; aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret)) << "port config ID " << portConfigId; @@ -1536,7 +1634,8 @@ TEST_P(AudioCoreModule, ResetAudioPortConfigToInitialValue) { EXPECT_NE(portConfigsAfter.end(), afterIt) << " port config ID " << c.id << " was removed by reset"; if (afterIt != portConfigsAfter.end()) { - EXPECT_EQ(c, *afterIt); + EXPECT_TRUE(c == *afterIt) + << "Expected: " << c.toString() << "; Actual: " << afterIt->toString(); } } } @@ -1648,10 +1747,17 @@ TEST_P(AudioCoreModule, TryConnectMissingDevice) { doNotSimulateConnections.flags().simulateDeviceConnections = false; ASSERT_NO_FATAL_FAILURE(doNotSimulateConnections.SetUp(module.get())); for (const auto& port : ports) { + // Virtual devices may not require external hardware and thus can always be connected. + if (port.ext.get().device.type.connection == + AudioDeviceDescription::CONNECTION_VIRTUAL) + continue; AudioPort portWithData = GenerateUniqueDeviceAddress(port), connectedPort; ScopedAStatus status = module->connectExternalDevice(portWithData, &connectedPort); EXPECT_STATUS(EX_ILLEGAL_STATE, status) << "static port " << portWithData.toString(); if (status.isOk()) { + EXPECT_IS_OK_OR_UNKNOWN_TRANSACTION( + module->prepareToDisconnectExternalDevice(connectedPort.id)) + << "when preparing to disconnect device port ID " << connectedPort.id; EXPECT_IS_OK(module->disconnectExternalDevice(connectedPort.id)) << "when disconnecting device port ID " << connectedPort.id; } @@ -1681,6 +1787,9 @@ TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceInvalidPorts) { invalidPort.id = portId; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->connectExternalDevice(invalidPort, &ignored)) << "port ID " << portId << ", when setting CONNECTED state"; + EXPECT_STATUS_OR_UNKNOWN_TRANSACTION(EX_ILLEGAL_ARGUMENT, + module->prepareToDisconnectExternalDevice(portId)) + << "port ID " << portId << ", when preparing to disconnect"; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->disconnectExternalDevice(portId)) << "port ID " << portId << ", when setting DISCONNECTED state"; } @@ -1691,6 +1800,9 @@ TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceInvalidPorts) { if (port.ext.getTag() != AudioPortExt::Tag::device) { EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->connectExternalDevice(port, &ignored)) << "non-device port ID " << port.id << " when setting CONNECTED state"; + EXPECT_STATUS_OR_UNKNOWN_TRANSACTION(EX_ILLEGAL_ARGUMENT, + module->prepareToDisconnectExternalDevice(port.id)) + << "non-device port ID " << port.id << " when preparing to disconnect"; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->disconnectExternalDevice(port.id)) << "non-device port ID " << port.id << " when setting DISCONNECTED state"; } else { @@ -1699,6 +1811,10 @@ TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceInvalidPorts) { EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->connectExternalDevice(port, &ignored)) << "for a permanently attached device port ID " << port.id << " when setting CONNECTED state"; + EXPECT_STATUS_OR_UNKNOWN_TRANSACTION( + EX_ILLEGAL_ARGUMENT, module->prepareToDisconnectExternalDevice(port.id)) + << "for a permanently attached device port ID " << port.id + << " when preparing to disconnect"; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->disconnectExternalDevice(port.id)) << "for a permanently attached device port ID " << port.id << " when setting DISCONNECTED state"; @@ -1716,6 +1832,9 @@ TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceTwice) { GTEST_SKIP() << "No external devices in the module."; } for (const auto& port : ports) { + EXPECT_STATUS_OR_UNKNOWN_TRANSACTION(EX_ILLEGAL_ARGUMENT, + module->prepareToDisconnectExternalDevice(port.id)) + << "when preparing to disconnect already disconnected device port ID " << port.id; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->disconnectExternalDevice(port.id)) << "when disconnecting already disconnected device port ID " << port.id; AudioPort portWithData = GenerateUniqueDeviceAddress(port); @@ -1750,6 +1869,10 @@ TEST_P(AudioCoreModule, DisconnectExternalDeviceNonResetPortConfig) { // Our test assumes that 'getAudioPort' returns at least one profile, and it // is not a dynamic profile. ASSERT_NO_FATAL_FAILURE(config.SetUp(module.get())); + EXPECT_IS_OK_OR_UNKNOWN_TRANSACTION( + module->prepareToDisconnectExternalDevice(portConnected.getId())) + << "when preparing to disconnect device port ID " << port.id + << " with active configuration " << config.getId(); EXPECT_STATUS(EX_ILLEGAL_STATE, module->disconnectExternalDevice(portConnected.getId())) << "when trying to disconnect device port ID " << port.id << " with active configuration " << config.getId(); @@ -1800,40 +1923,152 @@ TEST_P(AudioCoreModule, ExternalDevicePortRoutes) { } } -// Note: This test relies on simulation of external device connections by the HAL module. -TEST_P(AudioCoreModule, ExternalDeviceMixPortConfigs) { - // After an external device has been connected, all mix ports that can be routed - // to the device port for the connected device must have non-empty profiles. - ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); - std::vector externalDevicePorts = moduleConfig->getExternalDevicePorts(); - if (externalDevicePorts.empty()) { - GTEST_SKIP() << "No external devices in the module."; - } - for (const auto& port : externalDevicePorts) { - WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port)); - ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); +class RoutedPortsProfilesSnapshot { + public: + explicit RoutedPortsProfilesSnapshot(int32_t portId) : mPortId(portId) {} + void Capture(IModule* module) { std::vector routes; - ASSERT_IS_OK(module->getAudioRoutesForAudioPort(portConnected.getId(), &routes)); + ASSERT_IS_OK(module->getAudioRoutesForAudioPort(mPortId, &routes)); std::vector allPorts; ASSERT_IS_OK(module->getAudioPorts(&allPorts)); + ASSERT_NO_FATAL_FAILURE(GetAllRoutedPorts(routes, allPorts)); + ASSERT_NO_FATAL_FAILURE(GetProfileSizes()); + } + void VerifyNoProfilesChanges(const RoutedPortsProfilesSnapshot& before) { + for (const auto& p : before.mRoutedPorts) { + auto beforeIt = before.mPortProfileSizes.find(p.id); + ASSERT_NE(beforeIt, before.mPortProfileSizes.end()) + << "port ID " << p.id << " not found in the initial profile sizes"; + EXPECT_EQ(beforeIt->second, mPortProfileSizes[p.id]) + << " port " << p.toString() << " has an unexpected profile size change" + << " following an external device connection and disconnection"; + } + } + void VerifyProfilesNonEmpty() { + for (const auto& p : mRoutedPorts) { + EXPECT_NE(0UL, mPortProfileSizes[p.id]) + << " port " << p.toString() << " must have had its profiles" + << " populated while having a connected external device"; + } + } + + const std::vector& getRoutedPorts() const { return mRoutedPorts; } + + private: + void GetAllRoutedPorts(const std::vector& routes, + std::vector& allPorts) { for (const auto& r : routes) { - if (r.sinkPortId == portConnected.getId()) { + if (r.sinkPortId == mPortId) { for (const auto& srcPortId : r.sourcePortIds) { const auto srcPortIt = findById(allPorts, srcPortId); ASSERT_NE(allPorts.end(), srcPortIt) << "port ID " << srcPortId; - EXPECT_NE(0UL, srcPortIt->profiles.size()) - << " source port " << srcPortIt->toString() << " must have its profiles" - << " populated following external device connection"; + mRoutedPorts.push_back(*srcPortIt); } } else { const auto sinkPortIt = findById(allPorts, r.sinkPortId); ASSERT_NE(allPorts.end(), sinkPortIt) << "port ID " << r.sinkPortId; - EXPECT_NE(0UL, sinkPortIt->profiles.size()) - << " source port " << sinkPortIt->toString() << " must have its" - << " profiles populated following external device connection"; + mRoutedPorts.push_back(*sinkPortIt); } } } + void GetProfileSizes() { + std::transform( + mRoutedPorts.begin(), mRoutedPorts.end(), + std::inserter(mPortProfileSizes, mPortProfileSizes.end()), + [](const auto& port) { return std::make_pair(port.id, port.profiles.size()); }); + } + + const int32_t mPortId; + std::vector mRoutedPorts; + std::map mPortProfileSizes; +}; + +// Note: This test relies on simulation of external device connections by the HAL module. +TEST_P(AudioCoreModule, ExternalDeviceMixPortConfigs) { + // After an external device has been connected, all mix ports that can be routed + // to the device port for the connected device must have non-empty profiles. + // Since the test connects and disconnects a single device each time, the size + // of profiles for all mix ports routed to the device port under test must get back + // to the original count once the external device is disconnected. + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector externalDevicePorts = moduleConfig->getExternalDevicePorts(); + if (externalDevicePorts.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + for (const auto& port : externalDevicePorts) { + SCOPED_TRACE(port.toString()); + RoutedPortsProfilesSnapshot before(port.id); + ASSERT_NO_FATAL_FAILURE(before.Capture(module.get())); + if (before.getRoutedPorts().empty()) continue; + { + WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port)); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); + RoutedPortsProfilesSnapshot connected(portConnected.getId()); + ASSERT_NO_FATAL_FAILURE(connected.Capture(module.get())); + EXPECT_NO_FATAL_FAILURE(connected.VerifyProfilesNonEmpty()); + } + RoutedPortsProfilesSnapshot after(port.id); + ASSERT_NO_FATAL_FAILURE(after.Capture(module.get())); + EXPECT_NO_FATAL_FAILURE(after.VerifyNoProfilesChanges(before)); + } +} + +// Note: This test relies on simulation of external device connections by the HAL module. +TEST_P(AudioCoreModule, TwoExternalDevicesMixPortConfigsNested) { + // Ensure that in the case when two external devices are connected to the same + // device port, disconnecting one of them does not erase the profiles of routed mix ports. + // In this scenario, the connections are "nested." + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector externalDevicePorts = moduleConfig->getExternalDevicePorts(); + if (externalDevicePorts.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + for (const auto& port : externalDevicePorts) { + SCOPED_TRACE(port.toString()); + WithDevicePortConnectedState portConnected1(GenerateUniqueDeviceAddress(port)); + ASSERT_NO_FATAL_FAILURE(portConnected1.SetUp(module.get(), moduleConfig.get())); + { + // Connect and disconnect another device, if possible. It might not be possible + // for point-to-point connections, like analog or SPDIF. + WithDevicePortConnectedState portConnected2(GenerateUniqueDeviceAddress(port)); + if (auto status = portConnected2.SetUpNoChecks(module.get(), moduleConfig.get()); + !status.isOk()) { + continue; + } + } + RoutedPortsProfilesSnapshot connected(portConnected1.getId()); + ASSERT_NO_FATAL_FAILURE(connected.Capture(module.get())); + EXPECT_NO_FATAL_FAILURE(connected.VerifyProfilesNonEmpty()); + } +} + +// Note: This test relies on simulation of external device connections by the HAL module. +TEST_P(AudioCoreModule, TwoExternalDevicesMixPortConfigsInterleaved) { + // Ensure that in the case when two external devices are connected to the same + // device port, disconnecting one of them does not erase the profiles of routed mix ports. + // In this scenario, the connections are "interleaved." + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector externalDevicePorts = moduleConfig->getExternalDevicePorts(); + if (externalDevicePorts.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + for (const auto& port : externalDevicePorts) { + SCOPED_TRACE(port.toString()); + auto portConnected1 = + std::make_unique(GenerateUniqueDeviceAddress(port)); + ASSERT_NO_FATAL_FAILURE(portConnected1->SetUp(module.get(), moduleConfig.get())); + WithDevicePortConnectedState portConnected2(GenerateUniqueDeviceAddress(port)); + // Connect another device, if possible. It might not be possible for point-to-point + // connections, like analog or SPDIF. + if (auto status = portConnected2.SetUpNoChecks(module.get(), moduleConfig.get()); + !status.isOk()) { + continue; + } + portConnected1.reset(); + RoutedPortsProfilesSnapshot connected(portConnected2.getId()); + ASSERT_NO_FATAL_FAILURE(connected.Capture(module.get())); + EXPECT_NO_FATAL_FAILURE(connected.VerifyProfilesNonEmpty()); + } } TEST_P(AudioCoreModule, MasterMute) { @@ -1997,7 +2232,13 @@ TEST_P(AudioCoreModule, GetMmapPolicyInfos) { std::vector policyInfos; EXPECT_IS_OK(module->getMmapPolicyInfos(mmapPolicyType, &policyInfos)) << toString(mmapPolicyType); - EXPECT_EQ(isMmapSupported, !policyInfos.empty()); + const bool isMMapSupportedByPolicyInfos = + std::find_if(policyInfos.begin(), policyInfos.end(), [](const auto& info) { + return info.mmapPolicy == AudioMMapPolicy::AUTO || + info.mmapPolicy == AudioMMapPolicy::ALWAYS; + }) != policyInfos.end(); + EXPECT_EQ(isMmapSupported, isMMapSupportedByPolicyInfos) + << ::android::internal::ToString(policyInfos); } } @@ -2376,6 +2617,260 @@ class StreamLogicDriverInvalidCommand : public StreamLogicDriver { std::vector mStatuses; }; +// A helper which sets up necessary HAL structures for a proper stream initialization. +// +// The full sequence of actions to set up a stream is as follows: +// +// device port -> connect if necessary -> set up port config | -> set up patch +// mix port -> set up port config, unless it has been provided | +// +// then, from the patch, figure out the minimum HAL buffer size -> set up stream +// +// This sequence is reflected in the order of fields declaration. +// Various tests need to be able to start and stop at various point in this sequence, +// this is why there are methods that do just part of the work. +// +// Note: To maximize test coverage, this class relies on simulation of external device +// connections by the HAL module. +template +class StreamFixture { + public: + // Tests might need to override the direction. + StreamFixture(bool isInput = IOTraits::is_input) : mIsInput(isInput) {} + + void SetUpPortConfigAnyMixPort(IModule* module, ModuleConfig* moduleConfig, + bool connectedOnly) { + const auto mixPorts = moduleConfig->getMixPorts(mIsInput, connectedOnly); + mSkipTestReason = "No mix ports"; + for (const auto& mixPort : mixPorts) { + mSkipTestReason = ""; + ASSERT_NO_FATAL_FAILURE(SetUpPortConfigForMixPortOrConfig(module, moduleConfig, mixPort, + connectedOnly)); + if (mSkipTestReason.empty()) break; + } + } + + void SetUpPortConfigForMixPortOrConfig( + IModule* module, ModuleConfig* moduleConfig, const AudioPort& initialMixPort, + bool connectedOnly, const std::optional& mixPortConfig = {}) { + if (mixPortConfig.has_value() && !connectedOnly) { + // Connecting an external device may cause change in mix port profiles and the provided + // config may become invalid. + LOG(FATAL) << __func__ << ": when specifying a mix port config, it is not allowed " + << "to change connected devices, thus `connectedOnly` must be `true`"; + } + std::optional connectedDevicePort; + ASSERT_NO_FATAL_FAILURE(SetUpDevicePortForMixPort(module, moduleConfig, initialMixPort, + connectedOnly, &connectedDevicePort)); + if (!mSkipTestReason.empty()) return; + if (mixPortConfig.has_value()) { + ASSERT_NO_FATAL_FAILURE( + SetUpPortConfig(module, moduleConfig, *mixPortConfig, *connectedDevicePort)); + } else { + // If an external device was connected, the profiles of the mix port might have changed. + AudioPort mixPort; + ASSERT_NO_FATAL_FAILURE(module->getAudioPort(initialMixPort.id, &mixPort)); + ASSERT_NO_FATAL_FAILURE( + SetUpPortConfig(module, moduleConfig, mixPort, *connectedDevicePort)); + } + } + + void SetUpPortConfig(IModule* module, ModuleConfig* moduleConfig, const AudioPort& mixPort, + const AudioPort& devicePort) { + auto mixPortConfig = moduleConfig->getSingleConfigForMixPort(mIsInput, mixPort); + ASSERT_TRUE(mixPortConfig.has_value()) + << "Unable to generate port config for mix port " << mixPort.toString(); + ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module, moduleConfig, *mixPortConfig, devicePort)); + } + void SetUpPortConfig(IModule* module, ModuleConfig* moduleConfig, + const AudioPortConfig& mixPortConfig, const AudioPort& devicePort) { + ASSERT_NO_FATAL_FAILURE(SetUpPatch(module, moduleConfig, mixPortConfig, devicePort)); + mStream = std::make_unique>(mMixPortConfig->get()); + ASSERT_NO_FATAL_FAILURE(mStream->SetUpPortConfig(module)); + } + + ScopedAStatus SetUpStreamNoChecks(IModule* module) { + return mStream->SetUpNoChecks(module, getMinimumStreamBufferSizeFrames()); + } + void SetUpStream(IModule* module) { + ASSERT_NO_FATAL_FAILURE(mStream->SetUpStream(module, getMinimumStreamBufferSizeFrames())); + } + + void SetUpStreamForDevicePort(IModule* module, ModuleConfig* moduleConfig, + const AudioPort& devicePort, bool connectedOnly = false) { + ASSERT_NO_FATAL_FAILURE( + SetUpPortConfigForDevicePort(module, moduleConfig, devicePort, connectedOnly)); + if (!mSkipTestReason.empty()) return; + ASSERT_NO_FATAL_FAILURE(SetUpStream(module)); + } + void SetUpStreamForAnyMixPort(IModule* module, ModuleConfig* moduleConfig, + bool connectedOnly = false) { + ASSERT_NO_FATAL_FAILURE(SetUpPortConfigAnyMixPort(module, moduleConfig, connectedOnly)); + if (!mSkipTestReason.empty()) return; + ASSERT_NO_FATAL_FAILURE(SetUpStream(module)); + } + void SetUpStreamForMixPort(IModule* module, ModuleConfig* moduleConfig, + const AudioPort& mixPort, bool connectedOnly = false) { + ASSERT_NO_FATAL_FAILURE( + SetUpPortConfigForMixPortOrConfig(module, moduleConfig, mixPort, connectedOnly)); + if (!mSkipTestReason.empty()) return; + ASSERT_NO_FATAL_FAILURE(SetUpStream(module)); + } + void SetUpStreamForPortsPair(IModule* module, ModuleConfig* moduleConfig, + const AudioPort& mixPort, const AudioPort& devicePort) { + ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module, moduleConfig, mixPort, devicePort)); + if (!mSkipTestReason.empty()) return; + ASSERT_NO_FATAL_FAILURE(SetUpStream(module)); + } + void SetUpStreamForMixPortConfig(IModule* module, ModuleConfig* moduleConfig, + const AudioPortConfig& mixPortConfig) { + // Since mix port configs may change after connecting an external device, + // only connected device ports are considered. + constexpr bool connectedOnly = true; + const auto& ports = moduleConfig->getMixPorts(mIsInput, connectedOnly); + const auto mixPortIt = findById(ports, mixPortConfig.portId); + ASSERT_NE(mixPortIt, ports.end()) << "Port id " << mixPortConfig.portId << " not found"; + ASSERT_NO_FATAL_FAILURE(SetUpPortConfigForMixPortOrConfig(module, moduleConfig, *mixPortIt, + connectedOnly, mixPortConfig)); + if (!mSkipTestReason.empty()) return; + ASSERT_NO_FATAL_FAILURE(SetUpStream(module)); + } + void SetUpPatchForMixPortConfig(IModule* module, ModuleConfig* moduleConfig, + const AudioPortConfig& mixPortConfig) { + constexpr bool connectedOnly = true; + const auto& ports = moduleConfig->getMixPorts(mIsInput, connectedOnly); + const auto mixPortIt = findById(ports, mixPortConfig.portId); + ASSERT_NE(mixPortIt, ports.end()) << "Port id " << mixPortConfig.portId << " not found"; + std::optional connectedDevicePort; + ASSERT_NO_FATAL_FAILURE(SetUpDevicePortForMixPort(module, moduleConfig, *mixPortIt, + connectedOnly, &connectedDevicePort)); + if (!mSkipTestReason.empty()) return; + ASSERT_NO_FATAL_FAILURE( + SetUpPatch(module, moduleConfig, mixPortConfig, *connectedDevicePort)); + } + + void ReconnectPatch(IModule* module) { + mPatch = std::make_unique(mIsInput, mMixPortConfig->get(), + mDevicePortConfig->get()); + ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(module)); + } + void TeardownPatch() { mPatch.reset(); } + // Assuming that the patch is set up, while the stream isn't yet, + // tear the patch down and set up stream. + void TeardownPatchSetUpStream(IModule* module) { + const int32_t bufferSize = getMinimumStreamBufferSizeFrames(); + ASSERT_NO_FATAL_FAILURE(TeardownPatch()); + mStream = std::make_unique>(mMixPortConfig->get()); + ASSERT_NO_FATAL_FAILURE(mStream->SetUpPortConfig(module)); + ASSERT_NO_FATAL_FAILURE(mStream->SetUpStream(module, bufferSize)); + } + + const AudioDevice& getDevice() const { return mDevice; } + int32_t getMinimumStreamBufferSizeFrames() const { + return mPatch->getMinimumStreamBufferSizeFrames(); + } + const AudioPatch& getPatch() const { return mPatch->get(); } + const AudioPortConfig& getPortConfig() const { return mMixPortConfig->get(); } + int32_t getPortId() const { return mMixPortConfig->getId(); } + Stream* getStream() const { return mStream->get(); } + const StreamContext* getStreamContext() const { return mStream->getContext(); } + StreamEventReceiver* getStreamEventReceiver() { return mStream->getEventReceiver(); } + std::shared_ptr getStreamSharedPointer() const { return mStream->getSharedPointer(); } + const std::string& skipTestReason() const { return mSkipTestReason; } + + private: + void SetUpDevicePort(IModule* module, ModuleConfig* moduleConfig, + const std::set& devicePortIds, bool connectedOnly, + std::optional* connectedDevicePort) { + const auto attachedDevicePorts = moduleConfig->getAttachedDevicePorts(); + if (auto it = findAny(attachedDevicePorts, devicePortIds); + it != attachedDevicePorts.end()) { + *connectedDevicePort = *it; + LOG(DEBUG) << __func__ << ": found attached port " << it->toString(); + } + const auto connectedDevicePorts = moduleConfig->getConnectedExternalDevicePorts(); + if (auto it = findAny(connectedDevicePorts, devicePortIds); + it != connectedDevicePorts.end()) { + *connectedDevicePort = *it; + LOG(DEBUG) << __func__ << ": found connected port " << it->toString(); + } + if (!connectedOnly && !connectedDevicePort->has_value()) { + const auto externalDevicePorts = moduleConfig->getExternalDevicePorts(); + if (auto it = findAny(externalDevicePorts, devicePortIds); + it != externalDevicePorts.end()) { + AudioPort portWithData = GenerateUniqueDeviceAddress(*it); + mPortConnected = std::make_unique(portWithData); + ASSERT_NO_FATAL_FAILURE(mPortConnected->SetUp(module, moduleConfig)); + *connectedDevicePort = mPortConnected->get(); + LOG(DEBUG) << __func__ << ": connected port " << mPortConnected->get().toString(); + } + } + } + void SetUpDevicePortForMixPort(IModule* module, ModuleConfig* moduleConfig, + const AudioPort& mixPort, bool connectedOnly, + std::optional* connectedDevicePort) { + const auto devicePorts = + moduleConfig->getRoutableDevicePortsForMixPort(mixPort, connectedOnly); + if (devicePorts.empty()) { + mSkipTestReason = std::string("No routable device ports found for mix port id ") + .append(std::to_string(mixPort.id)); + LOG(DEBUG) << __func__ << ": " << mSkipTestReason; + return; + }; + ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(module, moduleConfig, + extractIds(devicePorts), connectedOnly, + connectedDevicePort)); + if (!connectedDevicePort->has_value()) { + mSkipTestReason = std::string("Unable to find a device port pair for mix port id ") + .append(std::to_string(mixPort.id)); + LOG(DEBUG) << __func__ << ": " << mSkipTestReason; + return; + } + } + void SetUpPortConfigForDevicePort(IModule* module, ModuleConfig* moduleConfig, + const AudioPort& devicePort, bool connectedOnly) { + std::optional connectedDevicePort; + ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(module, moduleConfig, {devicePort.id}, + connectedOnly, &connectedDevicePort)); + if (!connectedDevicePort.has_value()) { + mSkipTestReason = std::string("Device port id ") + .append(std::to_string(devicePort.id)) + .append(" is not attached and can not be connected"); + return; + } + const auto mixPorts = moduleConfig->getRoutableMixPortsForDevicePort( + *connectedDevicePort, true /*connectedOnly*/); + if (mixPorts.empty()) { + mSkipTestReason = std::string("No routable mix ports found for device port id ") + .append(std::to_string(devicePort.id)); + return; + } + ASSERT_NO_FATAL_FAILURE( + SetUpPortConfig(module, moduleConfig, *mixPorts.begin(), *connectedDevicePort)); + } + void SetUpPatch(IModule* module, ModuleConfig* moduleConfig, + const AudioPortConfig& mixPortConfig, const AudioPort& devicePort) { + mMixPortConfig = std::make_unique(mixPortConfig); + ASSERT_NO_FATAL_FAILURE(mMixPortConfig->SetUp(module)); + mDevicePortConfig = std::make_unique( + moduleConfig->getSingleConfigForDevicePort(devicePort)); + ASSERT_NO_FATAL_FAILURE(mDevicePortConfig->SetUp(module)); + mDevice = devicePort.ext.get().device; + mPatch = std::make_unique(mIsInput, mMixPortConfig->get(), + mDevicePortConfig->get()); + ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(module)); + } + + const bool mIsInput; + std::string mSkipTestReason; + std::unique_ptr mPortConnected; + AudioDevice mDevice; + std::unique_ptr mMixPortConfig; + std::unique_ptr mDevicePortConfig; + std::unique_ptr mPatch; + std::unique_ptr> mStream; +}; + template class AudioStream : public AudioCoreModule { public: @@ -2385,16 +2880,15 @@ class AudioStream : public AudioCoreModule { } void GetStreamCommon() { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits::is_input); - if (!portConfig.has_value()) { - GTEST_SKIP() << "No mix port for attached devices"; + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get())); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; } - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); std::shared_ptr streamCommon1; - EXPECT_IS_OK(stream.get()->getStreamCommon(&streamCommon1)); + EXPECT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon1)); std::shared_ptr streamCommon2; - EXPECT_IS_OK(stream.get()->getStreamCommon(&streamCommon2)); + EXPECT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon2)); ASSERT_NE(nullptr, streamCommon1); ASSERT_NE(nullptr, streamCommon2); EXPECT_EQ(streamCommon1->asBinder(), streamCommon2->asBinder()) @@ -2402,31 +2896,31 @@ class AudioStream : public AudioCoreModule { } void CloseTwice() { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits::is_input); - if (!portConfig.has_value()) { - GTEST_SKIP() << "No mix port for attached devices"; - } std::shared_ptr heldStream; { - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); - heldStream = stream.getSharedPointer(); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE( + stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get())); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; + } + heldStream = stream.getStreamSharedPointer(); } EXPECT_STATUS(EX_ILLEGAL_STATE, WithStream::callClose(heldStream)) << "when closing the stream twice"; } void PrepareToCloseTwice() { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits::is_input); - if (!portConfig.has_value()) { - GTEST_SKIP() << "No mix port for attached devices"; - } std::shared_ptr heldStreamCommon; { - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE( + stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get())); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; + } std::shared_ptr streamCommon; - ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon)); + ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon)); heldStreamCommon = streamCommon; EXPECT_IS_OK(streamCommon->prepareToClose()); EXPECT_IS_OK(streamCommon->prepareToClose()) @@ -2439,9 +2933,13 @@ class AudioStream : public AudioCoreModule { void OpenAllConfigs() { const auto allPortConfigs = moduleConfig->getPortConfigsForMixPorts(IOTraits::is_input); + if (allPortConfigs.empty()) { + GTEST_SKIP() << "No mix ports for attached devices"; + } for (const auto& portConfig : allPortConfigs) { - WithStream stream(portConfig); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPortConfig( + module.get(), moduleConfig.get(), portConfig)); } } @@ -2461,22 +2959,21 @@ class AudioStream : public AudioCoreModule { void OpenInvalidDirection() { // Important! The direction of the port config must be reversed. - const auto portConfig = - moduleConfig->getSingleConfigForMixPort(!IOTraits::is_input); - if (!portConfig.has_value()) { - GTEST_SKIP() << "No mix port for attached devices"; + StreamFixture stream(!IOTraits::is_input); + ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfigAnyMixPort(module.get(), moduleConfig.get(), + false /*connectedOnly*/)); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; } - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get())); - EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, - stream.SetUpNoChecks(module.get(), kDefaultBufferSizeFrames)) + EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, stream.SetUpStreamNoChecks(module.get())) << "port config ID " << stream.getPortId(); - EXPECT_EQ(nullptr, stream.get()); + EXPECT_EQ(nullptr, stream.getStream()); } void OpenOverMaxCount() { + constexpr bool connectedOnly = true; constexpr bool isInput = IOTraits::is_input; - auto ports = moduleConfig->getMixPorts(isInput, true /*connectedOnly*/); + auto ports = moduleConfig->getMixPorts(isInput, connectedOnly); bool hasSingleRun = false; for (const auto& port : ports) { const size_t maxStreamCount = port.ext.get().maxOpenStreamCount; @@ -2489,16 +2986,16 @@ class AudioStream : public AudioCoreModule { continue; } hasSingleRun = true; - std::optional> streamWraps[maxStreamCount + 1]; + StreamFixture streams[maxStreamCount + 1]; for (size_t i = 0; i <= maxStreamCount; ++i) { - streamWraps[i].emplace(portConfigs[i]); - WithStream& stream = streamWraps[i].value(); + ASSERT_NO_FATAL_FAILURE(streams[i].SetUpPortConfigForMixPortOrConfig( + module.get(), moduleConfig.get(), port, connectedOnly, portConfigs[i])); + ASSERT_EQ("", streams[i].skipTestReason()); + auto& stream = streams[i]; if (i < maxStreamCount) { - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + ASSERT_NO_FATAL_FAILURE(stream.SetUpStream(module.get())); } else { - ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get())); - EXPECT_STATUS(EX_ILLEGAL_STATE, - stream.SetUpNoChecks(module.get(), kDefaultBufferSizeFrames)) + EXPECT_STATUS(EX_ILLEGAL_STATE, stream.SetUpStreamNoChecks(module.get())) << "port config ID " << stream.getPortId() << ", maxOpenStreamCount is " << maxStreamCount; } @@ -2518,12 +3015,11 @@ class AudioStream : public AudioCoreModule { } void ResetPortConfigWithOpenStream() { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits::is_input); - if (!portConfig.has_value()) { - GTEST_SKIP() << "No mix port for attached devices"; + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get())); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; } - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); EXPECT_STATUS(EX_ILLEGAL_STATE, module->resetAudioPortConfig(stream.getPortId())) << "port config ID " << stream.getPortId(); } @@ -2537,14 +3033,13 @@ class AudioStream : public AudioCoreModule { } void UpdateHwAvSyncId() { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits::is_input); - if (!portConfig.has_value()) { - GTEST_SKIP() << "No mix port for attached devices"; + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get())); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; } - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); std::shared_ptr streamCommon; - ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon)); + ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon)); ASSERT_NE(nullptr, streamCommon); const auto kStatuses = {EX_NONE, EX_ILLEGAL_ARGUMENT, EX_ILLEGAL_STATE}; for (const auto id : {-100, -1, 0, 1, 100}) { @@ -2557,14 +3052,13 @@ class AudioStream : public AudioCoreModule { } void GetVendorParameters() { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits::is_input); - if (!portConfig.has_value()) { - GTEST_SKIP() << "No mix port for attached devices"; + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get())); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; } - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); std::shared_ptr streamCommon; - ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon)); + ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon)); ASSERT_NE(nullptr, streamCommon); bool isGetterSupported = false; @@ -2578,14 +3072,13 @@ class AudioStream : public AudioCoreModule { } void SetVendorParameters() { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits::is_input); - if (!portConfig.has_value()) { - GTEST_SKIP() << "No mix port for attached devices"; + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get())); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; } - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); std::shared_ptr streamCommon; - ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon)); + ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon)); ASSERT_NE(nullptr, streamCommon); bool isSupported = false; @@ -2596,32 +3089,37 @@ class AudioStream : public AudioCoreModule { } void HwGainHwVolume() { - const auto ports = - moduleConfig->getMixPorts(IOTraits::is_input, true /*connectedOnly*/); + // Since device connection emulation does not cover complete functionality, + // only use this test with connected devices. + constexpr bool connectedOnly = true; + const auto ports = moduleConfig->getMixPorts(IOTraits::is_input, connectedOnly); if (ports.empty()) { GTEST_SKIP() << "No mix ports"; } bool atLeastOneSupports = false; for (const auto& port : ports) { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port); - if (!portConfig.has_value()) continue; - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + SCOPED_TRACE(port.toString()); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), + port, connectedOnly)); + if (!stream.skipTestReason().empty()) continue; + const auto portConfig = stream.getPortConfig(); + SCOPED_TRACE(portConfig.toString()); std::vector> validValues, invalidValues; bool isSupported = false; if constexpr (IOTraits::is_input) { - GenerateTestArrays(getChannelCount(portConfig.value().channelMask.value()), + GenerateTestArrays(getChannelCount(portConfig.channelMask.value()), IStreamIn::HW_GAIN_MIN, IStreamIn::HW_GAIN_MAX, &validValues, &invalidValues); EXPECT_NO_FATAL_FAILURE(TestAccessors>( - stream.get(), &IStreamIn::getHwGain, &IStreamIn::setHwGain, validValues, - invalidValues, &isSupported)); + stream.getStream(), &IStreamIn::getHwGain, &IStreamIn::setHwGain, + validValues, invalidValues, &isSupported)); } else { - GenerateTestArrays(getChannelCount(portConfig.value().channelMask.value()), + GenerateTestArrays(getChannelCount(portConfig.channelMask.value()), IStreamOut::HW_VOLUME_MIN, IStreamOut::HW_VOLUME_MAX, &validValues, &invalidValues); EXPECT_NO_FATAL_FAILURE(TestAccessors>( - stream.get(), &IStreamOut::getHwVolume, &IStreamOut::setHwVolume, + stream.getStream(), &IStreamOut::getHwVolume, &IStreamOut::setHwVolume, validValues, invalidValues, &isSupported)); } if (isSupported) atLeastOneSupports = true; @@ -2635,19 +3133,22 @@ class AudioStream : public AudioCoreModule { // currently we can only pass a nullptr, and the HAL module must either reject // it as an invalid argument, or say that offloaded effects are not supported. void AddRemoveEffectInvalidArguments() { - const auto ports = - moduleConfig->getMixPorts(IOTraits::is_input, true /*connectedOnly*/); + constexpr bool connectedOnly = true; + const auto ports = moduleConfig->getMixPorts(IOTraits::is_input, connectedOnly); if (ports.empty()) { GTEST_SKIP() << "No mix ports"; } bool atLeastOneSupports = false; for (const auto& port : ports) { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port); - if (!portConfig.has_value()) continue; - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + SCOPED_TRACE(port.toString()); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), + port, connectedOnly)); + if (!stream.skipTestReason().empty()) continue; + const auto portConfig = stream.getPortConfig(); + SCOPED_TRACE(portConfig.toString()); std::shared_ptr streamCommon; - ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon)); + ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon)); ASSERT_NE(nullptr, streamCommon); ndk::ScopedAStatus addEffectStatus = streamCommon->addEffect(nullptr); ndk::ScopedAStatus removeEffectStatus = streamCommon->removeEffect(nullptr); @@ -2667,11 +3168,14 @@ class AudioStream : public AudioCoreModule { } void OpenTwiceSamePortConfigImpl(const AudioPortConfig& portConfig) { - WithStream stream1(portConfig); - ASSERT_NO_FATAL_FAILURE(stream1.SetUp(module.get(), kDefaultBufferSizeFrames)); + StreamFixture stream1; + ASSERT_NO_FATAL_FAILURE( + stream1.SetUpStreamForMixPortConfig(module.get(), moduleConfig.get(), portConfig)); + ASSERT_EQ("", stream1.skipTestReason()); WithStream stream2; - EXPECT_STATUS(EX_ILLEGAL_STATE, stream2.SetUpNoChecks(module.get(), stream1.getPortConfig(), - kDefaultBufferSizeFrames)) + EXPECT_STATUS(EX_ILLEGAL_STATE, + stream2.SetUpNoChecks(module.get(), stream1.getPortConfig(), + stream1.getMinimumStreamBufferSizeFrames())) << "when opening a stream twice for the same port config ID " << stream1.getPortId(); } @@ -2706,11 +3210,13 @@ class AudioStream : public AudioCoreModule { for (const auto& seq : sequences) { SCOPED_TRACE(std::string("Sequence ").append(seq.first)); LOG(DEBUG) << __func__ << ": Sequence " << seq.first; - WithStream stream(portConfig); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPortConfig( + module.get(), moduleConfig.get(), portConfig)); + ASSERT_EQ("", stream.skipTestReason()); StreamLogicDriverInvalidCommand driver(seq.second); - typename IOTraits::Worker worker(*stream.getContext(), &driver, - stream.getEventReceiver()); + typename IOTraits::Worker worker(*stream.getStreamContext(), &driver, + stream.getStreamEventReceiver()); LOG(DEBUG) << __func__ << ": starting worker..."; ASSERT_TRUE(worker.start()); LOG(DEBUG) << __func__ << ": joining worker..."; @@ -2763,63 +3269,59 @@ TEST_P(AudioStreamIn, ActiveMicrophones) { if (ports.empty()) { GTEST_SKIP() << "No input mix ports for attached devices"; } + bool atLeastOnePort = false; for (const auto& port : ports) { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port); - ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port"; - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); - { - // The port of the stream is not connected, thus the list of active mics must be empty. - std::vector activeMics; - EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics)); - EXPECT_TRUE(activeMics.empty()) << "a stream on an unconnected port returns a " - "non-empty list of active microphones"; - } - if (auto micDevicePorts = ModuleConfig::getBuiltInMicPorts( - moduleConfig->getConnectedSourceDevicesPortsForMixPort(port)); - !micDevicePorts.empty()) { - auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(micDevicePorts[0]); - WithAudioPatch patch(true /*isInput*/, stream.getPortConfig(), devicePortConfig); - ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get())); - std::vector activeMics; - EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics)); - EXPECT_FALSE(activeMics.empty()); - for (const auto& mic : activeMics) { - EXPECT_NE(micInfos.end(), - std::find_if(micInfos.begin(), micInfos.end(), - [&](const auto& micInfo) { return micInfo.id == mic.id; })) - << "active microphone \"" << mic.id << "\" is not listed in " - << "microphone infos returned by the module: " - << ::android::internal::ToString(micInfos); - EXPECT_NE(0UL, mic.channelMapping.size()) - << "No channels specified for the microphone \"" << mic.id << "\""; - } - } - { - // Now the port of the stream is not connected again, re-check that there are no - // active microphones. - std::vector activeMics; - EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics)); - EXPECT_TRUE(activeMics.empty()) << "a stream on an unconnected port returns a " - "non-empty list of active microphones"; - } + auto micDevicePorts = ModuleConfig::getBuiltInMicPorts( + moduleConfig->getConnectedSourceDevicesPortsForMixPort(port)); + if (micDevicePorts.empty()) continue; + atLeastOnePort = true; + SCOPED_TRACE(port.toString()); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForPortsPair(module.get(), moduleConfig.get(), + port, micDevicePorts[0])); + if (!stream.skipTestReason().empty()) continue; + std::vector activeMics; + EXPECT_IS_OK(stream.getStream()->getActiveMicrophones(&activeMics)); + EXPECT_FALSE(activeMics.empty()); + for (const auto& mic : activeMics) { + EXPECT_NE(micInfos.end(), + std::find_if(micInfos.begin(), micInfos.end(), + [&](const auto& micInfo) { return micInfo.id == mic.id; })) + << "active microphone \"" << mic.id << "\" is not listed in " + << "microphone infos returned by the module: " + << ::android::internal::ToString(micInfos); + EXPECT_NE(0UL, mic.channelMapping.size()) + << "No channels specified for the microphone \"" << mic.id << "\""; + } + stream.TeardownPatch(); + // Now the port of the stream is not connected, check that there are no active microphones. + std::vector emptyMics; + EXPECT_IS_OK(stream.getStream()->getActiveMicrophones(&emptyMics)); + EXPECT_TRUE(emptyMics.empty()) << "a stream on an unconnected port returns a " + "non-empty list of active microphones"; + } + if (!atLeastOnePort) { + GTEST_SKIP() << "No input mix ports could be routed to built-in microphone devices"; } } TEST_P(AudioStreamIn, MicrophoneDirection) { using MD = IStreamIn::MicrophoneDirection; - const auto ports = moduleConfig->getInputMixPorts(true /*connectedOnly*/); + constexpr bool connectedOnly = true; + const auto ports = moduleConfig->getInputMixPorts(connectedOnly); if (ports.empty()) { GTEST_SKIP() << "No input mix ports for attached devices"; } - bool isSupported = false; + bool isSupported = false, atLeastOnePort = false; for (const auto& port : ports) { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port); - ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port"; - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + SCOPED_TRACE(port.toString()); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port, + connectedOnly)); + if (!stream.skipTestReason().empty()) continue; + atLeastOnePort = true; EXPECT_NO_FATAL_FAILURE( - TestAccessors(stream.get(), &IStreamIn::getMicrophoneDirection, + TestAccessors(stream.getStream(), &IStreamIn::getMicrophoneDirection, &IStreamIn::setMicrophoneDirection, std::vector(enum_range().begin(), enum_range().end()), {}, &isSupported)); @@ -2828,21 +3330,27 @@ TEST_P(AudioStreamIn, MicrophoneDirection) { if (!isSupported) { GTEST_SKIP() << "Microphone direction is not supported"; } + if (!atLeastOnePort) { + GTEST_SKIP() << "No input mix ports could be routed to built-in microphone devices"; + } } TEST_P(AudioStreamIn, MicrophoneFieldDimension) { - const auto ports = moduleConfig->getInputMixPorts(true /*connectedOnly*/); + constexpr bool connectedOnly = true; + const auto ports = moduleConfig->getInputMixPorts(connectedOnly); if (ports.empty()) { GTEST_SKIP() << "No input mix ports for attached devices"; } - bool isSupported = false; + bool isSupported = false, atLeastOnePort = false; for (const auto& port : ports) { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port); - ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port"; - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + SCOPED_TRACE(port.toString()); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port, + connectedOnly)); + if (!stream.skipTestReason().empty()) continue; + atLeastOnePort = true; EXPECT_NO_FATAL_FAILURE(TestAccessors( - stream.get(), &IStreamIn::getMicrophoneFieldDimension, + stream.getStream(), &IStreamIn::getMicrophoneFieldDimension, &IStreamIn::setMicrophoneFieldDimension, {IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE, IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE / 2.0f, @@ -2859,6 +3367,9 @@ TEST_P(AudioStreamIn, MicrophoneFieldDimension) { if (!isSupported) { GTEST_SKIP() << "Microphone direction is not supported"; } + if (!atLeastOnePort) { + GTEST_SKIP() << "No input mix ports could be routed to built-in microphone devices"; + } } TEST_P(AudioStreamOut, OpenTwicePrimary) { @@ -2873,65 +3384,79 @@ TEST_P(AudioStreamOut, OpenTwicePrimary) { } TEST_P(AudioStreamOut, RequireOffloadInfo) { + constexpr bool connectedOnly = true; const auto offloadMixPorts = - moduleConfig->getOffloadMixPorts(true /*connectedOnly*/, true /*singlePort*/); + moduleConfig->getOffloadMixPorts(connectedOnly, true /*singlePort*/); if (offloadMixPorts.empty()) { GTEST_SKIP() << "No mix port for compressed offload that could be routed to attached devices"; } - const auto config = moduleConfig->getSingleConfigForMixPort(false, *offloadMixPorts.begin()); - ASSERT_TRUE(config.has_value()) << "No profiles specified for the compressed offload mix port"; - WithAudioPortConfig portConfig(config.value()); - ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get())); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfigForMixPortOrConfig( + module.get(), moduleConfig.get(), *offloadMixPorts.begin(), connectedOnly)); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; + } + const auto portConfig = stream.getPortConfig(); StreamDescriptor descriptor; - std::shared_ptr ignored; aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args; - args.portConfigId = portConfig.getId(); - args.sourceMetadata = GenerateSourceMetadata(portConfig.get()); + args.portConfigId = portConfig.id; + args.sourceMetadata = GenerateSourceMetadata(portConfig); args.bufferSizeFrames = kDefaultLargeBufferSizeFrames; aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret)) << "when no offload info is provided for a compressed offload mix port"; + if (ret.stream != nullptr) { + (void)WithStream::callClose(ret.stream); + } } TEST_P(AudioStreamOut, RequireAsyncCallback) { + constexpr bool connectedOnly = true; const auto nonBlockingMixPorts = - moduleConfig->getNonBlockingMixPorts(true /*connectedOnly*/, true /*singlePort*/); + moduleConfig->getNonBlockingMixPorts(connectedOnly, true /*singlePort*/); if (nonBlockingMixPorts.empty()) { GTEST_SKIP() << "No mix port for non-blocking output that could be routed to attached devices"; } - const auto config = - moduleConfig->getSingleConfigForMixPort(false, *nonBlockingMixPorts.begin()); - ASSERT_TRUE(config.has_value()) << "No profiles specified for the non-blocking mix port"; - WithAudioPortConfig portConfig(config.value()); - ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get())); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfigForMixPortOrConfig( + module.get(), moduleConfig.get(), *nonBlockingMixPorts.begin(), connectedOnly)); + if (auto reason = stream.skipTestReason(); !reason.empty()) { + GTEST_SKIP() << reason; + } + const auto portConfig = stream.getPortConfig(); StreamDescriptor descriptor; - std::shared_ptr ignored; aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args; - args.portConfigId = portConfig.getId(); - args.sourceMetadata = GenerateSourceMetadata(portConfig.get()); - args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig.get()); - args.bufferSizeFrames = kDefaultBufferSizeFrames; + args.portConfigId = portConfig.id; + args.sourceMetadata = GenerateSourceMetadata(portConfig); + args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig); + args.bufferSizeFrames = stream.getPatch().minimumStreamBufferSizeFrames; aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret; EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret)) << "when no async callback is provided for a non-blocking mix port"; + if (ret.stream != nullptr) { + (void)WithStream::callClose(ret.stream); + } } TEST_P(AudioStreamOut, AudioDescriptionMixLevel) { - const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/); + constexpr bool connectedOnly = true; + const auto ports = moduleConfig->getOutputMixPorts(connectedOnly); if (ports.empty()) { - GTEST_SKIP() << "No output mix ports"; + GTEST_SKIP() << "No output mix ports for attached devices"; } - bool atLeastOneSupports = false; + bool atLeastOneSupports = false, atLeastOnePort = false; for (const auto& port : ports) { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, port); - ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for output mix port"; - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + SCOPED_TRACE(port.toString()); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port, + connectedOnly)); + if (!stream.skipTestReason().empty()) continue; + atLeastOnePort = true; bool isSupported = false; EXPECT_NO_FATAL_FAILURE( - TestAccessors(stream.get(), &IStreamOut::getAudioDescriptionMixLevel, + TestAccessors(stream.getStream(), &IStreamOut::getAudioDescriptionMixLevel, &IStreamOut::setAudioDescriptionMixLevel, {IStreamOut::AUDIO_DESCRIPTION_MIX_LEVEL_MAX, IStreamOut::AUDIO_DESCRIPTION_MIX_LEVEL_MAX - 1, 0, @@ -2941,48 +3466,60 @@ TEST_P(AudioStreamOut, AudioDescriptionMixLevel) { &isSupported)); if (isSupported) atLeastOneSupports = true; } + if (!atLeastOnePort) { + GTEST_SKIP() << "No output mix ports could be routed to devices"; + } if (!atLeastOneSupports) { GTEST_SKIP() << "Audio description mix level is not supported"; } } TEST_P(AudioStreamOut, DualMonoMode) { - const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/); + constexpr bool connectedOnly = true; + const auto ports = moduleConfig->getOutputMixPorts(connectedOnly); if (ports.empty()) { - GTEST_SKIP() << "No output mix ports"; + GTEST_SKIP() << "No output mix ports for attached devices"; } - bool atLeastOneSupports = false; + bool atLeastOneSupports = false, atLeastOnePort = false; for (const auto& port : ports) { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, port); - ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for output mix port"; - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + SCOPED_TRACE(port.toString()); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port, + connectedOnly)); + if (!stream.skipTestReason().empty()) continue; + atLeastOnePort = true; bool isSupported = false; EXPECT_NO_FATAL_FAILURE(TestAccessors( - stream.get(), &IStreamOut::getDualMonoMode, &IStreamOut::setDualMonoMode, + stream.getStream(), &IStreamOut::getDualMonoMode, &IStreamOut::setDualMonoMode, std::vector(enum_range().begin(), enum_range().end()), {}, &isSupported)); if (isSupported) atLeastOneSupports = true; } + if (!atLeastOnePort) { + GTEST_SKIP() << "No output mix ports could be routed to devices"; + } if (!atLeastOneSupports) { GTEST_SKIP() << "Audio dual mono mode is not supported"; } } TEST_P(AudioStreamOut, LatencyMode) { - const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/); + constexpr bool connectedOnly = true; + const auto ports = moduleConfig->getOutputMixPorts(connectedOnly); if (ports.empty()) { - GTEST_SKIP() << "No output mix ports"; + GTEST_SKIP() << "No output mix ports for attached devices"; } - bool atLeastOneSupports = false; + bool atLeastOneSupports = false, atLeastOnePort = false; for (const auto& port : ports) { - const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, port); - ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for output mix port"; - WithStream stream(portConfig.value()); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + SCOPED_TRACE(port.toString()); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port, + connectedOnly)); + if (!stream.skipTestReason().empty()) continue; + atLeastOnePort = true; std::vector supportedModes; - ndk::ScopedAStatus status = stream.get()->getRecommendedLatencyModes(&supportedModes); + ndk::ScopedAStatus status = stream.getStream()->getRecommendedLatencyModes(&supportedModes); if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) continue; atLeastOneSupports = true; if (!status.isOk()) { @@ -2994,7 +3531,7 @@ TEST_P(AudioStreamOut, LatencyMode) { enum_range().end()); for (const auto mode : supportedModes) { unsupportedModes.erase(mode); - ndk::ScopedAStatus status = stream.get()->setLatencyMode(mode); + ndk::ScopedAStatus status = stream.getStream()->setLatencyMode(mode); if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) { ADD_FAILURE() << "When latency modes are supported, both getRecommendedLatencyModes" << " and setLatencyMode must be supported"; @@ -3002,12 +3539,15 @@ TEST_P(AudioStreamOut, LatencyMode) { EXPECT_IS_OK(status) << "Setting of supported latency mode must succeed"; } for (const auto mode : unsupportedModes) { - EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, stream.get()->setLatencyMode(mode)); + EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, stream.getStream()->setLatencyMode(mode)); } } if (!atLeastOneSupports) { GTEST_SKIP() << "Audio latency modes are not supported"; } + if (!atLeastOnePort) { + GTEST_SKIP() << "No output mix ports could be routed to devices"; + } } TEST_P(AudioStreamOut, PlaybackRate) { @@ -3309,29 +3849,22 @@ class AudioStreamIo : public AudioCoreModuleBase, } } - bool ValidateObservablePosition(const AudioPortConfig& devicePortConfig) { - return !isTelephonyDeviceType( - devicePortConfig.ext.get().device.type.type); + bool ValidateObservablePosition(const AudioDevice& device) { + return !isTelephonyDeviceType(device.type.type); } // Set up a patch first, then open a stream. void RunStreamIoCommandsImplSeq1(const AudioPortConfig& portConfig, std::shared_ptr commandsAndStates, bool validatePositionIncrease) { - auto devicePorts = moduleConfig->getConnectedDevicesPortsForMixPort( - IOTraits::is_input, portConfig); - ASSERT_FALSE(devicePorts.empty()); - auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]); - SCOPED_TRACE(devicePortConfig.toString()); - WithAudioPatch patch(IOTraits::is_input, portConfig, devicePortConfig); - ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get())); - - WithStream stream(patch.getPortConfig(IOTraits::is_input)); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE( + stream.SetUpStreamForMixPortConfig(module.get(), moduleConfig.get(), portConfig)); + ASSERT_EQ("", stream.skipTestReason()); StreamLogicDefaultDriver driver(commandsAndStates, - stream.getContext()->getFrameSizeBytes()); - typename IOTraits::Worker worker(*stream.getContext(), &driver, - stream.getEventReceiver()); + stream.getStreamContext()->getFrameSizeBytes()); + typename IOTraits::Worker worker(*stream.getStreamContext(), &driver, + stream.getStreamEventReceiver()); LOG(DEBUG) << __func__ << ": starting worker..."; ASSERT_TRUE(worker.start()); @@ -3339,7 +3872,7 @@ class AudioStreamIo : public AudioCoreModuleBase, worker.join(); EXPECT_FALSE(worker.hasError()) << worker.getError(); EXPECT_EQ("", driver.getUnexpectedStateTransition()); - if (ValidateObservablePosition(devicePortConfig)) { + if (ValidateObservablePosition(stream.getDevice())) { if (validatePositionIncrease) { EXPECT_TRUE(driver.hasObservablePositionIncrease()); } @@ -3347,24 +3880,21 @@ class AudioStreamIo : public AudioCoreModuleBase, } } - // Open a stream, then set up a patch for it. + // Open a stream, then set up a patch for it. Since first it is needed to get + // the minimum buffer size, a preliminary patch is set up, then removed. void RunStreamIoCommandsImplSeq2(const AudioPortConfig& portConfig, std::shared_ptr commandsAndStates, bool validatePositionIncrease) { - WithStream stream(portConfig); - ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + StreamFixture stream; + ASSERT_NO_FATAL_FAILURE( + stream.SetUpPatchForMixPortConfig(module.get(), moduleConfig.get(), portConfig)); + ASSERT_EQ("", stream.skipTestReason()); + ASSERT_NO_FATAL_FAILURE(stream.TeardownPatchSetUpStream(module.get())); StreamLogicDefaultDriver driver(commandsAndStates, - stream.getContext()->getFrameSizeBytes()); - typename IOTraits::Worker worker(*stream.getContext(), &driver, - stream.getEventReceiver()); - - auto devicePorts = moduleConfig->getConnectedDevicesPortsForMixPort( - IOTraits::is_input, portConfig); - ASSERT_FALSE(devicePorts.empty()); - auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]); - SCOPED_TRACE(devicePortConfig.toString()); - WithAudioPatch patch(IOTraits::is_input, stream.getPortConfig(), devicePortConfig); - ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get())); + stream.getStreamContext()->getFrameSizeBytes()); + typename IOTraits::Worker worker(*stream.getStreamContext(), &driver, + stream.getStreamEventReceiver()); + ASSERT_NO_FATAL_FAILURE(stream.ReconnectPatch(module.get())); LOG(DEBUG) << __func__ << ": starting worker..."; ASSERT_TRUE(worker.start()); @@ -3372,7 +3902,7 @@ class AudioStreamIo : public AudioCoreModuleBase, worker.join(); EXPECT_FALSE(worker.hasError()) << worker.getError(); EXPECT_EQ("", driver.getUnexpectedStateTransition()); - if (ValidateObservablePosition(devicePortConfig)) { + if (ValidateObservablePosition(stream.getDevice())) { if (validatePositionIncrease) { EXPECT_TRUE(driver.hasObservablePositionIncrease()); } @@ -3494,9 +4024,12 @@ class AudioModulePatch : public AudioCoreModule { patches.push_back( std::make_unique(srcSink.first, srcSink.second)); EXPECT_NO_FATAL_FAILURE(patches[patches.size() - 1]->SetUp(module.get())); + EXPECT_NO_FATAL_FAILURE( + patches[patches.size() - 1]->VerifyAgainstAllPatches(module.get())); } else { WithAudioPatch patch(srcSink.first, srcSink.second); EXPECT_NO_FATAL_FAILURE(patch.SetUp(module.get())); + EXPECT_NO_FATAL_FAILURE(patch.VerifyAgainstAllPatches(module.get())); } } } @@ -3517,6 +4050,29 @@ class AudioModulePatch : public AudioCoreModule { } } + void UpdatePatchPorts(bool isInput) { + auto srcSinkGroups = moduleConfig->getRoutableSrcSinkGroups(isInput); + if (srcSinkGroups.empty()) { + GTEST_SKIP() << "No routes to any attached " << direction(isInput, false) << " devices"; + } + bool hasAtLeastOnePair = false; + for (const auto& srcSinkGroup : srcSinkGroups) { + const auto& srcSinks = srcSinkGroup.second; + if (srcSinks.size() < 2) continue; + hasAtLeastOnePair = true; + const auto& pair1 = srcSinks[0]; + const auto& pair2 = srcSinks[1]; + WithAudioPatch patch(pair1.first, pair1.second); + ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get())); + WithAudioPatch update(patch, pair2.first, pair2.second); + EXPECT_NO_FATAL_FAILURE(update.SetUp(module.get())); + EXPECT_NO_FATAL_FAILURE(update.VerifyAgainstAllPatches(module.get())); + } + if (!hasAtLeastOnePair) { + GTEST_SKIP() << "No routes with multiple sources"; + } + } + void UpdateInvalidPatchId(bool isInput) { auto srcSinkGroups = moduleConfig->getRoutableSrcSinkGroups(isInput); if (srcSinkGroups.empty()) { @@ -3556,6 +4112,7 @@ TEST_PATCH_BOTH_DIRECTIONS(SetNonRoutablePatch); TEST_PATCH_BOTH_DIRECTIONS(SetPatch); TEST_PATCH_BOTH_DIRECTIONS(UpdateInvalidPatchId); TEST_PATCH_BOTH_DIRECTIONS(UpdatePatch); +TEST_PATCH_BOTH_DIRECTIONS(UpdatePatchPorts); TEST_P(AudioModulePatch, ResetInvalidPatchId) { std::set patchIds; @@ -4036,174 +4593,164 @@ template class WithRemoteSubmix { public: WithRemoteSubmix() = default; - WithRemoteSubmix(AudioDeviceAddress address) : mAddress(address) {} + explicit WithRemoteSubmix(AudioDeviceAddress address) : mAddress(address) {} WithRemoteSubmix(const WithRemoteSubmix&) = delete; WithRemoteSubmix& operator=(const WithRemoteSubmix&) = delete; - std::optional getAudioPort() { - AudioDeviceType deviceType = IOTraits::is_input ? AudioDeviceType::IN_SUBMIX - : AudioDeviceType::OUT_SUBMIX; - auto ports = mModuleConfig->getAudioPortsForDeviceTypes( - std::vector{deviceType}, - AudioDeviceDescription::CONNECTION_VIRTUAL); - if (!ports.empty()) return ports.front(); - return {}; - } - /* Connect remote submix external device */ - void SetUpPortConnection() { - auto port = getAudioPort(); - ASSERT_TRUE(port.has_value()) << "Device AudioPort for remote submix not found"; - if (mAddress.has_value()) { - port.value().ext.template get().device.address = - mAddress.value(); - } else { - port = GenerateUniqueDeviceAddress(port.value()); - } - mConnectedPort = std::make_unique(port.value()); - ASSERT_NO_FATAL_FAILURE(mConnectedPort->SetUp(mModule, mModuleConfig)); - } - AudioDeviceAddress getAudioDeviceAddress() { - if (!mAddress.has_value()) { - mAddress = mConnectedPort->get() - .ext.template get() - .device.address; - } - return mAddress.value(); - } - /* Get mix port config for stream and setup patch for it. */ - void SetupPatch() { - const auto portConfig = - mModuleConfig->getSingleConfigForMixPort(IOTraits::is_input); - if (!portConfig.has_value()) { - LOG(DEBUG) << __func__ << ": portConfig not found"; - mSkipTest = true; - return; + + static std::optional getRemoteSubmixAudioPort( + ModuleConfig* moduleConfig, + const std::optional& address = std::nullopt) { + auto ports = + moduleConfig->getRemoteSubmixPorts(IOTraits::is_input, true /*singlePort*/); + if (ports.empty()) return {}; + AudioPort port = ports.front(); + if (address) { + port.ext.template get().device.address = address.value(); } - auto devicePortConfig = mModuleConfig->getSingleConfigForDevicePort(mConnectedPort->get()); - mPatch = std::make_unique(IOTraits::is_input, portConfig.value(), - devicePortConfig); - ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(mModule)); + return port; } + void SetUp(IModule* module, ModuleConfig* moduleConfig) { - mModule = module; - mModuleConfig = moduleConfig; - ASSERT_NO_FATAL_FAILURE(SetUpPortConnection()); - ASSERT_NO_FATAL_FAILURE(SetupPatch()); - if (!mSkipTest) { - // open stream - mStream = std::make_unique>( - mPatch->getPortConfig(IOTraits::is_input)); - ASSERT_NO_FATAL_FAILURE( - mStream->SetUp(mModule, AudioCoreModuleBase::kDefaultBufferSizeFrames)); - } + auto devicePort = getRemoteSubmixAudioPort(moduleConfig, mAddress); + ASSERT_TRUE(devicePort.has_value()) << "Device port for remote submix device not found"; + ASSERT_NO_FATAL_FAILURE(SetUp(module, moduleConfig, *devicePort)); } - void sendBurstCommands() { - const StreamContext* context = mStream->getContext(); - StreamLogicDefaultDriver driver(makeBurstCommands(true), context->getFrameSizeBytes()); - typename IOTraits::Worker worker(*context, &driver, mStream->getEventReceiver()); - LOG(DEBUG) << __func__ << ": starting worker..."; - ASSERT_TRUE(worker.start()); - LOG(DEBUG) << __func__ << ": joining worker..."; - worker.join(); - EXPECT_FALSE(worker.hasError()) << worker.getError(); - EXPECT_EQ("", driver.getUnexpectedStateTransition()); + void SendBurstCommandsStartWorker() { + const StreamContext* context = mStream->getStreamContext(); + mWorkerDriver = std::make_unique(makeBurstCommands(true), + context->getFrameSizeBytes()); + mWorker = std::make_unique::Worker>( + *context, mWorkerDriver.get(), mStream->getStreamEventReceiver()); + LOG(DEBUG) << __func__ << ": starting " << IOTraits::directionStr << " worker..."; + ASSERT_TRUE(mWorker->start()); + } + + void SendBurstCommandsJoinWorker() { + // Must call 'prepareToClose' before attempting to join because the stream may be + // stuck due to absence of activity from the other side of the remote submix pipe. + std::shared_ptr common; + ASSERT_IS_OK(mStream->getStream()->getStreamCommon(&common)); + ASSERT_IS_OK(common->prepareToClose()); + LOG(DEBUG) << __func__ << ": joining " << IOTraits::directionStr << " worker..."; + mWorker->join(); + EXPECT_FALSE(mWorker->hasError()) << mWorker->getError(); + EXPECT_EQ("", mWorkerDriver->getUnexpectedStateTransition()); if (IOTraits::is_input) { - EXPECT_TRUE(driver.hasObservablePositionIncrease()); + EXPECT_TRUE(mWorkerDriver->hasObservablePositionIncrease()); } - EXPECT_FALSE(driver.hasRetrogradeObservablePosition()); + EXPECT_FALSE(mWorkerDriver->hasRetrogradeObservablePosition()); + mWorker.reset(); + mWorkerDriver.reset(); + } + + void SendBurstCommands() { + ASSERT_NO_FATAL_FAILURE(SendBurstCommandsStartWorker()); + ASSERT_NO_FATAL_FAILURE(SendBurstCommandsJoinWorker()); } - bool skipTest() { return mSkipTest; } + + std::optional getAudioDeviceAddress() const { return mAddress; } + std::string skipTestReason() const { return mStream->skipTestReason(); } private: - bool mSkipTest = false; - IModule* mModule = nullptr; - ModuleConfig* mModuleConfig = nullptr; + void SetUp(IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort) { + mStream = std::make_unique>(); + ASSERT_NO_FATAL_FAILURE( + mStream->SetUpStreamForDevicePort(module, moduleConfig, devicePort)); + mAddress = mStream->getDevice().address; + } + std::optional mAddress; - std::unique_ptr mConnectedPort; - std::unique_ptr mPatch; - std::unique_ptr> mStream; + std::unique_ptr> mStream; + std::unique_ptr mWorkerDriver; + std::unique_ptr::Worker> mWorker; }; class AudioModuleRemoteSubmix : public AudioCoreModule { public: void SetUp() override { - ASSERT_NO_FATAL_FAILURE(AudioCoreModule::SetUp()); + // Turn off "debug" which enables connections simulation. Since devices of the remote + // submix module are virtual, there is no need for simulation. + ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam(), false /*setUpDebug*/)); ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); } - - void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); } }; TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenNoInput) { - // open output stream WithRemoteSubmix streamOut; ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get())); - if (streamOut.skipTest()) { - GTEST_SKIP() << "No mix port for attached devices"; - } - // write something to stream - ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands()); + // Note: here and in other tests any issue with connection attempts is considered as a problem. + ASSERT_EQ("", streamOut.skipTestReason()); + ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommands()); } TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenInputStuck) { - // open output stream WithRemoteSubmix streamOut; ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get())); - if (streamOut.skipTest()) { - GTEST_SKIP() << "No mix port for attached devices"; - } + ASSERT_EQ("", streamOut.skipTestReason()); + auto address = streamOut.getAudioDeviceAddress(); + ASSERT_TRUE(address.has_value()); - // open input stream - WithRemoteSubmix streamIn(streamOut.getAudioDeviceAddress()); + WithRemoteSubmix streamIn(address.value()); ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get())); - if (streamIn.skipTest()) { - GTEST_SKIP() << "No mix port for attached devices"; - } + ASSERT_EQ("", streamIn.skipTestReason()); - // write something to stream - ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands()); + ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommands()); } TEST_P(AudioModuleRemoteSubmix, OutputAndInput) { - // open output stream WithRemoteSubmix streamOut; ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get())); - if (streamOut.skipTest()) { - GTEST_SKIP() << "No mix port for attached devices"; - } + ASSERT_EQ("", streamOut.skipTestReason()); + auto address = streamOut.getAudioDeviceAddress(); + ASSERT_TRUE(address.has_value()); - // open input stream - WithRemoteSubmix streamIn(streamOut.getAudioDeviceAddress()); + WithRemoteSubmix streamIn(address.value()); ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get())); - if (streamIn.skipTest()) { - GTEST_SKIP() << "No mix port for attached devices"; - } + ASSERT_EQ("", streamIn.skipTestReason()); - // write something to stream - ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands()); - // read from input stream - ASSERT_NO_FATAL_FAILURE(streamIn.sendBurstCommands()); + // Start writing into the output stream. + ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommandsStartWorker()); + // Simultaneously, read from the input stream. + ASSERT_NO_FATAL_FAILURE(streamIn.SendBurstCommands()); + ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommandsJoinWorker()); +} + +TEST_P(AudioModuleRemoteSubmix, OpenInputMultipleTimes) { + WithRemoteSubmix streamOut; + ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get())); + ASSERT_EQ("", streamOut.skipTestReason()); + auto address = streamOut.getAudioDeviceAddress(); + ASSERT_TRUE(address.has_value()); + + const size_t streamInCount = 3; + std::vector>> streamIns(streamInCount); + for (size_t i = 0; i < streamInCount; i++) { + streamIns[i] = std::make_unique>(address.value()); + ASSERT_NO_FATAL_FAILURE(streamIns[i]->SetUp(module.get(), moduleConfig.get())); + ASSERT_EQ("", streamIns[i]->skipTestReason()); + } + // Start writing into the output stream. + ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommandsStartWorker()); + // Simultaneously, read from input streams. + for (size_t i = 0; i < streamInCount; i++) { + ASSERT_NO_FATAL_FAILURE(streamIns[i]->SendBurstCommandsStartWorker()); + } + for (size_t i = 0; i < streamInCount; i++) { + ASSERT_NO_FATAL_FAILURE(streamIns[i]->SendBurstCommandsJoinWorker()); + } + ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommandsJoinWorker()); + // Clean up input streams in the reverse order because the device connection is owned + // by the first one. + for (size_t i = streamInCount; i != 0; --i) { + streamIns[i - 1].reset(); + } } INSTANTIATE_TEST_SUITE_P(AudioModuleRemoteSubmixTest, AudioModuleRemoteSubmix, ::testing::ValuesIn(getRemoteSubmixModuleInstance())); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioModuleRemoteSubmix); -class TestExecutionTracer : public ::testing::EmptyTestEventListener { - public: - void OnTestStart(const ::testing::TestInfo& test_info) override { - TraceTestState("Started", test_info); - } - void OnTestEnd(const ::testing::TestInfo& test_info) override { - TraceTestState("Completed", test_info); - } - - private: - static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) { - LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name(); - } -}; - int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp index 225640e1fceb730b551cc51ec192fb9005df9ffe..523f20da99a9ee6a6087394ce01b146045ccb4a1 100644 --- a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp @@ -52,6 +52,7 @@ using aidl::android::hardware::audio::effect::Processing; using aidl::android::media::audio::common::AudioSource; using aidl::android::media::audio::common::AudioStreamType; using aidl::android::media::audio::common::AudioUuid; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /// Effect factory testing. class EffectFactoryTest : public testing::TestWithParam { @@ -303,6 +304,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp index 1876756437c1679f733e7072d877158c4d743aa8..aaf9ad4e74e013254e975f765d38b785ebc98d2c 100644 --- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp @@ -51,6 +51,7 @@ using aidl::android::media::audio::common::AudioDeviceDescription; using aidl::android::media::audio::common::AudioDeviceType; using aidl::android::media::audio::common::AudioMode; using aidl::android::media::audio::common::AudioSource; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; enum ParamName { PARAM_INSTANCE_NAME }; using EffectTestParam = std::tuple, Descriptor>>; @@ -595,8 +596,14 @@ TEST_P(AudioEffectTest, SetAndGetParameterVolume) { Parameter::Id id = Parameter::Id::make(Parameter::volumeStereo); Parameter::VolumeStereo volume = {.left = 10.0, .right = 10.0}; - ASSERT_NO_FATAL_FAILURE( - setAndGetParameter(id, Parameter::make(volume))); + if (mDescriptor.common.flags.volume == Flags::Volume::CTRL) { + Parameter get; + EXPECT_IS_OK(mEffect->setParameter(volume)); + EXPECT_IS_OK(mEffect->getParameter(id, &get)); + } else { + ASSERT_NO_FATAL_FAILURE( + setAndGetParameter(id, Parameter::make(volume))); + } ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP)); ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE)); @@ -907,6 +914,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectDataPathTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp index afddb841ee3105ac96d9b7a90a05b71c7e3f989e..2d9a233337964b0322d2fa8ac3ba02cd77a5c436 100644 --- a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp +++ b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp @@ -32,7 +32,7 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::Range; - +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in * VtsAudioEffectTargetTest. @@ -155,6 +155,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BassBoostParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp index 7a2f31bfe5e556698037c0e83239c46aad95b030..d7db567a0b07ccb5f41b89c7f9b7578591ff976b 100644 --- a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp +++ b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp @@ -18,88 +18,114 @@ #define LOG_TAG "VtsHalDownmixTargetTest" #include +#include #include "EffectHelper.h" using namespace android; +using aidl::android::hardware::audio::common::getChannelCount; using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::Downmix; using aidl::android::hardware::audio::effect::getEffectTypeUuidDownmix; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; - -/** - * Here we focus on specific parameter checking, general IEffect interfaces testing performed in - * VtsAudioEffectTargetTest. - */ -enum ParamName { PARAM_INSTANCE_NAME, PARAM_TYPE }; -using DownmixParamTestParam = - std::tuple, Descriptor>, Downmix::Type>; +using android::audio_utils::channels::ChannelMix; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; // Testing for enum values -const std::vector kTypeValues = {Downmix::Type::STRIP, Downmix::Type::FOLD}; +static const std::vector kTypeValues = {ndk::enum_range().begin(), + ndk::enum_range().end()}; -class DownmixParamTest : public ::testing::TestWithParam, - public EffectHelper { - public: - DownmixParamTest() : mParamType(std::get(GetParam())) { - std::tie(mFactory, mDescriptor) = std::get(GetParam()); - } +// Testing for supported layouts from AudioChannelLayout.h +static const std::vector kLayoutValues = { + AudioChannelLayout::LAYOUT_STEREO, AudioChannelLayout::LAYOUT_2POINT1, + AudioChannelLayout::LAYOUT_TRI, AudioChannelLayout::LAYOUT_TRI_BACK, + AudioChannelLayout::LAYOUT_3POINT1, AudioChannelLayout::LAYOUT_2POINT0POINT2, + AudioChannelLayout::LAYOUT_2POINT1POINT2, AudioChannelLayout::LAYOUT_3POINT0POINT2, + AudioChannelLayout::LAYOUT_3POINT1POINT2, AudioChannelLayout::LAYOUT_QUAD, + AudioChannelLayout::LAYOUT_QUAD_SIDE, AudioChannelLayout::LAYOUT_SURROUND, + AudioChannelLayout::LAYOUT_PENTA, AudioChannelLayout::LAYOUT_5POINT1, + AudioChannelLayout::LAYOUT_5POINT1_SIDE, AudioChannelLayout::LAYOUT_5POINT1POINT2, + AudioChannelLayout::LAYOUT_5POINT1POINT4, AudioChannelLayout::LAYOUT_6POINT1, + AudioChannelLayout::LAYOUT_7POINT1, AudioChannelLayout::LAYOUT_7POINT1POINT2, + AudioChannelLayout::LAYOUT_7POINT1POINT4, AudioChannelLayout::LAYOUT_9POINT1POINT4, + AudioChannelLayout::LAYOUT_9POINT1POINT6, AudioChannelLayout::LAYOUT_13POINT_360RA, + AudioChannelLayout::LAYOUT_22POINT2}; - void SetUp() override { +static const std::vector kChannels = { + AudioChannelLayout::CHANNEL_FRONT_LEFT, + AudioChannelLayout::CHANNEL_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_FRONT_CENTER, + AudioChannelLayout::CHANNEL_LOW_FREQUENCY, + AudioChannelLayout::CHANNEL_BACK_LEFT, + AudioChannelLayout::CHANNEL_BACK_RIGHT, + AudioChannelLayout::CHANNEL_BACK_CENTER, + AudioChannelLayout::CHANNEL_SIDE_LEFT, + AudioChannelLayout::CHANNEL_SIDE_RIGHT, + AudioChannelLayout::CHANNEL_FRONT_LEFT_OF_CENTER, + AudioChannelLayout::CHANNEL_FRONT_RIGHT_OF_CENTER, + AudioChannelLayout::CHANNEL_TOP_CENTER, + AudioChannelLayout::CHANNEL_TOP_FRONT_LEFT, + AudioChannelLayout::CHANNEL_TOP_FRONT_CENTER, + AudioChannelLayout::CHANNEL_TOP_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_TOP_BACK_LEFT, + AudioChannelLayout::CHANNEL_TOP_BACK_CENTER, + AudioChannelLayout::CHANNEL_TOP_BACK_RIGHT, + AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT, + AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_LEFT, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_CENTER, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2, + AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT, + AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT, +}; + +class DownmixEffectHelper : public EffectHelper { + public: + void SetUpDownmix(int32_t inputBufferLayout = AudioChannelLayout::LAYOUT_STEREO) { ASSERT_NE(nullptr, mFactory); ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor)); + AudioChannelLayout inputChannelLayout = + AudioChannelLayout::make(inputBufferLayout); + Parameter::Specific specific = getDefaultParamSpecific(); Parameter::Common common = EffectHelper::createParamCommon( 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */, - kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */); - IEffect::OpenEffectReturn ret; - ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE)); + kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */, + inputChannelLayout); + ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE)); ASSERT_NE(nullptr, mEffect); } - void TearDown() override { + void TearDownDownmix() { ASSERT_NO_FATAL_FAILURE(close(mEffect)); ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect)); + mOpenEffectReturn = IEffect::OpenEffectReturn{}; } - static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100; - std::shared_ptr mFactory; - std::shared_ptr mEffect; - Descriptor mDescriptor; - Downmix::Type mParamType = Downmix::Type::STRIP; - - void SetAndGetDownmixParameters() { - for (auto& it : mTags) { - auto& tag = it.first; - auto& dm = it.second; - - // set parameter - Parameter expectParam; - Parameter::Specific specific; - specific.set(dm); - expectParam.set(specific); - // All values are valid, set parameter should succeed - EXPECT_STATUS(EX_NONE, mEffect->setParameter(expectParam)) << expectParam.toString(); - - // get parameter - Parameter getParam; - Parameter::Id id; - Downmix::Id dmId; - dmId.set(tag); - id.set(dmId); - EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); - - EXPECT_EQ(expectParam, getParam); - } + Parameter createDownmixParam(Downmix::Type type) { + return Parameter::make( + Parameter::Specific::make( + Downmix::make(type))); + } + void setParameters(Downmix::Type type) { + // set parameter + auto param = createDownmixParam(type); + EXPECT_STATUS(EX_NONE, mEffect->setParameter(param)) << param.toString(); } - void addTypeParam(Downmix::Type type) { - Downmix dm; - dm.set(type); - mTags.push_back({Downmix::type, dm}); + void validateParameters(Downmix::Type type) { + auto leId = Downmix::Id::make(Downmix::Tag(Downmix::type)); + auto id = Parameter::Id::make(leId); + // get parameter + Parameter getParam; + EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); + auto expectedParam = createDownmixParam(type); + EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString() + << "\ngetParam:" << getParam.toString(); } Parameter::Specific getDefaultParamSpecific() { @@ -108,14 +134,281 @@ class DownmixParamTest : public ::testing::TestWithParam, return specific; } - private: - std::vector> mTags; - void CleanUp() { mTags.clear(); } + void setDataTestParams(int32_t layoutType) { + mInputBuffer.resize(kBufferSize); + mOutputBuffer.resize(kBufferSize); + + // Get the number of channels used + mInputChannelCount = getChannelCount( + AudioChannelLayout::make(layoutType)); + + // In case of downmix, output is always configured to stereo layout. + mOutputBufferSize = (mInputBuffer.size() / mInputChannelCount) * kOutputChannelCount; + } + + // Generate mInputBuffer values between -kMaxDownmixSample to kMaxDownmixSample + void generateInputBuffer(size_t position, bool isStrip) { + size_t increment; + if (isStrip) + // Fill input at all the channels + increment = 1; + else + // Fill input at only one channel + increment = mInputChannelCount; + + for (size_t i = position; i < mInputBuffer.size(); i += increment) { + mInputBuffer[i] = + ((static_cast(std::rand()) / RAND_MAX) * 2 - 1) * kMaxDownmixSample; + } + } + + bool isLayoutValid(int32_t inputLayout) { + if (inputLayout & kMaxChannelMask) { + return false; + } + return true; + } + + static constexpr long kInputFrameCount = 100, kOutputFrameCount = 100; + std::shared_ptr mFactory; + Descriptor mDescriptor; + std::shared_ptr mEffect; + IEffect::OpenEffectReturn mOpenEffectReturn; + + std::vector mInputBuffer; + std::vector mOutputBuffer; + size_t mInputChannelCount; + size_t mOutputBufferSize; + static constexpr size_t kBufferSize = 128; + static constexpr float kMaxDownmixSample = 1; + static constexpr int kOutputChannelCount = 2; + // Mask for layouts greater than MAX_INPUT_CHANNELS_SUPPORTED + static constexpr int32_t kMaxChannelMask = + ~((1 << ChannelMix::MAX_INPUT_CHANNELS_SUPPORTED) - 1); +}; + +/** + * Here we focus on specific parameter checking, general IEffect interfaces testing performed in + * VtsAudioEffectTargetTest. + */ +enum ParamName { PARAM_INSTANCE_NAME, PARAM_TYPE }; + +using DownmixParamTestParam = + std::tuple, Descriptor>, Downmix::Type>; + +class DownmixParamTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixParamTest() : mParamType(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { SetUpDownmix(); } + + void TearDown() override { TearDownDownmix(); } + + const Downmix::Type mParamType; }; TEST_P(DownmixParamTest, SetAndGetType) { - EXPECT_NO_FATAL_FAILURE(addTypeParam(mParamType)); - SetAndGetDownmixParameters(); + ASSERT_NO_FATAL_FAILURE(setParameters(mParamType)); + ASSERT_NO_FATAL_FAILURE(validateParameters(mParamType)); +} + +enum FoldParamName { FOLD_INSTANCE_NAME, FOLD_INPUT_LAYOUT, FOLD_TEST_CHANNEL }; + +using DownmixDataTestParamFold = + std::tuple, Descriptor>, int32_t>; + +class DownmixFoldDataTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixFoldDataTest() : mInputChannelLayout(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { + SetUpDownmix(mInputChannelLayout); + if (!isLayoutValid(mInputChannelLayout)) { + GTEST_SKIP() << "Layout not supported \n"; + } + setDataTestParams(mInputChannelLayout); + } + + void TearDown() override { TearDownDownmix(); } + + void checkAtLeft(int32_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate Left channel has audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i], 0); + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[j]); + } + // Validate Right channel has no audio + ASSERT_EQ(mOutputBuffer[i + 1], 0); + } + } + + void checkAtRight(int32_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate Left channel has no audio + ASSERT_EQ(mOutputBuffer[i], 0); + // Validate Right channel has audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i + 1], 0); + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i + 1], mInputBuffer[j]); + } + } + } + + void checkAtCenter(size_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate both channels have audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i], 0); + ASSERT_NE(mOutputBuffer[i + 1], 0); + + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[j]); + ASSERT_EQ(mOutputBuffer[i + 1], mInputBuffer[j]); + } + } + } + + void validateOutput(int32_t channel, size_t position) { + switch (channel) { + case AudioChannelLayout::CHANNEL_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_BACK_LEFT: + case AudioChannelLayout::CHANNEL_SIDE_LEFT: + case AudioChannelLayout::CHANNEL_TOP_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_TOP_BACK_LEFT: + case AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT: + case AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT: + checkAtLeft(position); + break; + + case AudioChannelLayout::CHANNEL_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_BACK_RIGHT: + case AudioChannelLayout::CHANNEL_SIDE_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_BACK_RIGHT: + case AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT: + case AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2: + checkAtRight(position); + break; + + case AudioChannelLayout::CHANNEL_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_BACK_CENTER: + case AudioChannelLayout::CHANNEL_TOP_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_FRONT_LEFT_OF_CENTER: + case AudioChannelLayout::CHANNEL_FRONT_RIGHT_OF_CENTER: + case AudioChannelLayout::CHANNEL_TOP_CENTER: + case AudioChannelLayout::CHANNEL_TOP_BACK_CENTER: + checkAtCenter(position); + break; + + case AudioChannelLayout::CHANNEL_LOW_FREQUENCY: + // If CHANNEL_LOW_FREQUENCY_2 is supported + if (mInputChannelLayout & AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2) { + // Validate that only Left channel has audio + checkAtLeft(position); + } else { + // Validate that both channels have audio + checkAtCenter(position); + } + break; + } + } + + std::set getInputChannelLayouts() { + std::set supportedChannels; + for (int32_t channel : kChannels) { + if ((mInputChannelLayout & channel) == channel) { + supportedChannels.insert(channel); + } + } + return supportedChannels; + } + + int32_t mInputChannelLayout; +}; + +TEST_P(DownmixFoldDataTest, DownmixProcessData) { + // Set FOLD type parameter + ASSERT_NO_FATAL_FAILURE(setParameters(Downmix::Type::FOLD)); + + // Get all the channels from input layout + std::set supportedChannels = getInputChannelLayouts(); + + for (int32_t channel : supportedChannels) { + size_t position = std::distance(supportedChannels.begin(), supportedChannels.find(channel)); + generateInputBuffer(position, false /*isStripe*/); + ASSERT_NO_FATAL_FAILURE( + processAndWriteToOutput(mInputBuffer, mOutputBuffer, mEffect, &mOpenEffectReturn)); + validateOutput(channel, position); + std::fill(mInputBuffer.begin(), mInputBuffer.end(), 0); + } +} + +enum StripParamName { STRIP_INSTANCE_NAME, STRIP_INPUT_LAYOUT }; + +using DownmixStripDataTestParam = + std::tuple, Descriptor>, int32_t>; + +class DownmixStripDataTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixStripDataTest() : mInputChannelLayout(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { + SetUpDownmix(mInputChannelLayout); + if (!isLayoutValid(mInputChannelLayout)) { + GTEST_SKIP() << "Layout not supported \n"; + } + setDataTestParams(mInputChannelLayout); + } + + void TearDown() override { TearDownDownmix(); } + + void validateOutput() { + ASSERT_EQ(kBufferSize, mInputBuffer.size()); + ASSERT_GE(kBufferSize, mOutputBufferSize); + for (size_t i = 0, j = 0; i < kBufferSize && j < mOutputBufferSize; + i += mInputChannelCount, j += kOutputChannelCount) { + ASSERT_EQ(mOutputBuffer[j], mInputBuffer[i]); + ASSERT_EQ(mOutputBuffer[j + 1], mInputBuffer[i + 1]); + } + for (size_t i = mOutputBufferSize; i < kBufferSize; i++) { + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[i]); + } + } + + int32_t mInputChannelLayout; +}; + +TEST_P(DownmixStripDataTest, DownmixProcessData) { + // Set STRIP type parameter + ASSERT_NO_FATAL_FAILURE(setParameters(Downmix::Type::STRIP)); + + // Generate input buffer, call process and compare outputs + generateInputBuffer(0 /*position*/, true /*isStripe*/); + ASSERT_NO_FATAL_FAILURE( + processAndWriteToOutput(mInputBuffer, mOutputBuffer, mEffect, &mOpenEffectReturn)); + validateOutput(); } INSTANTIATE_TEST_SUITE_P( @@ -134,8 +427,42 @@ INSTANTIATE_TEST_SUITE_P( GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixParamTest); +INSTANTIATE_TEST_SUITE_P( + DownmixTest, DownmixFoldDataTest, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, getEffectTypeUuidDownmix())), + testing::ValuesIn(kLayoutValues)), + [](const testing::TestParamInfo& info) { + auto descriptor = std::get(info.param).second; + std::string layout = std::to_string(std::get(info.param)); + std::string name = getPrefix(descriptor) + "_fold" + "_layout" + layout; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixFoldDataTest); + +INSTANTIATE_TEST_SUITE_P( + DownmixTest, DownmixStripDataTest, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, getEffectTypeUuidDownmix())), + testing::ValuesIn(kLayoutValues)), + [](const testing::TestParamInfo& info) { + auto descriptor = std::get(info.param).second; + std::string layout = + std::to_string(static_cast(std::get(info.param))); + std::string name = getPrefix(descriptor) + "_strip" + "_layout" + layout; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixStripDataTest); + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp index 5509c76eb96cd896f4c5155c299f2854fb00e437..2650f49cb2e577d408900d56850913b99ee685d5 100644 --- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp +++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp @@ -36,6 +36,7 @@ using aidl::android::hardware::audio::effect::getEffectTypeUuidDynamicsProcessin using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in @@ -996,6 +997,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestMbcBandConfi int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp index f2ef185950c100798be844377137c259f26794a6..474b3610ea90d33558de1b8bd9613676598001c4 100644 --- a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp +++ b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp @@ -28,6 +28,7 @@ using aidl::android::hardware::audio::effect::getEffectTypeUuidEnvReverb; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in @@ -536,6 +537,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EnvironmentalReverbBypassTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp index 37e7c0a0ff5cc3da7037b6068704f02c111a768d..09396d1c88d6a838d0c54f2b69e5d4958346e13f 100644 --- a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp +++ b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp @@ -46,6 +46,7 @@ using aidl::android::hardware::audio::effect::getEffectTypeUuidEqualizer; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific effect (equalizer) parameter checking, general IEffect interfaces @@ -220,6 +221,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EqualizerTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp index b33234b04ba9247618f2c158c2a4f4407e23fa7f..5a32398b282e3403e71a3972c956968f193eac1b 100644 --- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp +++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp @@ -33,6 +33,7 @@ using aidl::android::hardware::audio::effect::HapticGenerator; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in @@ -431,6 +432,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorScalesTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp index cbb80a9101c3589648757d427d188c698e243ccf..925f9ecc8603df35f8c6a3677491b56a3679f79b 100644 --- a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp +++ b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp @@ -30,28 +30,21 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::LoudnessEnhancer; using aidl::android::hardware::audio::effect::Parameter; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; -/** - * Here we focus on specific parameter checking, general IEffect interfaces testing performed in - * VtsAudioEffectTargetTest. - */ -enum ParamName { PARAM_INSTANCE_NAME, PARAM_GAIN_MB }; -using LoudnessEnhancerParamTestParam = - std::tuple, Descriptor>, int>; +static constexpr float kMaxAudioSample = 1; +static constexpr int kZeroGain = 0; +static constexpr int kMaxGain = std::numeric_limits::max(); +static constexpr int kMinGain = std::numeric_limits::min(); +static constexpr float kAbsError = 0.0001; // Every int 32 bit value is a valid gain, so testing the corner cases and one regular value. // TODO : Update the test values once range/capability is updated by implementation. -const std::vector kGainMbValues = {std::numeric_limits::min(), 100, - std::numeric_limits::max()}; +static const std::vector kGainMbValues = {kMinGain, -100, -50, kZeroGain, 50, 100, kMaxGain}; -class LoudnessEnhancerParamTest : public ::testing::TestWithParam, - public EffectHelper { +class LoudnessEnhancerEffectHelper : public EffectHelper { public: - LoudnessEnhancerParamTest() : mParamGainMb(std::get(GetParam())) { - std::tie(mFactory, mDescriptor) = std::get(GetParam()); - } - - void SetUp() override { + void SetUpLoudnessEnhancer() { ASSERT_NE(nullptr, mFactory); ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor)); @@ -59,13 +52,14 @@ class LoudnessEnhancerParamTest : public ::testing::TestWithParam(gainMb); + Parameter param; + Parameter::Specific specific; + specific.set(le); + param.set(specific); + return param; + } + + binder_exception_t isGainValid(int gainMb) { + LoudnessEnhancer le; + le.set(gainMb); + if (isParameterValid(le, mDescriptor)) { + return EX_NONE; + } else { + return EX_ILLEGAL_ARGUMENT; + } + } + + void setParameters(int gain, binder_exception_t expected) { + // set parameter + auto param = createLoudnessParam(gain); + EXPECT_STATUS(expected, mEffect->setParameter(param)) << param.toString(); + } + + void validateParameters(int gain) { + // get parameter + LoudnessEnhancer::Id leId; + Parameter getParam; + Parameter::Id id; + + LoudnessEnhancer::Tag tag(LoudnessEnhancer::gainMb); + leId.set(tag); + id.set(leId); + EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); + auto expectedParam = createLoudnessParam(gain); + EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString() + << "\ngetParam:" << getParam.toString(); + } + static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100; + IEffect::OpenEffectReturn mOpenEffectReturn; std::shared_ptr mFactory; std::shared_ptr mEffect; Descriptor mDescriptor; +}; + +/** + * Here we focus on specific parameter checking, general IEffect interfaces testing performed in + * VtsAudioEffectTargetTest. + */ +enum ParamName { PARAM_INSTANCE_NAME, PARAM_GAIN_MB }; +using LoudnessEnhancerParamTestParam = + std::tuple, Descriptor>, int>; + +class LoudnessEnhancerParamTest : public ::testing::TestWithParam, + public LoudnessEnhancerEffectHelper { + public: + LoudnessEnhancerParamTest() : mParamGainMb(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { SetUpLoudnessEnhancer(); } + void TearDown() override { TearDownLoudnessEnhancer(); } int mParamGainMb = 0; +}; + +TEST_P(LoudnessEnhancerParamTest, SetAndGetGainMb) { + binder_exception_t expected = isGainValid(mParamGainMb); + setParameters(mParamGainMb, expected); + if (expected == EX_NONE) { + validateParameters(mParamGainMb); + } +} + +using LoudnessEnhancerDataTestParam = std::pair, Descriptor>; - void SetAndGetParameters() { - for (auto& it : mTags) { - auto& tag = it.first; - auto& le = it.second; - - // set parameter - Parameter expectParam; - Parameter::Specific specific; - specific.set(le); - expectParam.set(specific); - // All values are valid, set parameter should succeed - EXPECT_STATUS(EX_NONE, mEffect->setParameter(expectParam)) << expectParam.toString(); - - // get parameter - Parameter getParam; - Parameter::Id id; - LoudnessEnhancer::Id leId; - leId.set(tag); - id.set(leId); - EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); - - EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString() - << "\ngetParam:" << getParam.toString(); +class LoudnessEnhancerDataTest : public ::testing::TestWithParam, + public LoudnessEnhancerEffectHelper { + public: + LoudnessEnhancerDataTest() { + std::tie(mFactory, mDescriptor) = GetParam(); + generateInputBuffer(); + mOutputBuffer.resize(kBufferSize); + } + + void SetUp() override { + SetUpLoudnessEnhancer(); + + // Creating AidlMessageQueues + mStatusMQ = std::make_unique(mOpenEffectReturn.statusMQ); + mInputMQ = std::make_unique(mOpenEffectReturn.inputDataMQ); + mOutputMQ = std::make_unique(mOpenEffectReturn.outputDataMQ); + } + + void TearDown() override { TearDownLoudnessEnhancer(); } + + // Fill inputBuffer with random values between -kMaxAudioSample to kMaxAudioSample + void generateInputBuffer() { + for (size_t i = 0; i < kBufferSize; i++) { + mInputBuffer.push_back(((static_cast(std::rand()) / RAND_MAX) * 2 - 1) * + kMaxAudioSample); } } - void addGainMbParam(int gainMb) { - LoudnessEnhancer le; - le.set(gainMb); - mTags.push_back({LoudnessEnhancer::gainMb, le}); + // Add gains to the mInputBuffer and store processed output to mOutputBuffer + void processAndWriteToOutput() { + // Check AidlMessageQueues are not null + ASSERT_TRUE(mStatusMQ->isValid()); + ASSERT_TRUE(mInputMQ->isValid()); + ASSERT_TRUE(mOutputMQ->isValid()); + + // Enabling the process + ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START)); + ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING)); + + // Write from buffer to message queues and calling process + EXPECT_NO_FATAL_FAILURE(EffectHelper::writeToFmq(mStatusMQ, mInputMQ, mInputBuffer)); + + // Read the updated message queues into buffer + EXPECT_NO_FATAL_FAILURE(EffectHelper::readFromFmq(mStatusMQ, 1, mOutputMQ, + mOutputBuffer.size(), mOutputBuffer)); + + // Disable the process + ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP)); + } + + void assertGreaterGain(const std::vector& first, const std::vector& second) { + for (size_t i = 0; i < first.size(); i++) { + if (first[i] != 0) { + ASSERT_GT(abs(first[i]), abs(second[i])); + + } else { + ASSERT_EQ(first[i], second[i]); + } + } + } + + void assertSequentialGains(const std::vector& gainValues, bool isIncreasing) { + std::vector baseOutput(kBufferSize); + + // Process a reference output buffer with 0 gain which gives compressed input values + binder_exception_t expected; + expected = isGainValid(kZeroGain); + ASSERT_EQ(expected, EX_NONE); + setParameters(kZeroGain, expected); + ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput()); + baseOutput = mOutputBuffer; + + // Compare the outputs for increasing gain + for (int gain : gainValues) { + // Setting the parameters + binder_exception_t expected = isGainValid(gain); + if (expected != EX_NONE) { + GTEST_SKIP() << "Gains not supported."; + } + setParameters(gain, expected); + ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput()); + + // Compare the mOutputBuffer values with baseOutput and update it + if (isIncreasing) { + ASSERT_NO_FATAL_FAILURE(assertGreaterGain(mOutputBuffer, baseOutput)); + } else { + ASSERT_NO_FATAL_FAILURE(assertGreaterGain(baseOutput, mOutputBuffer)); + } + + baseOutput = mOutputBuffer; + } } - private: - std::vector> mTags; - void CleanUp() { mTags.clear(); } + std::unique_ptr mStatusMQ; + std::unique_ptr mInputMQ; + std::unique_ptr mOutputMQ; + + std::vector mInputBuffer; + std::vector mOutputBuffer; + static constexpr float kBufferSize = 128; }; -TEST_P(LoudnessEnhancerParamTest, SetAndGetGainMb) { - EXPECT_NO_FATAL_FAILURE(addGainMbParam(mParamGainMb)); - SetAndGetParameters(); +TEST_P(LoudnessEnhancerDataTest, IncreasingGains) { + static const std::vector kIncreasingGains = {50, 100}; + + assertSequentialGains(kIncreasingGains, true /*isIncreasing*/); +} + +TEST_P(LoudnessEnhancerDataTest, DecreasingGains) { + static const std::vector kDecreasingGains = {-50, -100}; + + assertSequentialGains(kDecreasingGains, false /*isIncreasing*/); +} + +TEST_P(LoudnessEnhancerDataTest, MinimumGain) { + // Setting the parameters + binder_exception_t expected = isGainValid(kMinGain); + if (expected != EX_NONE) { + GTEST_SKIP() << "Minimum integer value not supported"; + } + setParameters(kMinGain, expected); + ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput()); + + // Validate that mOutputBuffer has 0 values for INT_MIN gain + for (size_t i = 0; i < mOutputBuffer.size(); i++) { + ASSERT_FLOAT_EQ(mOutputBuffer[i], 0); + } +} + +TEST_P(LoudnessEnhancerDataTest, MaximumGain) { + // Setting the parameters + binder_exception_t expected = isGainValid(kMaxGain); + if (expected != EX_NONE) { + GTEST_SKIP() << "Maximum integer value not supported"; + } + setParameters(kMaxGain, expected); + ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput()); + + // Validate that mOutputBuffer reaches to kMaxAudioSample for INT_MAX gain + for (size_t i = 0; i < mOutputBuffer.size(); i++) { + if (mInputBuffer[i] != 0) { + EXPECT_NEAR(kMaxAudioSample, abs(mOutputBuffer[i]), kAbsError); + } else { + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[i]); + } + } } INSTANTIATE_TEST_SUITE_P( @@ -139,8 +311,23 @@ INSTANTIATE_TEST_SUITE_P( GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LoudnessEnhancerParamTest); +INSTANTIATE_TEST_SUITE_P( + LoudnessEnhancerTest, LoudnessEnhancerDataTest, + testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, getEffectTypeUuidLoudnessEnhancer())), + [](const testing::TestParamInfo& info) { + auto descriptor = info.param; + std::string name = getPrefix(descriptor.second); + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LoudnessEnhancerDataTest); + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalNSTargetTest.cpp b/audio/aidl/vts/VtsHalNSTargetTest.cpp index 624d5d29919fbda32c6130440af2b08efb3121e9..12d56b01513b4632531abd2d7e61d96e7eab652d 100644 --- a/audio/aidl/vts/VtsHalNSTargetTest.cpp +++ b/audio/aidl/vts/VtsHalNSTargetTest.cpp @@ -32,6 +32,7 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::NoiseSuppression; using aidl::android::hardware::audio::effect::Parameter; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; enum ParamName { PARAM_INSTANCE_NAME, PARAM_LEVEL, PARAM_TYPE }; using NSParamTestParam = std::tuple, Descriptor>, @@ -171,6 +172,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NSParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp index 3056c6c1ccc0ee21462344c718410e1b2a77b5b4..57eda0992caeff691bfd66e216c52235ee0f0a9f 100644 --- a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp +++ b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp @@ -29,6 +29,7 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::PresetReverb; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in @@ -147,6 +148,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PresetReverbParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp index 07a9fa4e70fdec5af00101247b551ee0f2b86ae1..3e39d3a67da4aeb2f1f41cee9e04655d791523c6 100644 --- a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp +++ b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp @@ -28,6 +28,7 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::Virtualizer; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in @@ -151,6 +152,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VirtualizerParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp index 903ba696be6cb4acf3f6b00af0b9391cf1db0195..1b8352b8cab5cc36248049436bb419b247fcf4dd 100644 --- a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp +++ b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp @@ -31,6 +31,7 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::Visualizer; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in @@ -207,6 +208,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VisualizerParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp index 0b5b9fc3dc9f0ebb2d823fd728296a134fbcff2e..257100b9cff6f468b65d73c84edcd811ad443ae9 100644 --- a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp +++ b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp @@ -28,6 +28,7 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::Volume; +using android::hardware::audio::common::testing::detail::TestExecutionTracer; /** * Here we focus on specific parameter checking, general IEffect interfaces testing performed in @@ -159,6 +160,7 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VolumeParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/audio/common/all-versions/default/service/android.hardware.audio.service.rc b/audio/common/all-versions/default/service/android.hardware.audio.service.rc index 0de4eea37337c43985351d0bd875ab12c46ca1af..a1df67a2864e9a81cd6c9f9f6b672d443214129f 100644 --- a/audio/common/all-versions/default/service/android.hardware.audio.service.rc +++ b/audio/common/all-versions/default/service/android.hardware.audio.service.rc @@ -2,7 +2,7 @@ service vendor.audio-hal /vendor/bin/hw/android.hardware.audio.service class hal user audioserver # media gid needed for /dev/fm (radio) and for /data/misc/media (tee) - group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub + group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub system capabilities BLOCK_SUSPEND SYS_NICE # setting RLIMIT_RTPRIO allows binder RT priority inheritance rlimit rtprio 10 10 diff --git a/audio/common/all-versions/test/utility/Android.bp b/audio/common/all-versions/test/utility/Android.bp index 757f8a853d32f560b643bc99e525d2b65f31ca38..c6a3963cb088fd4b5744af420f5f4ca237943dec 100644 --- a/audio/common/all-versions/test/utility/Android.bp +++ b/audio/common/all-versions/test/utility/Android.bp @@ -62,6 +62,5 @@ cc_test { "libxml2", "liblog", ], - static_libs: ["libgtest"], test_suites: ["general-tests"], } diff --git a/audio/common/all-versions/test/utility/src/ValidateXml.cpp b/audio/common/all-versions/test/utility/src/ValidateXml.cpp index 4d6f003bc5b1aad2b666654753e0abbd1354c47b..b7e685b6a3a72761d649e4f7dfdec98accc94161 100644 --- a/audio/common/all-versions/test/utility/src/ValidateXml.cpp +++ b/audio/common/all-versions/test/utility/src/ValidateXml.cpp @@ -19,6 +19,7 @@ #include +#include #define LIBXML_SCHEMAS_ENABLED #include #define LIBXML_XINCLUDE_ENABLED diff --git a/audio/common/all-versions/test/utility/tests/utility_tests.cpp b/audio/common/all-versions/test/utility/tests/utility_tests.cpp index c52306638f6e76d5e5db18dc916709d5686f2c1c..c2bcfbca8c6b597bfac54167353aa30764631fb1 100644 --- a/audio/common/all-versions/test/utility/tests/utility_tests.cpp +++ b/audio/common/all-versions/test/utility/tests/utility_tests.cpp @@ -70,6 +70,7 @@ namespace { std::string substitute(const char* fmt, const char* param) { std::string buffer(static_cast(strlen(fmt) + strlen(param)), '\0'); snprintf(buffer.data(), buffer.size(), fmt, param); + buffer.resize(strlen(buffer.c_str())); return buffer; } diff --git a/audio/core/all-versions/default/ParametersUtil.cpp b/audio/core/all-versions/default/ParametersUtil.cpp index e21eff28451f71ecd20d3934141b1bfe238f46b2..4d8a2085b61a2a45454932f6e2efcc5cf0f1a9d8 100644 --- a/audio/core/all-versions/default/ParametersUtil.cpp +++ b/audio/core/all-versions/default/ParametersUtil.cpp @@ -51,7 +51,7 @@ Result ParametersUtil::getParam(const char* name, bool* value) { Result retval = getParam(name, &halValue); *value = false; if (retval == Result::OK) { - if (halValue.empty()) { + if (halValue.length() == 0) { return Result::NOT_SUPPORTED; } *value = !(halValue == AudioParameter::valueOff); @@ -97,17 +97,17 @@ void ParametersUtil::getParametersImpl( retval = getHalStatusToResult(status); break; } - result[i].key = halKey.string(); - result[i].value = halValue.string(); + result[i].key = halKey.c_str(); + result[i].value = halValue.c_str(); } cb(retval, result); } std::unique_ptr ParametersUtil::getParams(const AudioParameter& keys) { String8 paramsAndValues; - char* halValues = halGetParameters(keys.keysToString().string()); + char* halValues = halGetParameters(keys.keysToString().c_str()); if (halValues != NULL) { - paramsAndValues.setTo(halValues); + paramsAndValues = halValues; free(halValues); } else { paramsAndValues.clear(); @@ -163,7 +163,7 @@ Result ParametersUtil::setParam(const char* name, const DeviceAddress& address) } Result ParametersUtil::setParams(const AudioParameter& param) { - int halStatus = halSetParameters(param.toString().string()); + int halStatus = halSetParameters(param.toString().c_str()); return util::analyzeStatus(halStatus); } diff --git a/audio/core/all-versions/default/PrimaryDevice.cpp b/audio/core/all-versions/default/PrimaryDevice.cpp index cf162f146387d12997bc4432378d52ab47868935..13efbbc757710d6678ddb3e26a15513a8397e970 100644 --- a/audio/core/all-versions/default/PrimaryDevice.cpp +++ b/audio/core/all-versions/default/PrimaryDevice.cpp @@ -283,7 +283,7 @@ Return PrimaryDevice::getTtyMode(getTtyMode_cb _hidl_cb) { _hidl_cb(retval, TtyMode::OFF); return Void(); } - TtyMode mode = convertTtyModeToHIDL(halMode); + TtyMode mode = convertTtyModeToHIDL(halMode.c_str()); if (mode == TtyMode(-1)) { ALOGE("HAL returned invalid TTY value: %s", halMode.c_str()); _hidl_cb(Result::INVALID_STATE, TtyMode::OFF); diff --git a/audio/core/all-versions/default/Stream.cpp b/audio/core/all-versions/default/Stream.cpp index 8e85a8b255d4c3faa49ba705676400a584e5ef4b..c11b675a7f0ac66534caa89008898d171279e9c5 100644 --- a/audio/core/all-versions/default/Stream.cpp +++ b/audio/core/all-versions/default/Stream.cpp @@ -114,7 +114,7 @@ Return Stream::getSupportedSampleRates(AudioFormat format, SampleRateSet halSampleRates; if (result == Result::OK) { halSampleRates = - samplingRatesFromString(halListValue.string(), AudioParameter::valueListSeparator); + samplingRatesFromString(halListValue.c_str(), AudioParameter::valueListSeparator); sampleRates = hidl_vec(halSampleRates.begin(), halSampleRates.end()); // Legacy get_parameter does not return a status_t, thus can not advertise of failure. // Note that this method must succeed (non empty list) if the format is supported. @@ -140,7 +140,7 @@ Return Stream::getSupportedChannelMasks(AudioFormat format, ChannelMaskSet halChannelMasks; if (result == Result::OK) { halChannelMasks = - channelMasksFromString(halListValue.string(), AudioParameter::valueListSeparator); + channelMasksFromString(halListValue.c_str(), AudioParameter::valueListSeparator); channelMasks.resize(halChannelMasks.size()); size_t i = 0; for (auto channelMask : halChannelMasks) { @@ -182,7 +182,7 @@ Return Stream::getSupportedFormats(getSupportedFormats_cb _hidl_cb) { hidl_vec formats; FormatVector halFormats; if (result == Result::OK) { - halFormats = formatsFromString(halListValue.string(), AudioParameter::valueListSeparator); + halFormats = formatsFromString(halListValue.c_str(), AudioParameter::valueListSeparator); formats.resize(halFormats.size()); for (size_t i = 0; i < halFormats.size(); ++i) { formats[i] = AudioFormat(halFormats[i]); @@ -226,7 +226,7 @@ Return Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) { // Ensure that the separator is one character, despite that it's defined as a C string. static_assert(sizeof(AUDIO_PARAMETER_VALUE_LIST_SEPARATOR) == 2); std::vector halFormats = - splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]); + splitString(halListValue.c_str(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]); hidl_vec formats; (void)HidlUtils::audioFormatsFromHal(halFormats, &formats); std::vector tempProfiles; @@ -241,7 +241,7 @@ Return Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) { result = getParam(AudioParameter::keyStreamSupportedSamplingRates, &halListValue, context); if (result != Result::OK) break; std::vector halSampleRates = - splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]); + splitString(halListValue.c_str(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]); hidl_vec sampleRates; sampleRates.resize(halSampleRates.size()); for (size_t i = 0; i < sampleRates.size(); ++i) { @@ -251,7 +251,7 @@ Return Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) { result = getParam(AudioParameter::keyStreamSupportedChannels, &halListValue, context); if (result != Result::OK) break; std::vector halChannelMasks = - splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]); + splitString(halListValue.c_str(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]); hidl_vec channelMasks; (void)HidlUtils::audioChannelMasksFromHal(halChannelMasks, &channelMasks); // Create a profile. diff --git a/audio/effect/4.0/xml/Android.bp b/audio/effect/4.0/xml/Android.bp index 8c03a35fbb0779a79bad4b719fb0fa0a15c7eef2..bdffe6065b01937cd3e19a4858890801640c9c90 100644 --- a/audio/effect/4.0/xml/Android.bp +++ b/audio/effect/4.0/xml/Android.bp @@ -9,9 +9,9 @@ package { genrule { name: "audio_effects_conf_V4_0", - srcs: ["audio_effects_conf.xsd"], + srcs: [":audio_effects_conf_V2_0"], out: [ "audio_effects_conf_V4_0.xsd", ], - cmd: "cp -f $(in) $(genDir)/audio_effects_conf_V4_0.xsd", + cmd: "cp -f $(in) $(out)", } diff --git a/audio/effect/4.0/xml/audio_effects_conf.xsd b/audio/effect/4.0/xml/audio_effects_conf.xsd deleted file mode 120000 index 9d85fa7c687a6a10f28473c96482781aac641c56..0000000000000000000000000000000000000000 --- a/audio/effect/4.0/xml/audio_effects_conf.xsd +++ /dev/null @@ -1 +0,0 @@ -../../2.0/xml/audio_effects_conf.xsd \ No newline at end of file diff --git a/audio/effect/5.0/xml/Android.bp b/audio/effect/5.0/xml/Android.bp index 7982e2a1a9c9b4a56443fa584bc28188f4ebaadf..ed12e38b5cbcd1e66c3873c30223992b4e276ff2 100644 --- a/audio/effect/5.0/xml/Android.bp +++ b/audio/effect/5.0/xml/Android.bp @@ -9,6 +9,6 @@ package { xsd_config { name: "audio_effects_conf_V5_0", - srcs: ["audio_effects_conf.xsd"], + srcs: [":audio_effects_conf_V2_0"], package_name: "audio.effects.V5_0", } diff --git a/audio/effect/5.0/xml/audio_effects_conf.xsd b/audio/effect/5.0/xml/audio_effects_conf.xsd deleted file mode 120000 index 9d85fa7c687a6a10f28473c96482781aac641c56..0000000000000000000000000000000000000000 --- a/audio/effect/5.0/xml/audio_effects_conf.xsd +++ /dev/null @@ -1 +0,0 @@ -../../2.0/xml/audio_effects_conf.xsd \ No newline at end of file diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp index 5aecd324ebe10b8ef48ddf9ca2a0d0315b85272d..4a9e1446272c96d08841ecfd7d34507b4168438a 100644 --- a/audio/effect/all-versions/default/Effect.cpp +++ b/audio/effect/all-versions/default/Effect.cpp @@ -315,7 +315,7 @@ Effect::Effect(bool isInput, effect_handle_t handle) Effect::~Effect() { ATRACE_CALL(); - (void)close(); + auto [_, handle] = closeImpl(); if (mProcessThread.get()) { ATRACE_NAME("mProcessThread->join"); status_t status = mProcessThread->join(); @@ -328,11 +328,10 @@ Effect::~Effect() { mInBuffer.clear(); mOutBuffer.clear(); #if MAJOR_VERSION <= 5 - int status = EffectRelease(mHandle); - ALOGW_IF(status, "Error releasing effect %p: %s", mHandle, strerror(-status)); + int status = EffectRelease(handle); + ALOGW_IF(status, "Error releasing effect %p: %s", handle, strerror(-status)); #endif - EffectMap::getInstance().remove(mHandle); - mHandle = 0; + EffectMap::getInstance().remove(handle); } // static @@ -459,7 +458,19 @@ Result Effect::analyzeStatus(const char* funcName, const char* subFuncName, } } -void Effect::getConfigImpl(int commandCode, const char* commandName, GetConfigCallback cb) { +#define RETURN_IF_EFFECT_CLOSED() \ + if (mHandle == kInvalidEffectHandle) { \ + return Result::INVALID_STATE; \ + } +#define RETURN_RESULT_IF_EFFECT_CLOSED(result) \ + if (mHandle == kInvalidEffectHandle) { \ + _hidl_cb(Result::INVALID_STATE, result); \ + return Void(); \ + } + +Return Effect::getConfigImpl(int commandCode, const char* commandName, + GetConfigCallback _hidl_cb) { + RETURN_RESULT_IF_EFFECT_CLOSED(EffectConfig()); uint32_t halResultSize = sizeof(effect_config_t); effect_config_t halConfig{}; status_t status = @@ -468,7 +479,8 @@ void Effect::getConfigImpl(int commandCode, const char* commandName, GetConfigCa if (status == OK) { status = EffectUtils::effectConfigFromHal(halConfig, mIsInput, &config); } - cb(analyzeCommandStatus(commandName, sContextCallToCommand, status), config); + _hidl_cb(analyzeCommandStatus(commandName, sContextCallToCommand, status), config); + return Void(); } Result Effect::getCurrentConfigImpl(uint32_t featureId, uint32_t configSize, @@ -530,6 +542,7 @@ Result Effect::getSupportedConfigsImpl(uint32_t featureId, uint32_t maxConfigs, } Return Effect::prepareForProcessing(prepareForProcessing_cb _hidl_cb) { + RETURN_RESULT_IF_EFFECT_CLOSED(StatusMQ::Descriptor()); status_t status; // Create message queue. if (mStatusMQ) { @@ -576,6 +589,7 @@ Return Effect::prepareForProcessing(prepareForProcessing_cb _hidl_cb) { Return Effect::setProcessBuffers(const AudioBuffer& inBuffer, const AudioBuffer& outBuffer) { + RETURN_IF_EFFECT_CLOSED(); AudioBufferManager& manager = AudioBufferManager::getInstance(); sp tempInBuffer, tempOutBuffer; if (!manager.wrap(inBuffer, &tempInBuffer)) { @@ -600,6 +614,7 @@ Result Effect::sendCommand(int commandCode, const char* commandName) { } Result Effect::sendCommand(int commandCode, const char* commandName, uint32_t size, void* data) { + RETURN_IF_EFFECT_CLOSED(); status_t status = (*mHandle)->command(mHandle, commandCode, size, data, 0, NULL); return analyzeCommandStatus(commandName, sContextCallToCommand, status); } @@ -611,6 +626,7 @@ Result Effect::sendCommandReturningData(int commandCode, const char* commandName Result Effect::sendCommandReturningData(int commandCode, const char* commandName, uint32_t size, void* data, uint32_t* replySize, void* replyData) { + RETURN_IF_EFFECT_CLOSED(); uint32_t expectedReplySize = *replySize; status_t status = (*mHandle)->command(mHandle, commandCode, size, data, replySize, replyData); if (status == OK && *replySize != expectedReplySize) { @@ -635,6 +651,7 @@ Result Effect::sendCommandReturningStatusAndData(int commandCode, const char* co uint32_t size, void* data, uint32_t* replySize, void* replyData, uint32_t minReplySize, CommandSuccessCallback onSuccess) { + RETURN_IF_EFFECT_CLOSED(); status_t status = (*mHandle)->command(mHandle, commandCode, size, data, replySize, replyData); Result retval; if (status == OK && minReplySize >= sizeof(uint32_t) && *replySize >= minReplySize) { @@ -792,13 +809,11 @@ Return Effect::setConfigReverse( } Return Effect::getConfig(getConfig_cb _hidl_cb) { - getConfigImpl(EFFECT_CMD_GET_CONFIG, "GET_CONFIG", _hidl_cb); - return Void(); + return getConfigImpl(EFFECT_CMD_GET_CONFIG, "GET_CONFIG", _hidl_cb); } Return Effect::getConfigReverse(getConfigReverse_cb _hidl_cb) { - getConfigImpl(EFFECT_CMD_GET_CONFIG_REVERSE, "GET_CONFIG_REVERSE", _hidl_cb); - return Void(); + return getConfigImpl(EFFECT_CMD_GET_CONFIG_REVERSE, "GET_CONFIG_REVERSE", _hidl_cb); } Return Effect::getSupportedAuxChannelsConfigs(uint32_t maxConfigs, @@ -845,6 +860,7 @@ Return Effect::offload(const EffectOffloadParameter& param) { } Return Effect::getDescriptor(getDescriptor_cb _hidl_cb) { + RETURN_RESULT_IF_EFFECT_CLOSED(EffectDescriptor()); effect_descriptor_t halDescriptor; memset(&halDescriptor, 0, sizeof(effect_descriptor_t)); status_t status = (*mHandle)->get_descriptor(mHandle, &halDescriptor); @@ -858,6 +874,10 @@ Return Effect::getDescriptor(getDescriptor_cb _hidl_cb) { Return Effect::command(uint32_t commandId, const hidl_vec& data, uint32_t resultMaxSize, command_cb _hidl_cb) { + if (mHandle == kInvalidEffectHandle) { + _hidl_cb(-ENODATA, hidl_vec()); + return Void(); + } uint32_t halDataSize; std::unique_ptr halData = hidlVecToHal(data, &halDataSize); uint32_t halResultSize = resultMaxSize; @@ -942,26 +962,33 @@ Return Effect::setCurrentConfigForFeature(uint32_t featureId, halCmd.size(), &halCmd[0]); } -Return Effect::close() { +std::tuple Effect::closeImpl() { if (mStopProcessThread.load(std::memory_order_relaxed)) { // only this thread modifies - return Result::INVALID_STATE; + return {Result::INVALID_STATE, kInvalidEffectHandle}; } mStopProcessThread.store(true, std::memory_order_release); if (mEfGroup) { mEfGroup->wake(static_cast(MessageQueueFlagBits::REQUEST_QUIT)); } + effect_handle_t handle = mHandle; + mHandle = kInvalidEffectHandle; #if MAJOR_VERSION <= 5 - return Result::OK; + return {Result::OK, handle}; #elif MAJOR_VERSION >= 6 // No need to join the processing thread, it is part of the API contract that the client // must finish processing before closing the effect. - Result retval = - analyzeStatus("EffectRelease", "", sContextCallFunction, EffectRelease(mHandle)); - EffectMap::getInstance().remove(mHandle); - return retval; + Result retval = analyzeStatus("EffectRelease", "", sContextCallFunction, EffectRelease(handle)); + EffectMap::getInstance().remove(handle); + return {retval, handle}; #endif } +Return Effect::close() { + RETURN_IF_EFFECT_CLOSED(); + auto [result, _] = closeImpl(); + return result; +} + Return Effect::debug(const hidl_handle& fd, const hidl_vec& /* options */) { if (fd.getNativeHandle() != nullptr && fd->numFds == 1) { uint32_t cmdData = fd->data[0]; diff --git a/audio/effect/all-versions/default/Effect.h b/audio/effect/all-versions/default/Effect.h index 5d8dcccba6b2f30761bd1dac49e8a2b2f2f994b4..2bcecec4c70c7ea870686debb1981f90076b7802 100644 --- a/audio/effect/all-versions/default/Effect.h +++ b/audio/effect/all-versions/default/Effect.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -186,6 +187,7 @@ struct Effect : public IEffect { // Sets the limit on the maximum size of vendor-provided data structures. static constexpr size_t kMaxDataSize = 1 << 20; + static constexpr effect_handle_t kInvalidEffectHandle = nullptr; static const char* sContextResultOfCommand; static const char* sContextCallToCommand; @@ -208,6 +210,7 @@ struct Effect : public IEffect { static size_t alignedSizeIn(size_t s); template std::unique_ptr hidlVecToHal(const hidl_vec& vec, uint32_t* halDataSize); + std::tuple closeImpl(); void effectAuxChannelsConfigFromHal(const channel_config_t& halConfig, EffectAuxChannelsConfig* config); static void effectAuxChannelsConfigToHal(const EffectAuxChannelsConfig& config, @@ -218,7 +221,8 @@ struct Effect : public IEffect { const void** valueData, std::vector* halParamBuffer); Result analyzeCommandStatus(const char* commandName, const char* context, status_t status); - void getConfigImpl(int commandCode, const char* commandName, GetConfigCallback cb); + Return getConfigImpl(int commandCode, const char* commandName, + GetConfigCallback _hidl_cb); Result getCurrentConfigImpl(uint32_t featureId, uint32_t configSize, GetCurrentConfigSuccessCallback onSuccess); Result getSupportedConfigsImpl(uint32_t featureId, uint32_t maxConfigs, uint32_t configSize, diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp index d95bb06c3ad66e7a4369211cc05e6e05a5192d0b..ff84f9d5623c9f3c4c8005a22262e5f51dd1991f 100644 --- a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp +++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp @@ -169,13 +169,15 @@ static const Uuid LOUDNESS_ENHANCER_EFFECT_TYPE = { 0xfe3199be, 0xaed0, 0x413f, 0x87bb, std::array{{0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}}; -enum { PARAM_FACTORY_NAME, PARAM_EFFECT_UUID }; -using EffectParameter = std::tuple; +enum { PARAM_FACTORY_NAME, PARAM_EFFECT_UUID, PARAM_USE_AFTER_CLOSE }; +using EffectParameter = std::tuple; static inline std::string EffectParameterToString( const ::testing::TestParamInfo& info) { - return ::android::hardware::PrintInstanceNameToString(::testing::TestParamInfo{ - std::get(info.param), info.index}); + std::string prefix = std::get(info.param) ? "UseAfterClose_" : ""; + return prefix.append( + ::android::hardware::PrintInstanceNameToString(::testing::TestParamInfo{ + std::get(info.param), info.index})); } // The main test class for Audio Effect HIDL HAL. @@ -191,6 +193,13 @@ class AudioEffectHidlTest : public ::testing::TestWithParam { Return ret = effect->init(); ASSERT_TRUE(ret.isOk()); ASSERT_EQ(Result::OK, ret); + + useAfterClose = std::get(GetParam()); + if (useAfterClose) { + Return ret = effect->close(); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, ret); + } } void TearDown() override { @@ -205,14 +214,34 @@ class AudioEffectHidlTest : public ::testing::TestWithParam { Uuid getEffectType() const { return std::get(GetParam()); } + void checkResult(const Result& result); + void checkResultForUseAfterClose(const Result& result); void findAndCreateEffect(const Uuid& type); void findEffectInstance(const Uuid& type, Uuid* uuid); void getChannelCount(uint32_t* channelCount); sp effectsFactory; sp effect; + bool useAfterClose; }; +void AudioEffectHidlTest::checkResult(const Result& result) { + if (!useAfterClose) { + ASSERT_EQ(Result::OK, result); + } else { + ASSERT_NO_FATAL_FAILURE(checkResultForUseAfterClose(result)); + } +} + +void AudioEffectHidlTest::checkResultForUseAfterClose(const Result& result) { + if (useAfterClose) { + // The actual error does not matter. It's important that the effect did not crash + // while executing any command after a call to "close", and that the returned status + // is not OK. + ASSERT_NE(Result::OK, result); + } +} + void AudioEffectHidlTest::findAndCreateEffect(const Uuid& type) { Uuid effectUuid; ASSERT_NO_FATAL_FAILURE(findEffectInstance(type, &effectUuid)); @@ -257,7 +286,11 @@ void AudioEffectHidlTest::getChannelCount(uint32_t* channelCount) { } }); ASSERT_TRUE(ret.isOk()); - ASSERT_EQ(Result::OK, retval); + ASSERT_NO_FATAL_FAILURE(checkResult(retval)); + if (useAfterClose) { + *channelCount = 1; + return; + } #if MAJOR_VERSION <= 6 ASSERT_TRUE(audio_channel_mask_is_valid( static_cast(currentConfig.outputCfg.channels))); @@ -276,7 +309,7 @@ TEST_P(AudioEffectHidlTest, Close) { description("Verify that an effect can be closed"); Return ret = effect->close(); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + ASSERT_NO_FATAL_FAILURE(checkResult(ret)); } TEST_P(AudioEffectHidlTest, GetDescriptor) { @@ -290,7 +323,8 @@ TEST_P(AudioEffectHidlTest, GetDescriptor) { } }); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, retval); + ASSERT_NO_FATAL_FAILURE(checkResult(retval)); + if (useAfterClose) return; EXPECT_EQ(getEffectType(), actualType); } @@ -307,7 +341,8 @@ TEST_P(AudioEffectHidlTest, GetSetConfig) { } }); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, retval); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); + if (useAfterClose) return; Return ret2 = effect->setConfig(currentConfig, nullptr, nullptr); EXPECT_TRUE(ret2.isOk()); EXPECT_EQ(Result::OK, ret2); @@ -336,7 +371,8 @@ TEST_P(AudioEffectHidlTest, SetConfigInvalidArguments) { } }); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, retval); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); + if (useAfterClose) return; for (const auto& invalidInputCfg : generateInvalidConfigs(currentConfig.inputCfg)) { EffectConfig invalidConfig = currentConfig; invalidConfig.inputCfg = invalidInputCfg; @@ -356,27 +392,35 @@ TEST_P(AudioEffectHidlTest, SetConfigInvalidArguments) { TEST_P(AudioEffectHidlTest, GetConfigReverse) { description("Verify that GetConfigReverse does not crash"); - Return ret = effect->getConfigReverse([&](Result, const EffectConfig&) {}); + Result retval = Result::OK; + Return ret = effect->getConfigReverse([&](Result r, const EffectConfig&) { retval = r; }); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval)); } TEST_P(AudioEffectHidlTest, GetSupportedAuxChannelsConfigs) { description("Verify that GetSupportedAuxChannelsConfigs does not crash"); + Result retval = Result::OK; Return ret = effect->getSupportedAuxChannelsConfigs( - 0, [&](Result, const hidl_vec&) {}); + 0, [&](Result r, const hidl_vec&) { retval = r; }); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval)); } TEST_P(AudioEffectHidlTest, GetAuxChannelsConfig) { description("Verify that GetAuxChannelsConfig does not crash"); - Return ret = effect->getAuxChannelsConfig([&](Result, const EffectAuxChannelsConfig&) {}); + Result retval = Result::OK; + Return ret = effect->getAuxChannelsConfig( + [&](Result r, const EffectAuxChannelsConfig&) { retval = r; }); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval)); } TEST_P(AudioEffectHidlTest, SetAuxChannelsConfig) { description("Verify that SetAuxChannelsConfig does not crash"); Return ret = effect->setAuxChannelsConfig(EffectAuxChannelsConfig()); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret)); } // Not generated automatically because AudioBuffer contains @@ -427,45 +471,56 @@ TEST_P(AudioEffectHidlTest, Reset) { description("Verify that Reset preserves effect configuration"); Result retval = Result::NOT_INITIALIZED; EffectConfig originalConfig; - Return ret = effect->getConfig([&](Result r, const EffectConfig& conf) { - retval = r; - if (r == Result::OK) { - originalConfig = conf; - } - }); - ASSERT_TRUE(ret.isOk()); - ASSERT_EQ(Result::OK, retval); + if (!useAfterClose) { + Return ret = effect->getConfig([&](Result r, const EffectConfig& conf) { + retval = r; + if (r == Result::OK) { + originalConfig = conf; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, retval); + } Return ret2 = effect->reset(); EXPECT_TRUE(ret2.isOk()); - EXPECT_EQ(Result::OK, ret2); - EffectConfig configAfterReset; - ret = effect->getConfig([&](Result r, const EffectConfig& conf) { - retval = r; - if (r == Result::OK) { - configAfterReset = conf; - } - }); - EXPECT_EQ(originalConfig, configAfterReset); + EXPECT_NO_FATAL_FAILURE(checkResult(ret2)); + if (!useAfterClose) { + EffectConfig configAfterReset; + Return ret = effect->getConfig([&](Result r, const EffectConfig& conf) { + retval = r; + if (r == Result::OK) { + configAfterReset = conf; + } + }); + EXPECT_EQ(originalConfig, configAfterReset); + } } TEST_P(AudioEffectHidlTest, DisableEnableDisable) { description("Verify Disable -> Enable -> Disable sequence for an effect"); Return ret = effect->disable(); EXPECT_TRUE(ret.isOk()); - // Note: some legacy effects may return -EINVAL (INVALID_ARGUMENTS), - // more canonical is to return -ENOSYS (NOT_SUPPORTED) - EXPECT_TRUE(ret == Result::NOT_SUPPORTED || ret == Result::INVALID_ARGUMENTS); + if (!useAfterClose) { + // Note: some legacy effects may return -EINVAL (INVALID_ARGUMENTS), + // more canonical is to return -ENOSYS (NOT_SUPPORTED) + EXPECT_TRUE(ret == Result::NOT_SUPPORTED || ret == Result::INVALID_ARGUMENTS); + } else { + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret)); + } ret = effect->enable(); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); ret = effect->disable(); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); } #if MAJOR_VERSION >= 7 TEST_P(AudioEffectHidlTest, SetDeviceInvalidDeviceAddress) { description("Verify that invalid device address is rejected by SetDevice"); + if (useAfterClose) { + GTEST_SKIP() << "Does not make sense for the useAfterClose case"; + } DeviceAddress device{.deviceType = "random_string"}; Return ret = effect->setDevice(device); EXPECT_TRUE(ret.isOk()); @@ -482,13 +537,13 @@ TEST_P(AudioEffectHidlTest, SetDevice) { Return ret = effect->setDevice(device); #endif EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); } TEST_P(AudioEffectHidlTest, SetAndGetVolume) { description("Verify that SetAndGetVolume method works for an effect"); uint32_t channelCount; - getChannelCount(&channelCount); + ASSERT_NO_FATAL_FAILURE(getChannelCount(&channelCount)); hidl_vec volumes; volumes.resize(channelCount); for (uint32_t i = 0; i < channelCount; ++i) { @@ -498,13 +553,13 @@ TEST_P(AudioEffectHidlTest, SetAndGetVolume) { Return ret = effect->setAndGetVolume(volumes, [&](Result r, const hidl_vec&) { retval = r; }); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, retval); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); } TEST_P(AudioEffectHidlTest, VolumeChangeNotification) { description("Verify that effect accepts VolumeChangeNotification"); uint32_t channelCount; - getChannelCount(&channelCount); + ASSERT_NO_FATAL_FAILURE(getChannelCount(&channelCount)); hidl_vec volumes; volumes.resize(channelCount); for (uint32_t i = 0; i < channelCount; ++i) { @@ -512,25 +567,29 @@ TEST_P(AudioEffectHidlTest, VolumeChangeNotification) { } Return ret = effect->volumeChangeNotification(volumes); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); } TEST_P(AudioEffectHidlTest, SetAudioMode) { description("Verify that SetAudioMode works for an effect"); Return ret = effect->setAudioMode(AudioMode::NORMAL); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); } TEST_P(AudioEffectHidlTest, SetConfigReverse) { description("Verify that SetConfigReverse does not crash"); Return ret = effect->setConfigReverse(EffectConfig(), nullptr, nullptr); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret)); } #if MAJOR_VERSION >= 7 TEST_P(AudioEffectHidlTest, SetInputDeviceInvalidDeviceAddress) { description("Verify that invalid device address is rejected by SetInputDevice"); + if (useAfterClose) { + GTEST_SKIP() << "Does not make sense for the useAfterClose case"; + } DeviceAddress device{.deviceType = "random_string"}; Return ret = effect->setInputDevice(device); EXPECT_TRUE(ret.isOk()); @@ -548,11 +607,15 @@ TEST_P(AudioEffectHidlTest, SetInputDevice) { Return ret = effect->setInputDevice(device); #endif EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret)); } #if MAJOR_VERSION >= 7 TEST_P(AudioEffectHidlTest, SetInvalidAudioSource) { description("Verify that an invalid audio source is rejected by SetAudioSource"); + if (useAfterClose) { + GTEST_SKIP() << "Does not make sense for the useAfterClose case"; + } Return ret = effect->setAudioSource("random_string"); ASSERT_TRUE(ret.isOk()); EXPECT_TRUE(ret == Result::INVALID_ARGUMENTS || ret == Result::NOT_SUPPORTED) @@ -568,12 +631,14 @@ TEST_P(AudioEffectHidlTest, SetAudioSource) { Return ret = effect->setAudioSource(toString(xsd::AudioSource::AUDIO_SOURCE_MIC)); #endif EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret)); } TEST_P(AudioEffectHidlTest, Offload) { description("Verify that calling Offload method does not crash"); Return ret = effect->offload(EffectOffloadParameter{}); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret)); } TEST_P(AudioEffectHidlTest, PrepareForProcessing) { @@ -582,7 +647,7 @@ TEST_P(AudioEffectHidlTest, PrepareForProcessing) { Return ret = effect->prepareForProcessing( [&](Result r, const MQDescriptorSync&) { retval = r; }); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, retval); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); } TEST_P(AudioEffectHidlTest, SetProcessBuffers) { @@ -601,7 +666,7 @@ TEST_P(AudioEffectHidlTest, SetProcessBuffers) { ASSERT_TRUE(success); Return ret2 = effect->setProcessBuffers(buffer, buffer); EXPECT_TRUE(ret2.isOk()); - EXPECT_EQ(Result::OK, ret2); + EXPECT_NO_FATAL_FAILURE(checkResult(ret2)); } TEST_P(AudioEffectHidlTest, Command) { @@ -615,6 +680,7 @@ TEST_P(AudioEffectHidlTest, SetParameter) { description("Verify that SetParameter does not crash"); Return ret = effect->setParameter(hidl_vec(), hidl_vec()); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret)); } TEST_P(AudioEffectHidlTest, GetParameter) { @@ -630,6 +696,9 @@ TEST_P(AudioEffectHidlTest, GetParameterInvalidMaxReplySize) { if (!isNewDeviceLaunchingOnTPlus) { GTEST_SKIP() << "The test only applies to devices launching on T or later"; } + if (useAfterClose) { + GTEST_SKIP() << "Does not make sense for the useAfterClose case"; + } // Use a non-empty parameter to avoid being rejected by any earlier checks. hidl_vec parameter; parameter.resize(16); @@ -647,16 +716,20 @@ TEST_P(AudioEffectHidlTest, GetParameterInvalidMaxReplySize) { TEST_P(AudioEffectHidlTest, GetSupportedConfigsForFeature) { description("Verify that GetSupportedConfigsForFeature does not crash"); + Result retval = Result::OK; Return ret = effect->getSupportedConfigsForFeature( - 0, 0, 0, [&](Result, uint32_t, const hidl_vec&) {}); + 0, 0, 0, [&](Result r, uint32_t, const hidl_vec&) { retval = r; }); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval)); } TEST_P(AudioEffectHidlTest, GetCurrentConfigForFeature) { description("Verify that GetCurrentConfigForFeature does not crash"); - Return ret = - effect->getCurrentConfigForFeature(0, 0, [&](Result, const hidl_vec&) {}); + Result retval = Result::OK; + Return ret = effect->getCurrentConfigForFeature( + 0, 0, [&](Result r, const hidl_vec&) { retval = r; }); EXPECT_TRUE(ret.isOk()); + EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval)); } TEST_P(AudioEffectHidlTest, SetCurrentConfigForFeature) { @@ -671,6 +744,9 @@ TEST_P(AudioEffectHidlTest, GetSupportedConfigsForFeatureInvalidConfigSize) { if (!isNewDeviceLaunchingOnTPlus) { GTEST_SKIP() << "The test only applies to devices launching on T or later"; } + if (useAfterClose) { + GTEST_SKIP() << "Does not make sense for the useAfterClose case"; + } // Use very large size to ensure that the service does not crash. const uint32_t veryLargeConfigSize = std::numeric_limits::max() - 100; Result retval = Result::OK; @@ -687,6 +763,9 @@ TEST_P(AudioEffectHidlTest, GetCurrentConfigForFeatureInvalidConfigSize) { if (!isNewDeviceLaunchingOnTPlus) { GTEST_SKIP() << "The test only applies to devices launching on T or later"; } + if (useAfterClose) { + GTEST_SKIP() << "Does not make sense for the useAfterClose case"; + } // Use very large size to ensure that the service does not crash. const uint32_t veryLargeConfigSize = std::numeric_limits::max() - 100; Result retval = Result::OK; @@ -729,7 +808,8 @@ void EqualizerAudioEffectHidlTest::getNumBands(uint16_t* numBands) { } }); ASSERT_TRUE(ret.isOk()); - ASSERT_EQ(Result::OK, retval); + ASSERT_NO_FATAL_FAILURE(checkResult(retval)); + if (useAfterClose) *numBands = 1; } void EqualizerAudioEffectHidlTest::getLevelRange(int16_t* minLevel, int16_t* maxLevel) { @@ -742,7 +822,11 @@ void EqualizerAudioEffectHidlTest::getLevelRange(int16_t* minLevel, int16_t* max } }); ASSERT_TRUE(ret.isOk()); - ASSERT_EQ(Result::OK, retval); + ASSERT_NO_FATAL_FAILURE(checkResult(retval)); + if (useAfterClose) { + *minLevel = 0; + *maxLevel = 255; + } } void EqualizerAudioEffectHidlTest::getBandFrequencyRange(uint16_t band, uint32_t* minFreq, @@ -757,7 +841,7 @@ void EqualizerAudioEffectHidlTest::getBandFrequencyRange(uint16_t band, uint32_t } }); ASSERT_TRUE(ret.isOk()); - ASSERT_EQ(Result::OK, retval); + ASSERT_NO_FATAL_FAILURE(checkResult(retval)); ret = equalizer->getBandCenterFrequency(band, [&](Result r, uint32_t center) { retval = r; if (retval == Result::OK) { @@ -765,7 +849,12 @@ void EqualizerAudioEffectHidlTest::getBandFrequencyRange(uint16_t band, uint32_t } }); ASSERT_TRUE(ret.isOk()); - ASSERT_EQ(Result::OK, retval); + ASSERT_NO_FATAL_FAILURE(checkResult(retval)); + if (useAfterClose) { + *minFreq = 20; + *centerFreq = 10000; + *maxFreq = 20000; + } } void EqualizerAudioEffectHidlTest::getPresetCount(size_t* count) { @@ -777,37 +866,38 @@ void EqualizerAudioEffectHidlTest::getPresetCount(size_t* count) { } }); ASSERT_TRUE(ret.isOk()); - ASSERT_EQ(Result::OK, retval); + ASSERT_NO_FATAL_FAILURE(checkResult(retval)); + if (useAfterClose) *count = 1; } TEST_P(EqualizerAudioEffectHidlTest, GetNumBands) { description("Verify that Equalizer effect reports at least one band"); uint16_t numBands = 0; - getNumBands(&numBands); + ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands)); EXPECT_GT(numBands, 0); } TEST_P(EqualizerAudioEffectHidlTest, GetLevelRange) { description("Verify that Equalizer effect reports adequate band level range"); int16_t minLevel = 0x7fff, maxLevel = 0; - getLevelRange(&minLevel, &maxLevel); + ASSERT_NO_FATAL_FAILURE(getLevelRange(&minLevel, &maxLevel)); EXPECT_GT(maxLevel, minLevel); } TEST_P(EqualizerAudioEffectHidlTest, GetSetBandLevel) { description("Verify that manipulating band levels works for Equalizer effect"); uint16_t numBands = 0; - getNumBands(&numBands); + ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands)); ASSERT_GT(numBands, 0); int16_t levels[3]{0x7fff, 0, 0}; - getLevelRange(&levels[0], &levels[2]); + ASSERT_NO_FATAL_FAILURE(getLevelRange(&levels[0], &levels[2])); ASSERT_GT(levels[2], levels[0]); levels[1] = (levels[2] + levels[0]) / 2; for (uint16_t i = 0; i < numBands; ++i) { for (size_t j = 0; j < ARRAY_SIZE(levels); ++j) { Return ret = equalizer->setBandLevel(i, levels[j]); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); Result retval = Result::NOT_INITIALIZED; int16_t actualLevel; Return ret2 = equalizer->getBandLevel(i, [&](Result r, int16_t l) { @@ -817,8 +907,10 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetBandLevel) { } }); EXPECT_TRUE(ret2.isOk()); - EXPECT_EQ(Result::OK, retval); - EXPECT_EQ(levels[j], actualLevel); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); + if (!useAfterClose) { + EXPECT_EQ(levels[j], actualLevel); + } } } } @@ -826,11 +918,11 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetBandLevel) { TEST_P(EqualizerAudioEffectHidlTest, GetBandCenterFrequencyAndRange) { description("Verify that Equalizer effect reports adequate band frequency range"); uint16_t numBands = 0; - getNumBands(&numBands); + ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands)); ASSERT_GT(numBands, 0); for (uint16_t i = 0; i < numBands; ++i) { uint32_t minFreq = 0xffffffff, centerFreq = 0xffffffff, maxFreq = 0xffffffff; - getBandFrequencyRange(i, &minFreq, ¢erFreq, &maxFreq); + ASSERT_NO_FATAL_FAILURE(getBandFrequencyRange(i, &minFreq, ¢erFreq, &maxFreq)); // Note: NXP legacy implementation reports "1" as upper bound for last band, // so this check fails. EXPECT_GE(maxFreq, centerFreq); @@ -841,7 +933,7 @@ TEST_P(EqualizerAudioEffectHidlTest, GetBandCenterFrequencyAndRange) { TEST_P(EqualizerAudioEffectHidlTest, GetBandForFrequency) { description("Verify that Equalizer effect supports GetBandForFrequency correctly"); uint16_t numBands = 0; - getNumBands(&numBands); + ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands)); ASSERT_GT(numBands, 0); for (uint16_t i = 0; i < numBands; ++i) { uint32_t freqs[3]{0, 0, 0}; @@ -861,8 +953,10 @@ TEST_P(EqualizerAudioEffectHidlTest, GetBandForFrequency) { } }); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, retval); - EXPECT_EQ(i, actualBand) << "Frequency: " << freqs[j]; + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); + if (!useAfterClose) { + EXPECT_EQ(i, actualBand) << "Frequency: " << freqs[j]; + } } } } @@ -870,19 +964,19 @@ TEST_P(EqualizerAudioEffectHidlTest, GetBandForFrequency) { TEST_P(EqualizerAudioEffectHidlTest, GetPresetNames) { description("Verify that Equalizer effect reports at least one preset"); size_t presetCount; - getPresetCount(&presetCount); + ASSERT_NO_FATAL_FAILURE(getPresetCount(&presetCount)); EXPECT_GT(presetCount, 0u); } TEST_P(EqualizerAudioEffectHidlTest, GetSetCurrentPreset) { description("Verify that manipulating the current preset for Equalizer effect"); size_t presetCount; - getPresetCount(&presetCount); + ASSERT_NO_FATAL_FAILURE(getPresetCount(&presetCount)); ASSERT_GT(presetCount, 0u); for (uint16_t i = 0; i < presetCount; ++i) { Return ret = equalizer->setCurrentPreset(i); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); Result retval = Result::NOT_INITIALIZED; uint16_t actualPreset = 0xffff; Return ret2 = equalizer->getCurrentPreset([&](Result r, uint16_t p) { @@ -892,8 +986,10 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetCurrentPreset) { } }); EXPECT_TRUE(ret2.isOk()); - EXPECT_EQ(Result::OK, retval); - EXPECT_EQ(i, actualPreset); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); + if (!useAfterClose) { + EXPECT_EQ(i, actualPreset); + } } } @@ -904,7 +1000,7 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetAllProperties) { using AllProperties = ::android::hardware::audio::effect::CPP_VERSION::IEqualizerEffect::AllProperties; uint16_t numBands = 0; - getNumBands(&numBands); + ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands)); ASSERT_GT(numBands, 0); AllProperties props; props.bandLevels.resize(numBands); @@ -919,7 +1015,7 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetAllProperties) { props.curPreset = -1; Return ret = equalizer->setAllProperties(props); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); Return ret2 = equalizer->getAllProperties([&](Result r, AllProperties p) { retval = r; if (retval == Result::OK) { @@ -927,14 +1023,16 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetAllProperties) { } }); EXPECT_TRUE(ret2.isOk()); - EXPECT_EQ(Result::OK, retval); - EXPECT_EQ(props.bandLevels, actualProps.bandLevels); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); + if (!useAfterClose) { + EXPECT_EQ(props.bandLevels, actualProps.bandLevels); + } // Verify setting of the current preset via properties. props.curPreset = 0; // Assuming there is at least one preset. ret = equalizer->setAllProperties(props); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); ret2 = equalizer->getAllProperties([&](Result r, AllProperties p) { retval = r; if (retval == Result::OK) { @@ -942,8 +1040,10 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetAllProperties) { } }); EXPECT_TRUE(ret2.isOk()); - EXPECT_EQ(Result::OK, retval); - EXPECT_EQ(props.curPreset, actualProps.curPreset); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); + if (!useAfterClose) { + EXPECT_EQ(props.curPreset, actualProps.curPreset); + } } // The main test class for Equalizer Audio Effect HIDL HAL. @@ -971,7 +1071,7 @@ TEST_P(LoudnessEnhancerAudioEffectHidlTest, GetSetTargetGain) { const int32_t gain = 100; Return ret = enhancer->setTargetGain(gain); EXPECT_TRUE(ret.isOk()); - EXPECT_EQ(Result::OK, ret); + EXPECT_NO_FATAL_FAILURE(checkResult(ret)); int32_t actualGain = 0; Result retval; Return ret2 = enhancer->getTargetGain([&](Result r, int32_t g) { @@ -981,8 +1081,10 @@ TEST_P(LoudnessEnhancerAudioEffectHidlTest, GetSetTargetGain) { } }); EXPECT_TRUE(ret2.isOk()); - EXPECT_EQ(Result::OK, retval); - EXPECT_EQ(gain, actualGain); + EXPECT_NO_FATAL_FAILURE(checkResult(retval)); + if (!useAfterClose) { + EXPECT_EQ(gain, actualGain); + } } INSTANTIATE_TEST_SUITE_P(EffectsFactory, AudioEffectsFactoryHidlTest, @@ -993,25 +1095,29 @@ INSTANTIATE_TEST_SUITE_P( Equalizer_IEffect, AudioEffectHidlTest, ::testing::Combine(::testing::ValuesIn(::android::hardware::getAllHalInstanceNames( IEffectsFactory::descriptor)), - ::testing::Values(EQUALIZER_EFFECT_TYPE)), + ::testing::Values(EQUALIZER_EFFECT_TYPE), + ::testing::Values(false, true) /*useAfterClose*/), EffectParameterToString); INSTANTIATE_TEST_SUITE_P( LoudnessEnhancer_IEffect, AudioEffectHidlTest, ::testing::Combine(::testing::ValuesIn(::android::hardware::getAllHalInstanceNames( IEffectsFactory::descriptor)), - ::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE)), + ::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE), + ::testing::Values(false, true) /*useAfterClose*/), EffectParameterToString); INSTANTIATE_TEST_SUITE_P( Equalizer, EqualizerAudioEffectHidlTest, ::testing::Combine(::testing::ValuesIn(::android::hardware::getAllHalInstanceNames( IEffectsFactory::descriptor)), - ::testing::Values(EQUALIZER_EFFECT_TYPE)), + ::testing::Values(EQUALIZER_EFFECT_TYPE), + ::testing::Values(false, true) /*useAfterClose*/), EffectParameterToString); INSTANTIATE_TEST_SUITE_P( LoudnessEnhancer, LoudnessEnhancerAudioEffectHidlTest, ::testing::Combine(::testing::ValuesIn(::android::hardware::getAllHalInstanceNames( IEffectsFactory::descriptor)), - ::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE)), + ::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE), + ::testing::Values(false, true) /*useAfterClose*/), EffectParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. diff --git a/audio/policy/1.0/xml/pfw_schemas/Android.bp b/audio/policy/1.0/xml/pfw_schemas/Android.bp index 5d669c2a836d16ac6424317b0e4d04700aa80fd8..225c06577fc79c3b633a8ac33822d75b4982912a 100644 --- a/audio/policy/1.0/xml/pfw_schemas/Android.bp +++ b/audio/policy/1.0/xml/pfw_schemas/Android.bp @@ -11,6 +11,7 @@ xsd_config { name: "audio_policy_engine_configurable_configuration_V1_0", srcs: ["AllSchemas.xsd"], package_name: "audio.policy.configurable.V1_0", + root_elements: ["ParameterFrameworkConfiguration"], } // Unfortunately, all rules only have a single output, thus diff --git a/audio/policy/1.0/xml/pfw_schemas/api/current.txt b/audio/policy/1.0/xml/pfw_schemas/api/current.txt index c2fb6fc437cadd6907266caaf6b9999fee1cf756..2b83e6040071a157020d9edfb29ab2003ba2e2ae 100644 --- a/audio/policy/1.0/xml/pfw_schemas/api/current.txt +++ b/audio/policy/1.0/xml/pfw_schemas/api/current.txt @@ -470,23 +470,7 @@ package audio.policy.configurable.V1_0 { public class XmlParser { ctor public XmlParser(); - method public static audio.policy.configurable.V1_0.BitParameterBlock readBitParameterBlock(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.BooleanParameter readBooleanParameter(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.ComponentTypeSetType readComponentTypeSetType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.ComponentTypeSetType readComponentTypeSetType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.ConfigurableDomainType readConfigurableDomainType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.ConfigurableDomains readConfigurableDomains(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.EnumParameterType readEnumParameterType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.FixedPointParameterType readFixedPointParameterType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.FloatingPointParameterType readFloatingPointParameterType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.IntegerParameterType readIntegerParameterType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.LinearAdaptationType readLinearAdaptationType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.LogarithmicAdaptation readLogarithmicAdaptation(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static audio.policy.configurable.V1_0.ParameterFrameworkConfiguration readParameterFrameworkConfiguration(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.StringParameter readStringParameter(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.SubsystemPlugins readSubsystemPlugins(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.SubsystemType readSubsystemType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static audio.policy.configurable.V1_0.SystemClass readSystemClass(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; } diff --git a/oemlock/1.0/vts/functional/OWNERS b/authsecret/OWNERS similarity index 98% rename from oemlock/1.0/vts/functional/OWNERS rename to authsecret/OWNERS index ec8c304b67e2bbb692e26ae5acb3b15e3d0d9cba..5a5d074d0ef1ffe59cc5440fdd666749be1503be 100644 --- a/oemlock/1.0/vts/functional/OWNERS +++ b/authsecret/OWNERS @@ -1,3 +1,4 @@ # Bug component: 186411 + chengyouho@google.com frankwoo@google.com diff --git a/authsecret/aidl/default/Android.bp b/authsecret/aidl/default/Android.bp index 7ce83fd9eaed38b773ff0626b673731bf8b297ec..91f4faeab3739396c37dca969cd20575d63bd273 100644 --- a/authsecret/aidl/default/Android.bp +++ b/authsecret/aidl/default/Android.bp @@ -26,16 +26,49 @@ package { cc_binary { name: "android.hardware.authsecret-service.example", relative_install_path: "hw", - init_rc: ["android.hardware.authsecret-service.example.rc"], - vintf_fragments: ["android.hardware.authsecret-service.example.xml"], vendor: true, srcs: [ "service.cpp", "AuthSecret.cpp", ], shared_libs: [ + "libbinder_ndk", + "liblog", + ], + static_libs: [ "android.hardware.authsecret-V1-ndk", "libbase", - "libbinder_ndk", + ], + stl: "c++_static", +} + +prebuilt_etc { + name: "android.hardware.authsecret-service.example.rc", + src: "android.hardware.authsecret-service.example.rc", + installable: false, +} + +prebuilt_etc { + name: "android.hardware.authsecret-service.example.xml", + src: "android.hardware.authsecret-service.example.xml", + sub_dir: "vintf", + installable: false, +} + +apex { + name: "com.android.hardware.authsecret", + manifest: "manifest.json", + file_contexts: "file_contexts", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: [ + "android.hardware.authsecret-service.example", + ], + prebuilts: [ + "android.hardware.authsecret-service.example.rc", // init_rc + "android.hardware.authsecret-service.example.xml", // vintf_fragments ], } diff --git a/authsecret/aidl/default/android.hardware.authsecret-service.example.rc b/authsecret/aidl/default/android.hardware.authsecret-service.example.rc index fef6e2401e6e88a80c7bf044a94d01869f41d614..9f518375753116b4c130af22754242fa4c189d74 100644 --- a/authsecret/aidl/default/android.hardware.authsecret-service.example.rc +++ b/authsecret/aidl/default/android.hardware.authsecret-service.example.rc @@ -1,4 +1,4 @@ -service vendor.authsecret_default /vendor/bin/hw/android.hardware.authsecret-service.example +service vendor.authsecret_default /apex/com.android.hardware.authsecret/bin/hw/android.hardware.authsecret-service.example class hal user hsm group hsm diff --git a/authsecret/aidl/default/file_contexts b/authsecret/aidl/default/file_contexts new file mode 100644 index 0000000000000000000000000000000000000000..8e79f26c6dd910ab617d62283a77f5b708a8d49c --- /dev/null +++ b/authsecret/aidl/default/file_contexts @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/android\.hardware\.authsecret-service\.example u:object_r:hal_authsecret_default_exec:s0 \ No newline at end of file diff --git a/authsecret/aidl/default/manifest.json b/authsecret/aidl/default/manifest.json new file mode 100644 index 0000000000000000000000000000000000000000..ad5a45b444846fa2211160c54d96952ecedee08d --- /dev/null +++ b/authsecret/aidl/default/manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.authsecret", + "version": 1 +} diff --git a/authsecret/aidl/vts/OWNERS b/authsecret/aidl/vts/OWNERS deleted file mode 100644 index 40d95e4bf00bf45391f3f8572f76ef39fc672f14..0000000000000000000000000000000000000000 --- a/authsecret/aidl/vts/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -chengyouho@google.com -frankwoo@google.com diff --git a/automotive/audiocontrol/aidl/Android.bp b/automotive/audiocontrol/aidl/Android.bp index 9ae77cdea94e1f2c5cd5ef0b5cd90c2953726ae5..86b63a6d9f050ed7e1f63a873b5add6cf092acf0 100644 --- a/automotive/audiocontrol/aidl/Android.bp +++ b/automotive/audiocontrol/aidl/Android.bp @@ -13,9 +13,9 @@ aidl_interface { name: "android.hardware.automotive.audiocontrol", vendor_available: true, srcs: ["android/hardware/automotive/audiocontrol/*.aidl"], - imports: [ - "android.hardware.audio.common-V1", - "android.media.audio.common.types-V2", + defaults: [ + "latest_android_hardware_audio_common_import_interface", + "latest_android_media_audio_common_types_import_interface", ], stability: "vintf", backend: { @@ -52,6 +52,37 @@ aidl_interface { }, ], - frozen: true, + frozen: false, } + +// Note: This should always be one version ahead of the last frozen version +latest_android_hardware_automotive_audiocontrol = "android.hardware.automotive.audiocontrol-V4" + +cc_defaults { + name: "latest_android_hardware_automotive_audiocontrol_cpp_static", + static_libs: [ + latest_android_hardware_automotive_audiocontrol + "-cpp", + ], +} + +cc_defaults { + name: "latest_android_hardware_automotive_audiocontrol_cpp_shared", + shared_libs: [ + latest_android_hardware_automotive_audiocontrol + "-cpp", + ], +} + +cc_defaults { + name: "latest_android_hardware_automotive_audiocontrol_ndk_static", + static_libs: [ + latest_android_hardware_automotive_audiocontrol + "-ndk", + ], +} + +cc_defaults { + name: "latest_android_hardware_automotive_audiocontrol_ndk_shared", + shared_libs: [ + latest_android_hardware_automotive_audiocontrol + "-ndk", + ], +} diff --git a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml index 95cd7f024f433628eaebea39553d768b8187f26f..bcb5669f385713afa986d0e78f386d76cf945388 100644 --- a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml +++ b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml @@ -1,7 +1,7 @@ android.hardware.automotive.audiocontrol - 3 + 4 IAudioControl/default diff --git a/automotive/audiocontrol/aidl/vts/Android.bp b/automotive/audiocontrol/aidl/vts/Android.bp index cfc2a3e4ec8b0ec95b2c49cba5dfe3ab6a5e73b7..c73ad7939b1e5ff1022528153070a3981dff44ce 100644 --- a/automotive/audiocontrol/aidl/vts/Android.bp +++ b/automotive/audiocontrol/aidl/vts/Android.bp @@ -24,6 +24,8 @@ package { cc_test { name: "VtsAidlHalAudioControlTest", defaults: [ + "latest_android_hardware_audio_common_cpp_static", + "latest_android_hardware_automotive_audiocontrol_cpp_static", "latest_android_media_audio_common_types_cpp_static", "VtsHalTargetTestDefaults", "use_libaidlvintf_gtest_helper_static", @@ -38,8 +40,6 @@ cc_test { "libxml2", ], static_libs: [ - "android.hardware.automotive.audiocontrol-V3-cpp", - "android.hardware.audio.common-V1-cpp", "libgmock", ], test_suites: [ diff --git a/automotive/can/1.0/default/libc++fs/include/automotive/filesystem b/automotive/can/1.0/default/libc++fs/include/automotive/filesystem index 660ad09745b15e5daa17aebf64bb96dd992a5bdb..bd3dda551883c4d92bc997003cc666f162cfe8c8 100644 --- a/automotive/can/1.0/default/libc++fs/include/automotive/filesystem +++ b/automotive/can/1.0/default/libc++fs/include/automotive/filesystem @@ -9,6 +9,18 @@ //===----------------------------------------------------------------------===// #ifndef _LIBAUTO_FILESYSTEM #define _LIBAUTO_FILESYSTEM + +// TODO(152067309): Remove this once the libc++ upgrade is complete. +#include <__config> +#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION > 8000 + +#include +namespace android::hardware::automotive { +namespace filesystem = std::filesystem; +} + +#else + /* filesystem synopsis @@ -2696,4 +2708,6 @@ end(const recursive_directory_iterator&) noexcept { _LIBCPP_POP_MACROS +#endif // defined(_LIBCPP_VERSION) && _LIBCPP_VERSION > 8000 + #endif // _LIBAUTO_FILESYSTEM diff --git a/automotive/can/1.0/default/libc++fs/src/filesystem/directory_iterator.cpp b/automotive/can/1.0/default/libc++fs/src/filesystem/directory_iterator.cpp index 37c863bfcb94c972072ade2855880cf1c95d812f..0dbf492106a167893e191a26e6c024e323056d23 100644 --- a/automotive/can/1.0/default/libc++fs/src/filesystem/directory_iterator.cpp +++ b/automotive/can/1.0/default/libc++fs/src/filesystem/directory_iterator.cpp @@ -6,9 +6,15 @@ // Source Licenses. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// + +// TODO(152067309): Remove this once the libc++ upgrade is complete. +#include <__config> +#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 8000 + /* clang-format off */ #include "automotive/filesystem" #include <__config> + #if defined(_LIBCPP_WIN32API) #define WIN32_LEAN_AND_MEAN #include @@ -395,3 +401,5 @@ bool recursive_directory_iterator::__try_recursion(error_code* ec) { } // namespace android::hardware::automotive::filesystem /* clang-format on */ + +#endif // defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 8000 diff --git a/automotive/can/1.0/default/libc++fs/src/filesystem/operations.cpp b/automotive/can/1.0/default/libc++fs/src/filesystem/operations.cpp index 404c0bd4c97edec11537ca61db80abbc2c030864..6a76bdce55ac1b1edc14e7303fe7d27f45621e71 100644 --- a/automotive/can/1.0/default/libc++fs/src/filesystem/operations.cpp +++ b/automotive/can/1.0/default/libc++fs/src/filesystem/operations.cpp @@ -6,6 +6,11 @@ // Source Licenses. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// + +// TODO(152067309): Remove this once the libc++ upgrade is complete. +#include <__config> +#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 8000 + /* clang-format off */ #include "automotive/filesystem" #include @@ -1771,3 +1776,5 @@ error_code directory_entry::__do_refresh() noexcept { #endif } // namespace android::hardware::automotive::filesystem /* clang-format on */ + +#endif // defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 8000 diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp index aad07de6d78c6280a9ce81235e7694eb9ab65d79..fe749f6c1fd55f8d6eb05fbaec4a78858cfdaa99 100644 --- a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp +++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp @@ -102,7 +102,7 @@ std::optional isUp(std::string ifname) { static bool hasIpv4(std::string ifname) { auto ifr = ifreqs::fromName(ifname); - switch (const auto status = ifreqs::trySend(SIOCGIFADDR, ifr)) { + switch (ifreqs::trySend(SIOCGIFADDR, ifr)) { case 0: return true; case EADDRNOTAVAIL: diff --git a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp index 96110dbe401eb28498a61ec5764eef891c4e3b8e..e8821606a60b277696a8f9ec707991dfdfafefcb 100644 --- a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp +++ b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp @@ -18,9 +18,9 @@ namespace android::hardware::automotive::can::V1_0::implementation::fuzzer { -constexpr CanController::InterfaceType kInterfaceType[] = {CanController::InterfaceType::VIRTUAL, - CanController::InterfaceType::SOCKETCAN, - CanController::InterfaceType::SLCAN}; +constexpr CanController::InterfaceType kInterfaceType[] = { + CanController::InterfaceType::VIRTUAL, CanController::InterfaceType::SOCKETCAN, + CanController::InterfaceType::SLCAN, CanController::InterfaceType::INDEXED}; constexpr FilterFlag kFilterFlag[] = {FilterFlag::DONT_CARE, FilterFlag::SET, FilterFlag::NOT_SET}; constexpr size_t kInterfaceTypeLength = std::size(kInterfaceType); constexpr size_t kFilterFlagLength = std::size(kFilterFlag); @@ -28,8 +28,8 @@ constexpr size_t kMaxCharacters = 30; constexpr size_t kMaxPayloadBytes = 64; constexpr size_t kMaxFilters = 20; constexpr size_t kMaxSerialNumber = 1000; -constexpr size_t kMaxBuses = 10; -constexpr size_t kMaxRepeat = 5; +constexpr size_t kMaxBuses = 100; +constexpr size_t kMaxRepeat = 100; Bus CanFuzzer::makeBus() { ICanController::BusConfig config = {}; @@ -56,9 +56,13 @@ hidl_vec CanFuzzer::getBusNames() { } void CanFuzzer::invokeUpInterface() { - const CanController::InterfaceType iftype = - kInterfaceType[mFuzzedDataProvider->ConsumeIntegralInRange( - 0, kInterfaceTypeLength - 1)]; + CanController::InterfaceType controller; + if (mFuzzedDataProvider->ConsumeBool()) { + controller = (CanController::InterfaceType)mFuzzedDataProvider->ConsumeIntegral(); + } else { + controller = kInterfaceType[mFuzzedDataProvider->ConsumeIntegralInRange( + 0, kInterfaceTypeLength - 1)]; + } std::string configName; if (const bool shouldInvokeValidBus = mFuzzedDataProvider->ConsumeBool(); @@ -73,7 +77,7 @@ void CanFuzzer::invokeUpInterface() { ICanController::BusConfig config = {.name = configName}; - if (iftype == CanController::InterfaceType::SOCKETCAN) { + if (controller == CanController::InterfaceType::SOCKETCAN) { CanController::BusConfig::InterfaceId::Socketcan socketcan = {}; if (const bool shouldPassSerialSocket = mFuzzedDataProvider->ConsumeBool(); shouldPassSerialSocket) { @@ -83,7 +87,7 @@ void CanFuzzer::invokeUpInterface() { socketcan.ifname(ifname); } config.interfaceId.socketcan(socketcan); - } else if (iftype == CanController::InterfaceType::SLCAN) { + } else if (controller == CanController::InterfaceType::SLCAN) { CanController::BusConfig::InterfaceId::Slcan slcan = {}; if (const bool shouldPassSerialSlcan = mFuzzedDataProvider->ConsumeBool(); shouldPassSerialSlcan) { @@ -93,8 +97,12 @@ void CanFuzzer::invokeUpInterface() { slcan.ttyname(ifname); } config.interfaceId.slcan(slcan); - } else if (iftype == CanController::InterfaceType::VIRTUAL) { + } else if (controller == CanController::InterfaceType::VIRTUAL) { config.interfaceId.virtualif({ifname}); + } else if (controller == CanController::InterfaceType::INDEXED) { + CanController::BusConfig::InterfaceId::Indexed indexed; + indexed.index = mFuzzedDataProvider->ConsumeIntegral(); + config.interfaceId.indexed(indexed); } const size_t numInvocations = @@ -108,8 +116,13 @@ void CanFuzzer::invokeDownInterface() { hidl_string configName; if (const bool shouldInvokeValidBus = mFuzzedDataProvider->ConsumeBool(); (shouldInvokeValidBus) && (mBusNames.size() > 0)) { - const size_t busNameIndex = - mFuzzedDataProvider->ConsumeIntegralInRange(0, mBusNames.size() - 1); + size_t busNameIndex; + if (mBusNames.size() == 1) { + busNameIndex = 0; + } else { + busNameIndex = + mFuzzedDataProvider->ConsumeIntegralInRange(0, mBusNames.size() - 1); + } configName = mBusNames[busNameIndex]; } else { configName = mFuzzedDataProvider->ConsumeRandomLengthString(kMaxCharacters); @@ -122,12 +135,6 @@ void CanFuzzer::invokeDownInterface() { } } -void CanFuzzer::invokeController() { - getSupportedInterfaceTypes(); - invokeUpInterface(); - invokeDownInterface(); -} - void CanFuzzer::invokeBus() { const size_t numBuses = mFuzzedDataProvider->ConsumeIntegralInRange(1, kMaxBuses); for (size_t i = 0; i < numBuses; ++i) { @@ -152,12 +159,22 @@ void CanFuzzer::invokeBus() { for (uint32_t k = 0; k < numFilters; ++k) { filterVector[k].id = mFuzzedDataProvider->ConsumeIntegral(); filterVector[k].mask = mFuzzedDataProvider->ConsumeIntegral(); - filterVector[k].rtr = - kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange( - 0, kFilterFlagLength - 1)]; - filterVector[k].extendedFormat = - kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange( - 0, kFilterFlagLength - 1)]; + if (mFuzzedDataProvider->ConsumeBool()) { + filterVector[k].rtr = + (FilterFlag)mFuzzedDataProvider->ConsumeIntegral(); + } else { + filterVector[k].rtr = + kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange( + 0, kFilterFlagLength - 1)]; + } + if (mFuzzedDataProvider->ConsumeBool()) { + filterVector[k].extendedFormat = + (FilterFlag)mFuzzedDataProvider->ConsumeIntegral(); + } else { + filterVector[k].extendedFormat = + kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange( + 0, kFilterFlagLength - 1)]; + } filterVector[k].exclude = mFuzzedDataProvider->ConsumeBool(); } auto listener = listeningBus.listen(filterVector); @@ -175,8 +192,16 @@ void CanFuzzer::deInit() { void CanFuzzer::process(const uint8_t* data, size_t size) { mFuzzedDataProvider = new FuzzedDataProvider(data, size); - invokeController(); - invokeBus(); + while (mFuzzedDataProvider->remaining_bytes()) { + auto CanFuzzerFunction = + mFuzzedDataProvider->PickValueInArray>({ + [&]() { getSupportedInterfaceTypes(); }, + [&]() { invokeUpInterface(); }, + [&]() { invokeDownInterface(); }, + [&]() { invokeBus(); }, + }); + CanFuzzerFunction(); + } } bool CanFuzzer::init() { diff --git a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h index 930cddd89b153636804acb46281c9eeffb802f57..3211bd0b8e74fdc0176fbe96137a284b215dc4c9 100644 --- a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h +++ b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h @@ -116,7 +116,6 @@ class CanFuzzer { hidl_vec getBusNames(); void getSupportedInterfaceTypes(); void invokeBus(); - void invokeController(); void invokeUpInterface(); void invokeDownInterface(); FuzzedDataProvider* mFuzzedDataProvider = nullptr; diff --git a/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp b/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp index 9c72acdf8ff4ee7c645080557d97785079eed365..580b0ee721d66bae82f28f9f819bb10b9fcc76cb 100644 --- a/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp +++ b/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp @@ -66,8 +66,8 @@ public: ASSERT_NE(pEnumerator.get(), nullptr); - // "default" is reserved for EVS manager. - constexpr static char kEvsManagerName[] = "default"; + // "legacy_sw/0" is reserved for EVS manager v1.0 implementation. + constexpr static char kEvsManagerName[] = "legacy_sw/0"; mIsHwModule = service_name.compare(kEvsManagerName); } @@ -364,8 +364,14 @@ TEST_P(EvsHidlTest, CameraStreamPerformance) { TEST_P(EvsHidlTest, CameraStreamBuffering) { ALOGI("Starting CameraStreamBuffering test"); - // Arbitrary constant (should be > 1 and not too big) - static const unsigned int kBuffersToHold = 2; + // Maximum number of frames in flight this test case will attempt. This test + // case chooses an arbitrary number that is large enough to run a camera + // pipeline for a single client. + constexpr unsigned int kMaxBuffersToHold = 20; + + // Initial value for setMaxFramesInFlight() call. This number should be + // greater than 1. + unsigned int buffersToHold = 2; // Get the camera list loadCameraList(); @@ -381,9 +387,16 @@ TEST_P(EvsHidlTest, CameraStreamBuffering) { EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult); // Now ask for exactly two buffers in flight as we'll test behavior in that case - Return goodResult = pCam->setMaxFramesInFlight(kBuffersToHold); - EXPECT_EQ(EvsResult::OK, goodResult); - + // Find the minimum number of buffers to run a target camera. + while (buffersToHold < kMaxBuffersToHold) { + Return goodResult = pCam->setMaxFramesInFlight(buffersToHold); + if (goodResult == EvsResult::OK) { + break; + } + + ++buffersToHold; + } + EXPECT_LE(buffersToHold, kMaxBuffersToHold); // Set up a frame receiver object which will fire up its own thread. sp frameHandler = new FrameHandler(pCam, cam, @@ -399,7 +412,7 @@ TEST_P(EvsHidlTest, CameraStreamBuffering) { sleep(2); // 1 second should be enough for at least 5 frames to be delivered worst case unsigned framesReceived = 0; frameHandler->getFramesCounters(&framesReceived, nullptr); - ASSERT_EQ(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit"; + ASSERT_EQ(buffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit"; // Give back one buffer @@ -410,7 +423,7 @@ TEST_P(EvsHidlTest, CameraStreamBuffering) { // filled since we require 10fps minimum -- but give a 10% allowance just in case. usleep(110 * kMillisecondsToMicroseconds); frameHandler->getFramesCounters(&framesReceived, nullptr); - EXPECT_EQ(kBuffersToHold+1, framesReceived) << "Stream should've resumed"; + EXPECT_EQ(buffersToHold+1, framesReceived) << "Stream should've resumed"; // Even when the camera pointer goes out of scope, the FrameHandler object will // keep the stream alive unless we tell it to shutdown. diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp index 9c8bfc49964b47952bc078150c60d083c2e1e619..03f256ec8125308b9eb3c286e06f6e41d22c9773 100644 --- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp +++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp @@ -534,8 +534,14 @@ TEST_P(EvsHidlTest, CameraStreamPerformance) { TEST_P(EvsHidlTest, CameraStreamBuffering) { LOG(INFO) << "Starting CameraStreamBuffering test"; - // Arbitrary constant (should be > 1 and not too big) - static const unsigned int kBuffersToHold = 2; + // Maximum number of frames in flight this test case will attempt. This test + // case chooses an arbitrary number that is large enough to run a camera + // pipeline for a single client. + constexpr unsigned int kMaxBuffersToHold = 20; + + // Initial value for setMaxFramesInFlight() call. This number should be + // greater than 1. + unsigned int buffersToHold = 2; // Get the camera list loadCameraList(); @@ -567,9 +573,15 @@ TEST_P(EvsHidlTest, CameraStreamBuffering) { EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult); // Now ask for exactly two buffers in flight as we'll test behavior in that case - Return goodResult = pCam->setMaxFramesInFlight(kBuffersToHold); - EXPECT_EQ(EvsResult::OK, goodResult); + while (buffersToHold < kMaxBuffersToHold) { + Return goodResult = pCam->setMaxFramesInFlight(buffersToHold); + if (goodResult == EvsResult::OK) { + break; + } + ++buffersToHold; + } + EXPECT_LE(buffersToHold, kMaxBuffersToHold); // Set up a frame receiver object which will fire up its own thread. sp frameHandler = new FrameHandler(pCam, cam, @@ -585,7 +597,7 @@ TEST_P(EvsHidlTest, CameraStreamBuffering) { sleep(1); // 1 second should be enough for at least 5 frames to be delivered worst case unsigned framesReceived = 0; frameHandler->getFramesCounters(&framesReceived, nullptr); - ASSERT_EQ(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit"; + ASSERT_EQ(buffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit"; // Give back one buffer @@ -596,7 +608,7 @@ TEST_P(EvsHidlTest, CameraStreamBuffering) { // filled since we require 10fps minimum -- but give a 10% allowance just in case. usleep(110 * kMillisecondsToMicroseconds); frameHandler->getFramesCounters(&framesReceived, nullptr); - EXPECT_EQ(kBuffersToHold+1, framesReceived) << "Stream should've resumed"; + EXPECT_EQ(buffersToHold+1, framesReceived) << "Stream should've resumed"; // Even when the camera pointer goes out of scope, the FrameHandler object will // keep the stream alive unless we tell it to shutdown. diff --git a/automotive/evs/OWNERS b/automotive/evs/OWNERS index 15de48f04ef75d6032c8a823fefe76c6e39ed315..4787f0bdcbc04d834b58976ac2ee2f0c3971a14d 100644 --- a/automotive/evs/OWNERS +++ b/automotive/evs/OWNERS @@ -1,2 +1,2 @@ ankitarora@google.com -jwhpryor@google.com +changyeon@google.com diff --git a/automotive/evs/aidl/Android.bp b/automotive/evs/aidl/Android.bp index bafb4af30f3be3a440000794686b505109e61e06..3bfe8f306985abe6ea969958aded078b9727a00c 100644 --- a/automotive/evs/aidl/Android.bp +++ b/automotive/evs/aidl/Android.bp @@ -30,7 +30,7 @@ aidl_interface { stability: "vintf", imports: [ "android.hardware.common-V2", - "android.hardware.graphics.common-V4", + "android.hardware.graphics.common-V5", ], backend: { java: { @@ -53,14 +53,14 @@ aidl_interface { version: "1", imports: [ "android.hardware.common-V2", - "android.hardware.graphics.common-V4", + "android.hardware.graphics.common-V5", ], }, { version: "2", imports: [ "android.hardware.common-V2", - "android.hardware.graphics.common-V4", + "android.hardware.graphics.common-V5", ], }, diff --git a/automotive/evs/aidl/impl/default/Android.bp b/automotive/evs/aidl/impl/default/Android.bp index 70c523b5faff4076a7e9b314ac53b349188168b7..3d5b7c4956d8cbacd32e5e98ad4b13b457588c85 100644 --- a/automotive/evs/aidl/impl/default/Android.bp +++ b/automotive/evs/aidl/impl/default/Android.bp @@ -21,24 +21,11 @@ package { default_applicable_licenses: ["hardware_interfaces_license"], } -cc_binary { - name: "android.hardware.automotive.evs-aidl-default-service", +cc_defaults { + name: "android.hardware.automotive.evs-aidl-default-service-default", defaults: ["EvsHalDefaults"], - vintf_fragments: ["manifest_evs-default-service.xml"], - init_rc: ["evs-default-service.rc"], - vendor: true, - relative_install_path: "hw", - cflags: [ - "-DGL_GLEXT_PROTOTYPES", - "-DEGL_EGLEXT_PROTOTYPES", - "-Wall", - "-Wextra", - "-Werror", - "-Wthread-safety", - ], - srcs: [ - ":libgui_frame_event_aidl", - "src/*.cpp", + header_libs: [ + "libstagefright_headers", ], shared_libs: [ "android.hardware.graphics.bufferqueue@1.0", @@ -46,28 +33,63 @@ cc_binary { "android.hidl.token@1.0-utils", "libEGL", "libGLESv2", - "libbase", "libbinder_ndk", "libbufferqueueconverter", "libcamera_metadata", "libhardware_legacy", "libhidlbase", - "liblog", + "libmediandk", "libnativewindow", "libtinyxml2", "libui", - "libutils", "libyuv", ], - static_libs: [ - "android.frameworks.automotive.display-V1-ndk", +} + +cc_library { + name: "android.hardware.automotive.evs-aidl-default-service-lib", + defaults: ["android.hardware.automotive.evs-aidl-default-service-default"], + vendor: true, + cflags: [ + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + ], + srcs: [ + ":libgui_frame_event_aidl", + "src/*.cpp", + ], + exclude_srcs: ["src/service.cpp"], + whole_static_libs: [ + "android.frameworks.automotive.display-V2-ndk", "android.hardware.automotive.evs-V2-ndk", "android.hardware.common-V2-ndk", "libaidlcommonsupport", "libcutils", ], + header_libs: [ + "libgui_aidl_headers", + ], local_include_dirs: ["include"], include_dirs: ["frameworks/native/include/"], + export_include_dirs: ["include"], +} + +cc_binary { + name: "android.hardware.automotive.evs-aidl-default-service", + defaults: ["android.hardware.automotive.evs-aidl-default-service-default"], + vintf_fragments: ["manifest_evs-default-service.xml"], + init_rc: ["evs-default-service.rc"], + vendor: true, + relative_install_path: "hw", + cflags: [ + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + ], + srcs: ["src/service.cpp"], + static_libs: [ + "android.hardware.automotive.evs-aidl-default-service-lib", + ], + include_dirs: ["frameworks/native/include/"], required: ["evs_mock_hal_configuration.xml"], } @@ -77,3 +99,31 @@ prebuilt_etc { src: "resources/evs_mock_configuration.xml", sub_dir: "automotive/evs", } + +cc_test { + name: "android.hardware.automotive.evs-aidl-default-service_cam_buffer_test", + defaults: ["android.hardware.automotive.evs-aidl-default-service-default"], + vendor: true, + srcs: ["tests/EvsCameraBufferTest.cpp"], + static_libs: [ + "android.hardware.automotive.evs-aidl-default-service-lib", + "libgmock", + ], + test_suites: [ + "general-tests", + ], +} + +cc_test { + name: "android.hardware.automotive.evs-aidl-default-service_cam_state_test", + defaults: ["android.hardware.automotive.evs-aidl-default-service-default"], + vendor: true, + srcs: ["tests/EvsCameraStateTest.cpp"], + static_libs: [ + "android.hardware.automotive.evs-aidl-default-service-lib", + "libgmock", + ], + test_suites: [ + "general-tests", + ], +} diff --git a/automotive/evs/aidl/impl/default/include/ConfigManager.h b/automotive/evs/aidl/impl/default/include/ConfigManager.h index 1d5fe772b243f4badb30851f406a45fc90104d59..37a17dc230e1100232720bd4a0c4fb59ef485fef 100644 --- a/automotive/evs/aidl/impl/default/include/ConfigManager.h +++ b/automotive/evs/aidl/impl/default/include/ConfigManager.h @@ -25,8 +25,10 @@ #include +#include #include #include +#include #include #include #include @@ -54,6 +56,15 @@ class ConfigManager final { /* Camera device's capabilities and metadata */ class CameraInfo { public: + enum class DeviceType : std::int32_t { + NONE = 0, + MOCK = 1, + V4L2 = 2, + VIDEO = 3, + + UNKNOWN = std::numeric_limits>::max(), + }; + CameraInfo() : characteristics(nullptr) {} virtual ~CameraInfo(); @@ -69,6 +80,10 @@ class ConfigManager final { return characteristics != nullptr; } + static DeviceType deviceTypeFromSV(const std::string_view sv); + + DeviceType deviceType{DeviceType::NONE}; + /* * List of supported controls that the primary client can program. * Paraemters are stored with its valid range diff --git a/automotive/evs/aidl/impl/default/include/EvsAllCameras.h b/automotive/evs/aidl/impl/default/include/EvsAllCameras.h new file mode 100644 index 0000000000000000000000000000000000000000..a76501d7581efd3eef5a07d721404a59990e353d --- /dev/null +++ b/automotive/evs/aidl/impl/default/include/EvsAllCameras.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EvsMockCamera.h" +#include "EvsVideoEmulatedCamera.h" diff --git a/automotive/evs/aidl/impl/default/include/EvsCamera.h b/automotive/evs/aidl/impl/default/include/EvsCamera.h new file mode 100644 index 0000000000000000000000000000000000000000..539d5f6f9c762806e691e898a8375ff05c78c412 --- /dev/null +++ b/automotive/evs/aidl/impl/default/include/EvsCamera.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EvsCameraBase.h" + +#include +#include + +#include +#include +#include +#include + +namespace aidl::android::hardware::automotive::evs::implementation { + +class EvsCamera : public EvsCameraBase { + private: + using Base = EvsCameraBase; + using Self = EvsCamera; + + public: + using Base::Base; + + ~EvsCamera() override; + + // Methods from ::android::hardware::automotive::evs::IEvsCamera follow. + ndk::ScopedAStatus doneWithFrame(const std::vector& buffers) override; + + ndk::ScopedAStatus importExternalBuffers(const std::vector& buffers, + int32_t* _aidl_return) override; + + ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override; + + ndk::ScopedAStatus startVideoStream( + const std::shared_ptr& receiver) override; + + ndk::ScopedAStatus stopVideoStream() override; + + ndk::ScopedAStatus pauseVideoStream() override; + + ndk::ScopedAStatus resumeVideoStream() override; + + protected: + virtual ::android::status_t allocateOneFrame(buffer_handle_t* handle) = 0; + + virtual void freeOneFrame(const buffer_handle_t handle); + + virtual bool preVideoStreamStart_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck); + + virtual bool startVideoStreamImpl_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck) = 0; + + virtual bool postVideoStreamStart_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck); + + virtual bool preVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck); + + virtual bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) = 0; + + virtual bool postVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck); + + void shutdown() override; + + void closeAllBuffers_unsafe(); + + // Returns (ID, handle) if succeeds. (kInvalidBufferID, nullptr) otherwise. + [[nodiscard]] std::pair useBuffer_unsafe(); + + void returnBuffer_unsafe(const std::size_t id); + + bool increaseAvailableFrames_unsafe(const buffer_handle_t handle); + + bool decreaseAvailableFrames_unsafe(); + + bool setAvailableFrames_unsafe(const std::size_t bufferCount); + + void swapBufferFrames_unsafe(const std::size_t pos1, const std::size_t pos2); + + struct BufferRecord { + BufferRecord() = default; + BufferRecord(const BufferRecord&) = default; + BufferRecord(BufferRecord&&) = default; + BufferRecord& operator=(const BufferRecord&) = default; + BufferRecord& operator=(BufferRecord&&) = default; + ~BufferRecord() = default; + + explicit BufferRecord(buffer_handle_t h) : handle(h) {} + + buffer_handle_t handle{nullptr}; + bool inUse{false}; + }; + + enum class StreamState { + STOPPED = 0, + RUNNING = 1, + STOPPING = 2, + DEAD = 3, + }; + + StreamState mStreamState{StreamState::STOPPED}; + + std::mutex mMutex; + + // Graphics buffers to transfer images, always in the order of: + // In use buffers ... available buffers ... unavailable (unallocated) buffers. + std::vector mBuffers; + + // Double-mapping between buffer position and ID. + std::vector mBufferPosToId; + std::vector mBufferIdToPos; + + std::size_t mAvailableFrames{0}; + std::size_t mFramesInUse{0}; + + // We use all 1's as a reserved invalid buffer ID. + static constexpr std::size_t kInvalidBufferID = ~static_cast(0); + + public: + static bool IsBufferIDValid(const std::size_t bufferId) { return ~bufferId; } +}; + +} // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/evs/aidl/impl/default/include/EvsCameraBase.h b/automotive/evs/aidl/impl/default/include/EvsCameraBase.h new file mode 100644 index 0000000000000000000000000000000000000000..c3e9dfc47d7f55e5d46f2881d1bd8eba52a8ebb4 --- /dev/null +++ b/automotive/evs/aidl/impl/default/include/EvsCameraBase.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace aidl::android::hardware::automotive::evs::implementation { + +class EvsCameraBase : public evs::BnEvsCamera { + private: + using Base = evs::BnEvsCamera; + using Self = EvsCameraBase; + + public: + using Base::Base; + + ~EvsCameraBase() override = default; + + virtual void shutdown() = 0; + + protected: + // This is used for the derived classes and it prevents constructors from direct access + // while it allows this class to be instantiated via ndk::SharedRefBase::make<>. + struct Sigil { + explicit Sigil() = default; + }; +}; + +} // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/evs/aidl/impl/default/include/EvsEnumerator.h b/automotive/evs/aidl/impl/default/include/EvsEnumerator.h index 259c266721a4345c7a1ce9b419a7c3eb03927afe..9dcc774740ed0a7950a015aa195855c370b6eda7 100644 --- a/automotive/evs/aidl/impl/default/include/EvsEnumerator.h +++ b/automotive/evs/aidl/impl/default/include/EvsEnumerator.h @@ -17,8 +17,8 @@ #pragma once #include "ConfigManager.h" +#include "EvsCameraBase.h" #include "EvsGlDisplay.h" -#include "EvsMockCamera.h" #include #include @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -72,7 +73,7 @@ class EvsEnumerator final : public ::aidl::android::hardware::automotive::evs::B private: struct CameraRecord { evs::CameraDesc desc; - std::weak_ptr activeInstance; + std::weak_ptr activeInstance; CameraRecord(const char* cameraId) : desc() { desc.id = cameraId; } }; diff --git a/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h b/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h index ceabd9e8634bf8760a4f0cdfd78fb68c4b8245b6..0865a04109e0f011efc060ffb2dcf20c513b1f35 100644 --- a/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h +++ b/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h @@ -23,6 +23,7 @@ #include #include #include +#include #include diff --git a/automotive/evs/aidl/impl/default/include/EvsMockCamera.h b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h index 7e010a29e0f17d54f307b73422b4ef9e3aabd8b5..cd685324be33b59b7f386bdd823cbb504408a91c 100644 --- a/automotive/evs/aidl/impl/default/include/EvsMockCamera.h +++ b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h @@ -17,36 +17,36 @@ #pragma once #include "ConfigManager.h" +#include "EvsCamera.h" -#include -#include #include #include -#include #include #include #include #include -// #include #include #include -#include +#include +#include #include +#include +#include namespace aidl::android::hardware::automotive::evs::implementation { -class EvsMockCamera : public evs::BnEvsCamera { - // This prevents constructors from direct access while it allows this class to - // be instantiated via ndk::SharedRefBase::make<>. +class EvsMockCamera : public EvsCamera { private: - struct Sigil { - explicit Sigil() = default; - }; + using Base = EvsCamera; public: + EvsMockCamera(Sigil sigil, const char* deviceName, + std::unique_ptr& camInfo); + EvsMockCamera(const EvsMockCamera&) = delete; + EvsMockCamera& operator=(const EvsMockCamera&) = delete; + // Methods from ::android::hardware::automotive::evs::IEvsCamera follow. - ndk::ScopedAStatus doneWithFrame(const std::vector& buffers) override; ndk::ScopedAStatus forcePrimaryClient( const std::shared_ptr& display) override; ndk::ScopedAStatus getCameraInfo(evs::CameraDesc* _aidl_return) override; @@ -58,47 +58,37 @@ class EvsMockCamera : public evs::BnEvsCamera { ndk::ScopedAStatus getParameterList(std::vector* _aidl_return) override; ndk::ScopedAStatus getPhysicalCameraInfo(const std::string& deviceId, evs::CameraDesc* _aidl_return) override; - ndk::ScopedAStatus importExternalBuffers(const std::vector& buffers, - int32_t* _aidl_return) override; - ndk::ScopedAStatus pauseVideoStream() override; - ndk::ScopedAStatus resumeVideoStream() override; ndk::ScopedAStatus setExtendedInfo(int32_t opaqueIdentifier, const std::vector& opaqueValue) override; ndk::ScopedAStatus setIntParameter(evs::CameraParam id, int32_t value, std::vector* effectiveValue) override; ndk::ScopedAStatus setPrimaryClient() override; - ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override; - ndk::ScopedAStatus startVideoStream( - const std::shared_ptr& receiver) override; - ndk::ScopedAStatus stopVideoStream() override; ndk::ScopedAStatus unsetPrimaryClient() override; + const evs::CameraDesc& getDesc() { return mDescription; } + static std::shared_ptr Create(const char* deviceName); static std::shared_ptr Create( const char* deviceName, std::unique_ptr& camInfo, const evs::Stream* streamCfg = nullptr); - EvsMockCamera(const EvsMockCamera&) = delete; - EvsMockCamera& operator=(const EvsMockCamera&) = delete; - virtual ~EvsMockCamera() override; - void shutdown(); + private: + void generateFrames(); + void fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc); - const evs::CameraDesc& getDesc() { return mDescription; } + ::android::status_t allocateOneFrame(buffer_handle_t* handle) override; - // Constructors - EvsMockCamera(Sigil sigil, const char* deviceName, - std::unique_ptr& camInfo); + bool startVideoStreamImpl_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck) override; - private: - // These three functions are expected to be called while mAccessLock is held - bool setAvailableFrames_Locked(unsigned bufferCount); - unsigned increaseAvailableFrames_Locked(unsigned numToAdd); - unsigned decreaseAvailableFrames_Locked(unsigned numToRemove); + bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) override; - void generateFrames(); - void fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc); - void returnBufferLocked(const uint32_t bufferId); - ndk::ScopedAStatus stopVideoStream_impl(); + bool postVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) override; + + void initializeParameters(); CameraDesc mDescription = {}; // The properties of this camera @@ -119,28 +109,6 @@ class EvsMockCamera : public evs::BnEvsCamera { // Bytes per line in the buffers uint32_t mStride = 0; - struct BufferRecord { - buffer_handle_t handle; - bool inUse; - - explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false){}; - }; - - std::vector mBuffers; // Graphics buffers to transfer images - unsigned mFramesAllowed; // How many buffers are we currently using - unsigned mFramesInUse; // How many buffers are currently outstanding - - enum StreamStateValues { - STOPPED, - RUNNING, - STOPPING, - DEAD, - }; - StreamStateValues mStreamState; - - // Synchronization necessary to deconflict mCaptureThread from the main service thread - std::mutex mAccessLock; - // Static camera module information std::unique_ptr& mCameraInfo; @@ -160,7 +128,6 @@ class EvsMockCamera : public evs::BnEvsCamera { int32_t value; }; std::unordered_map> mParams; - void initializeParameters(); }; } // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h b/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h new file mode 100644 index 0000000000000000000000000000000000000000..a850d6598b4abd60bcca09b2830cbab3c9a843cf --- /dev/null +++ b/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "ConfigManager.h" +#include "EvsCamera.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::automotive::evs::implementation { + +class EvsVideoEmulatedCamera : public EvsCamera { + private: + using Base = EvsCamera; + + public: + EvsVideoEmulatedCamera(Sigil sigil, const char* deviceName, + std::unique_ptr& camInfo); + + ~EvsVideoEmulatedCamera() override = default; + + // Methods from ::android::hardware::automotive::evs::IEvsCamera follow. + ndk::ScopedAStatus forcePrimaryClient( + const std::shared_ptr& display) override; + ndk::ScopedAStatus getCameraInfo(evs::CameraDesc* _aidl_return) override; + ndk::ScopedAStatus getExtendedInfo(int32_t opaqueIdentifier, + std::vector* value) override; + ndk::ScopedAStatus getIntParameter(evs::CameraParam id, std::vector* value) override; + ndk::ScopedAStatus getIntParameterRange(evs::CameraParam id, + evs::ParameterRange* _aidl_return) override; + ndk::ScopedAStatus getParameterList(std::vector* _aidl_return) override; + ndk::ScopedAStatus getPhysicalCameraInfo(const std::string& deviceId, + evs::CameraDesc* _aidl_return) override; + ndk::ScopedAStatus setExtendedInfo(int32_t opaqueIdentifier, + const std::vector& opaqueValue) override; + ndk::ScopedAStatus setIntParameter(evs::CameraParam id, int32_t value, + std::vector* effectiveValue) override; + ndk::ScopedAStatus setPrimaryClient() override; + ndk::ScopedAStatus unsetPrimaryClient() override; + + // Methods from EvsCameraBase follow. + void shutdown() override; + + const evs::CameraDesc& getDesc() { return mDescription; } + + static std::shared_ptr Create(const char* deviceName); + static std::shared_ptr Create( + const char* deviceName, std::unique_ptr& camInfo, + const evs::Stream* streamCfg = nullptr); + + private: + // For the camera parameters. + struct CameraParameterDesc { + CameraParameterDesc(int min = 0, int max = 0, int step = 0, int value = 0) { + this->range.min = min; + this->range.max = max; + this->range.step = step; + this->value = value; + } + + ParameterRange range; + int32_t value; + }; + + bool initialize(); + + void generateFrames(); + + void renderOneFrame(); + + void initializeParameters(); + + void onCodecInputAvailable(const int32_t index); + + void onCodecOutputAvailable(const int32_t index, const AMediaCodecBufferInfo& info); + + ::android::status_t allocateOneFrame(buffer_handle_t* handle) override; + + bool startVideoStreamImpl_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck) override; + + bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) override; + + bool postVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) override; + + // The properties of this camera. + CameraDesc mDescription = {}; + + std::thread mCaptureThread; + + // The callback used to deliver each frame + std::shared_ptr mStream; + + std::string mVideoFileName; + // Media decoder resources - Owned by mDecoderThead when thread is running. + int mVideoFd = 0; + + struct AMediaExtractorDeleter { + void operator()(AMediaExtractor* extractor) const { AMediaExtractor_delete(extractor); } + }; + struct AMediaCodecDeleter { + void operator()(AMediaCodec* codec) const { AMediaCodec_delete(codec); } + }; + + std::unique_ptr mVideoExtractor; + std::unique_ptr mVideoCodec; + + // Horizontal pixel count in the buffers + int32_t mWidth = 0; + // Vertical pixel count in the buffers + int32_t mHeight = 0; + // Values from android_pixel_format_t + uint32_t mFormat = 0; + // Values from from Gralloc.h + uint64_t mUsage = 0; + // Bytes per line in the buffers + uint32_t mStride = 0; + + // Camera parameters. + std::unordered_map> mParams; + + // Static camera module information + std::unique_ptr& mCameraInfo; + + // For the extended info + std::unordered_map> mExtInfo; +}; + +} // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/evs/aidl/impl/default/include/GlWrapper.h b/automotive/evs/aidl/impl/default/include/GlWrapper.h index adb250c8e1296fb77ada83af434e6579d68ed9bb..7ff6104bb9883c88a16e07dd1c5ef95c9a011b0c 100644 --- a/automotive/evs/aidl/impl/default/include/GlWrapper.h +++ b/automotive/evs/aidl/impl/default/include/GlWrapper.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include namespace aidl::android::hardware::automotive::evs::implementation { @@ -33,7 +33,6 @@ namespace automotivedisplay = ::aidl::android::frameworks::automotive::display; class GlWrapper { public: - GlWrapper() : mSurfaceHolder(::android::SurfaceHolderUniquePtr(nullptr, nullptr)) {} bool initialize(const std::shared_ptr& svc, uint64_t displayId); void shutdown(); @@ -53,9 +52,6 @@ class GlWrapper { unsigned getHeight() { return mHeight; }; private: - ::android::sp<::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer> - mGfxBufferProducer; - EGLDisplay mDisplay; EGLSurface mSurface; EGLContext mContext; @@ -71,9 +67,6 @@ class GlWrapper { // Opaque handle for a native hardware buffer defined in // frameworks/native/opengl/include/EGL/eglplatform.h ANativeWindow* mWindow; - - // Pointer to a Surface wrapper. - ::android::SurfaceHolderUniquePtr mSurfaceHolder; }; } // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/evs/aidl/impl/default/src/ConfigManager.cpp b/automotive/evs/aidl/impl/default/src/ConfigManager.cpp index da791ed0b9c44ba2f274d98eaed691ea5422dee2..ba4cdc0c160f693d1a9400748ef5429543345069 100644 --- a/automotive/evs/aidl/impl/default/src/ConfigManager.cpp +++ b/automotive/evs/aidl/impl/default/src/ConfigManager.cpp @@ -40,6 +40,18 @@ std::string_view ConfigManager::sConfigDefaultPath = std::string_view ConfigManager::sConfigOverridePath = "/vendor/etc/automotive/evs/evs_configuration_override.xml"; +ConfigManager::CameraInfo::DeviceType ConfigManager::CameraInfo::deviceTypeFromSV( + const std::string_view sv) { + using namespace std::string_view_literals; + static const std::unordered_map nameToType = { + {"mock"sv, DeviceType::MOCK}, + {"v4l2"sv, DeviceType::V4L2}, + {"video"sv, DeviceType::VIDEO}, + }; + const auto search = nameToType.find(sv); + return search == nameToType.end() ? DeviceType::UNKNOWN : search->second; +} + void ConfigManager::printElementNames(const XMLElement* rootElem, const std::string& prefix) const { const XMLElement* curElem = rootElem; @@ -128,6 +140,10 @@ bool ConfigManager::readCameraDeviceInfo(CameraInfo* aCamera, const XMLElement* return false; } + if (const auto typeAttr = aDeviceElem->FindAttribute("type")) { + aCamera->deviceType = CameraInfo::deviceTypeFromSV(typeAttr->Value()); + } + /* size information to allocate camera_metadata_t */ size_t totalEntries = 0; size_t totalDataSize = 0; diff --git a/automotive/evs/aidl/impl/default/src/EvsCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsCamera.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc3bfdd367a471c5cc881b9770cb73701f0a3b83 --- /dev/null +++ b/automotive/evs/aidl/impl/default/src/EvsCamera.cpp @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2023 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 "EvsCamera.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace aidl::android::hardware::automotive::evs::implementation { + +// Arbitrary limit on number of graphics buffers allowed to be allocated +// Safeguards against unreasonable resource consumption and provides a testable limit +constexpr std::size_t kMaxBuffersInFlight = 100; + +// Minimum number of buffers to run a video stream +constexpr int kMinimumBuffersInFlight = 1; + +EvsCamera::~EvsCamera() { + shutdown(); +} + +ndk::ScopedAStatus EvsCamera::doneWithFrame(const std::vector& buffers) { + std::lock_guard lck(mMutex); + for (const auto& desc : buffers) { + returnBuffer_unsafe(desc.bufferId); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsCamera::importExternalBuffers(const std::vector& buffers, + int32_t* _aidl_return) { + if (buffers.empty()) { + LOG(DEBUG) << __func__ + << ": Ignoring a request to import external buffers with an empty list."; + return ndk::ScopedAStatus::ok(); + } + static auto& mapper = ::android::GraphicBufferMapper::get(); + std::lock_guard lck(mMutex); + std::size_t numBuffersToAdd = std::min(buffers.size(), kMaxBuffersInFlight - mAvailableFrames); + if (numBuffersToAdd == 0) { + LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit (" + << kMaxBuffersInFlight << "). Stop importing."; + return ndk::ScopedAStatus::ok(); + } else if (numBuffersToAdd < buffers.size()) { + LOG(WARNING) << "Exceeds the limit on the number of buffers. Only " << numBuffersToAdd + << " buffers will be imported. " << buffers.size() << " are asked."; + } + const size_t before = mAvailableFrames; + for (std::size_t idx = 0; idx < numBuffersToAdd; ++idx) { + auto& buffer = buffers[idx]; + const AHardwareBuffer_Desc* pDesc = + reinterpret_cast(&buffer.buffer.description); + + buffer_handle_t handleToImport = ::android::dupFromAidl(buffer.buffer.handle); + buffer_handle_t handleToStore = nullptr; + if (handleToImport == nullptr) { + LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer " + << buffer.bufferId; + continue; + } + + ::android::status_t result = + mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers, + pDesc->format, pDesc->usage, pDesc->stride, &handleToStore); + if (result != ::android::NO_ERROR || handleToStore == nullptr || + !increaseAvailableFrames_unsafe(handleToStore)) { + LOG(WARNING) << "Failed to import a buffer " << buffer.bufferId; + } + } + *_aidl_return = mAvailableFrames - before; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsCamera::setMaxFramesInFlight(int32_t bufferCount) { + std::lock_guard lock(mMutex); + if (bufferCount < 1) { + LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested."; + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::INVALID_ARG)); + } + if (!setAvailableFrames_unsafe(bufferCount)) { + LOG(ERROR) << "Failed to adjust the maximum number of frames in flight."; + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::BUFFER_NOT_AVAILABLE)); + } + return ndk::ScopedAStatus::ok(); +} + +void EvsCamera::freeOneFrame(const buffer_handle_t handle) { + static auto& alloc = ::android::GraphicBufferAllocator::get(); + alloc.free(handle); +} + +bool EvsCamera::preVideoStreamStart_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& /* lck */) { + if (!receiver) { + LOG(ERROR) << __func__ << ": Null receiver."; + status = ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::INVALID_ARG)); + return false; + } + + // If we've been displaced by another owner of the camera, then we can't do anything else + if (mStreamState == StreamState::DEAD) { + LOG(ERROR) << __func__ << ": Ignoring when camera has been lost."; + status = ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::OWNERSHIP_LOST)); + return false; + } + + if (mStreamState != StreamState::STOPPED) { + LOG(ERROR) << __func__ << ": Ignoring when a stream is already running."; + status = ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::STREAM_ALREADY_RUNNING)); + return false; + } + + // If the client never indicated otherwise, configure ourselves for a single streaming buffer + if (mAvailableFrames < kMinimumBuffersInFlight && + !setAvailableFrames_unsafe(kMinimumBuffersInFlight)) { + LOG(ERROR) << __func__ << "Failed to because we could not get a graphics buffer."; + status = ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::BUFFER_NOT_AVAILABLE)); + return false; + } + mStreamState = StreamState::RUNNING; + return true; +} + +bool EvsCamera::postVideoStreamStart_locked( + const std::shared_ptr& /* receiver */, + ndk::ScopedAStatus& /* status */, std::unique_lock& /* lck */) { + return true; +} + +bool EvsCamera::preVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& /* lck */) { + if (mStreamState != StreamState::RUNNING) { + // Terminate the stop process because a stream is not running. + status = ndk::ScopedAStatus::ok(); + return false; + } + mStreamState = StreamState::STOPPING; + return true; +} + +bool EvsCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& /* status */, + std::unique_lock& /* lck */) { + mStreamState = StreamState::STOPPED; + return true; +} + +ndk::ScopedAStatus EvsCamera::startVideoStream( + const std::shared_ptr& receiver) { + bool needShutdown = false; + auto status = ndk::ScopedAStatus::ok(); + { + std::unique_lock lck(mMutex); + if (!preVideoStreamStart_locked(receiver, status, lck)) { + return status; + } + + if ((!startVideoStreamImpl_locked(receiver, status, lck) || + !postVideoStreamStart_locked(receiver, status, lck)) && + !status.isOk()) { + needShutdown = true; + } + } + if (needShutdown) { + shutdown(); + } + return status; +} + +ndk::ScopedAStatus EvsCamera::stopVideoStream() { + bool needShutdown = false; + auto status = ndk::ScopedAStatus::ok(); + { + std::unique_lock lck(mMutex); + if ((!preVideoStreamStop_locked(status, lck) || !stopVideoStreamImpl_locked(status, lck) || + !postVideoStreamStop_locked(status, lck)) && + !status.isOk()) { + needShutdown = true; + } + } + if (needShutdown) { + shutdown(); + } + return status; +} + +ndk::ScopedAStatus EvsCamera::pauseVideoStream() { + return ndk::ScopedAStatus::fromServiceSpecificError(static_cast(EvsResult::NOT_SUPPORTED)); +} + +ndk::ScopedAStatus EvsCamera::resumeVideoStream() { + return ndk::ScopedAStatus::fromServiceSpecificError(static_cast(EvsResult::NOT_SUPPORTED)); +} + +bool EvsCamera::setAvailableFrames_unsafe(const std::size_t bufferCount) { + if (bufferCount < 1) { + LOG(ERROR) << "Ignoring request to set buffer count to zero."; + return false; + } + if (bufferCount > kMaxBuffersInFlight) { + LOG(ERROR) << "Rejecting buffer request in excess of internal limit"; + return false; + } + + if (bufferCount > mAvailableFrames) { + bool success = true; + const std::size_t numBufferBeforeAlloc = mAvailableFrames; + for (int numBufferToAllocate = bufferCount - mAvailableFrames; + success && numBufferToAllocate > 0; --numBufferToAllocate) { + buffer_handle_t handle = nullptr; + const auto result = allocateOneFrame(&handle); + if (result != ::android::NO_ERROR || !handle) { + LOG(ERROR) << __func__ << ": Failed to allocate a graphics buffer. Error " << result + << ", handle: " << handle; + success = false; + break; + } + success &= increaseAvailableFrames_unsafe(handle); + } + if (!success) { + // Rollback when failure. + for (int numBufferToRelease = mAvailableFrames - numBufferBeforeAlloc; + numBufferToRelease > 0; --numBufferToRelease) { + decreaseAvailableFrames_unsafe(); + } + return false; + } + } else { + for (int numBufferToRelease = mAvailableFrames - std::max(bufferCount, mFramesInUse); + numBufferToRelease > 0; --numBufferToRelease) { + decreaseAvailableFrames_unsafe(); + } + if (mAvailableFrames > bufferCount) { + // This shouldn't happen with a properly behaving client because the client + // should only make this call after returning sufficient outstanding buffers + // to allow a clean resize. + LOG(ERROR) << "Buffer queue shrink failed, asked: " << bufferCount + << ", actual: " << mAvailableFrames + << " -- too many buffers currently in use?"; + } + } + return true; +} + +void EvsCamera::shutdown() { + stopVideoStream(); + std::lock_guard lck(mMutex); + closeAllBuffers_unsafe(); + mStreamState = StreamState::DEAD; +} + +void EvsCamera::closeAllBuffers_unsafe() { + if (mFramesInUse > 0) { + LOG(WARNING) << __func__ << ": Closing while " << mFramesInUse + << " frame(s) are still in use."; + } + for (auto& buffer : mBuffers) { + freeOneFrame(buffer.handle); + buffer.handle = nullptr; + } + mBuffers.clear(); + mBufferPosToId.clear(); + mBufferIdToPos.clear(); +} + +std::pair EvsCamera::useBuffer_unsafe() { + if (mFramesInUse >= mAvailableFrames) { + DCHECK_EQ(mFramesInUse, mAvailableFrames); + return {kInvalidBufferID, nullptr}; + } + const std::size_t pos = mFramesInUse++; + auto& buffer = mBuffers[pos]; + DCHECK(!buffer.inUse); + DCHECK(buffer.handle); + buffer.inUse = true; + return {mBufferPosToId[pos], buffer.handle}; +} + +void EvsCamera::returnBuffer_unsafe(const std::size_t id) { + if (id >= mBuffers.size()) { + LOG(ERROR) << __func__ << ": ID out-of-bound. id: " << id + << " max: " << mBuffers.size() - 1; + return; + } + const std::size_t pos = mBufferIdToPos[id]; + + if (!mBuffers[pos].inUse) { + LOG(ERROR) << __func__ << ": Ignoring returning frame " << id << " which is already free."; + return; + } + DCHECK_LT(pos, mFramesInUse); + const std::size_t last_in_use_pos = --mFramesInUse; + swapBufferFrames_unsafe(pos, last_in_use_pos); + mBuffers[last_in_use_pos].inUse = false; +} + +bool EvsCamera::increaseAvailableFrames_unsafe(const buffer_handle_t handle) { + if (mAvailableFrames >= kMaxBuffersInFlight) { + LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit (" + << kMaxBuffersInFlight << "). Stop increasing."; + return false; + } + const std::size_t pos = mAvailableFrames++; + if (mAvailableFrames > mBuffers.size()) { + const std::size_t oldBufferSize = mBuffers.size(); + mBuffers.resize(mAvailableFrames); + mBufferPosToId.resize(mAvailableFrames); + mBufferIdToPos.resize(mAvailableFrames); + // Build position/ID mapping. + for (std::size_t idx = oldBufferSize; idx < mBuffers.size(); ++idx) { + mBufferPosToId[idx] = idx; + mBufferIdToPos[idx] = idx; + } + } + auto& buffer = mBuffers[pos]; + DCHECK(!buffer.inUse); + DCHECK(!buffer.handle); + buffer.handle = handle; + return true; +} + +bool EvsCamera::decreaseAvailableFrames_unsafe() { + if (mFramesInUse >= mAvailableFrames) { + DCHECK_EQ(mFramesInUse, mAvailableFrames); + return false; + } + const std::size_t pos = --mAvailableFrames; + auto& buffer = mBuffers[pos]; + DCHECK(!buffer.inUse); + DCHECK(buffer.handle); + freeOneFrame(buffer.handle); + buffer.handle = nullptr; + return true; +} + +void EvsCamera::swapBufferFrames_unsafe(const std::size_t pos1, const std::size_t pos2) { + if (pos1 == pos2) { + return; + } + if (pos1 >= mBuffers.size() || pos2 >= mBuffers.size()) { + LOG(ERROR) << __func__ << ": Index out-of-bound. pos1: " << pos1 << ", pos2: " << pos2 + << ", buffer size: " << mBuffers.size(); + return; + } + const std::size_t id1 = mBufferPosToId[pos1]; + const std::size_t id2 = mBufferPosToId[pos2]; + std::swap(mBufferPosToId[pos1], mBufferPosToId[pos2]); + std::swap(mBufferIdToPos[id1], mBufferIdToPos[id2]); + std::swap(mBuffers[pos1], mBuffers[pos2]); +} + +} // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp b/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp index 517895867597f7f0e27f0c41d1a3412b6278bdaa..80e72a73b500987fa21e17b6150bf1609a9ded68 100644 --- a/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp +++ b/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp @@ -17,8 +17,9 @@ #include "EvsEnumerator.h" #include "ConfigManager.h" +#include "EvsAllCameras.h" +#include "EvsCameraBase.h" #include "EvsGlDisplay.h" -#include "EvsMockCamera.h" #include #include @@ -243,7 +244,7 @@ ScopedAStatus EvsEnumerator::openCamera(const std::string& id, const Stream& cfg } // Has this camera already been instantiated by another caller? - std::shared_ptr pActiveCamera = pRecord->activeInstance.lock(); + std::shared_ptr pActiveCamera = pRecord->activeInstance.lock(); if (pActiveCamera) { LOG(WARNING) << "Killing previous camera because of new caller"; closeCamera(pActiveCamera); @@ -253,12 +254,31 @@ ScopedAStatus EvsEnumerator::openCamera(const std::string& id, const Stream& cfg if (!sConfigManager) { pActiveCamera = EvsMockCamera::Create(id.data()); } else { - pActiveCamera = EvsMockCamera::Create(id.data(), sConfigManager->getCameraInfo(id), &cfg); + auto& cameraInfo = sConfigManager->getCameraInfo(id); + switch (cameraInfo->deviceType) { + using DeviceType = ConfigManager::CameraInfo::DeviceType; + + // Default to MOCK for backward compatibility. + case DeviceType::NONE: + case DeviceType::MOCK: + pActiveCamera = EvsMockCamera::Create(id.data(), cameraInfo, &cfg); + break; + + case DeviceType::VIDEO: + pActiveCamera = EvsVideoEmulatedCamera::Create(id.data(), cameraInfo, &cfg); + break; + + default: + LOG(ERROR) << __func__ << ": camera device type " + << static_cast(cameraInfo->deviceType) + << " is not supported."; + break; + } } pRecord->activeInstance = pActiveCamera; if (!pActiveCamera) { - LOG(ERROR) << "Failed to create new EvsMockCamera object for " << id; + LOG(ERROR) << "Failed to create new EVS camera object for " << id; return ScopedAStatus::fromServiceSpecificError( static_cast(EvsResult::UNDERLYING_SERVICE_ERROR)); } @@ -445,7 +465,7 @@ void EvsEnumerator::closeCamera_impl(const std::shared_ptr& pCamera, if (!pRecord) { LOG(ERROR) << "Asked to close a camera whose name isn't recognized"; } else { - std::shared_ptr pActiveCamera = pRecord->activeInstance.lock(); + std::shared_ptr pActiveCamera = pRecord->activeInstance.lock(); if (!pActiveCamera) { LOG(WARNING) << "Somehow a camera is being destroyed " << "when the enumerator didn't know one existed"; diff --git a/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp b/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp index e5f8e4c84aa6fa849e1fc307a85f620a11eb9c34..5b5cbcc9aeb94bf7baab59541c05ed2250a5498c 100644 --- a/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp +++ b/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp @@ -352,8 +352,8 @@ ScopedAStatus EvsGlDisplay::getTargetBuffer(BufferDesc* _aidl_return) { BufferDesc bufferDescToSend = { .buffer = { - .handle = std::move(::android::dupToAidl(mBuffer.handle)), .description = mBuffer.description, + .handle = std::move(::android::dupToAidl(mBuffer.handle)), }, .pixelSizeBytes = 4, // RGBA_8888 is 4-byte-per-pixel format .bufferId = mBuffer.fingerprint, diff --git a/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp index 797b22154b7e5da76cde2a1a1f75dc850cc696cc..ef4392532176e98f800b09e68df873f2efad7d00 100644 --- a/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp +++ b/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp @@ -15,28 +15,25 @@ */ #include "EvsMockCamera.h" -#include "ConfigManager.h" -#include "EvsEnumerator.h" + +#include #include +#include #include #include #include +#include +#include #include +#include namespace { using ::aidl::android::hardware::graphics::common::BufferUsage; using ::ndk::ScopedAStatus; -// Arbitrary limit on number of graphics buffers allowed to be allocated -// Safeguards against unreasonable resource consumption and provides a testable limit -constexpr unsigned kMaxBuffersInFlight = 100; - -// Minimum number of buffers to run a video stream -constexpr int kMinimumBuffersInFlight = 1; - // Colors for the colorbar test pattern in ABGR format constexpr uint32_t kColors[] = { 0xFFFFFFFF, // white @@ -56,7 +53,7 @@ namespace aidl::android::hardware::automotive::evs::implementation { EvsMockCamera::EvsMockCamera([[maybe_unused]] Sigil sigil, const char* id, std::unique_ptr& camInfo) - : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED), mCameraInfo(camInfo) { + : mCameraInfo(camInfo) { LOG(DEBUG) << __FUNCTION__; /* set a camera id */ @@ -73,11 +70,6 @@ EvsMockCamera::EvsMockCamera([[maybe_unused]] Sigil sigil, const char* id, initializeParameters(); } -EvsMockCamera::~EvsMockCamera() { - LOG(DEBUG) << __FUNCTION__; - shutdown(); -} - void EvsMockCamera::initializeParameters() { mParams.emplace( CameraParam::BRIGHTNESS, @@ -90,35 +82,6 @@ void EvsMockCamera::initializeParameters() { new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255)); } -// This gets called if another caller "steals" ownership of the camera -void EvsMockCamera::shutdown() { - LOG(DEBUG) << __FUNCTION__; - - // Make sure our output stream is cleaned up - // (It really should be already) - stopVideoStream_impl(); - - // Claim the lock while we work on internal state - std::lock_guard lock(mAccessLock); - - // Drop all the graphics buffers we've been using - if (mBuffers.size() > 0) { - ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get()); - for (auto&& rec : mBuffers) { - if (rec.inUse) { - LOG(WARNING) << "WARNING: releasing a buffer remotely owned."; - } - alloc.free(rec.handle); - rec.handle = nullptr; - } - mBuffers.clear(); - } - - // Put this object into an unrecoverable error state since somebody else - // is going to own the underlying camera now - mStreamState = DEAD; -} - // Methods from ::aidl::android::hardware::automotive::evs::IEvsCamera follow. ScopedAStatus EvsMockCamera::getCameraInfo(CameraDesc* _aidl_return) { LOG(DEBUG) << __FUNCTION__; @@ -128,115 +91,6 @@ ScopedAStatus EvsMockCamera::getCameraInfo(CameraDesc* _aidl_return) { return ScopedAStatus::ok(); } -ScopedAStatus EvsMockCamera::setMaxFramesInFlight(int32_t bufferCount) { - LOG(DEBUG) << __FUNCTION__ << ", bufferCount = " << bufferCount; - ; - - std::lock_guard lock(mAccessLock); - - // If we've been displaced by another owner of the camera, then we can't do anything else - if (mStreamState == DEAD) { - LOG(ERROR) << "Ignoring setMaxFramesInFlight call when camera has been lost."; - return ScopedAStatus::fromServiceSpecificError(static_cast(EvsResult::OWNERSHIP_LOST)); - } - - // We cannot function without at least one video buffer to send data - if (bufferCount < 1) { - LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested."; - return ScopedAStatus::fromServiceSpecificError(static_cast(EvsResult::INVALID_ARG)); - } - - // Update our internal state - if (!setAvailableFrames_Locked(bufferCount)) { - LOG(ERROR) << "Failed to adjust the maximum number of frames in flight."; - return ScopedAStatus::fromServiceSpecificError( - static_cast(EvsResult::BUFFER_NOT_AVAILABLE)); - } - - return ScopedAStatus::ok(); -} - -ScopedAStatus EvsMockCamera::startVideoStream(const std::shared_ptr& cb) { - LOG(DEBUG) << __FUNCTION__; - - if (!cb) { - LOG(ERROR) << "A given stream callback is invalid."; - return ScopedAStatus::fromServiceSpecificError(static_cast(EvsResult::INVALID_ARG)); - } - - std::lock_guard lock(mAccessLock); - - // If we've been displaced by another owner of the camera, then we can't do anything else - if (mStreamState == DEAD) { - LOG(ERROR) << "Ignoring startVideoStream call when camera has been lost."; - return ScopedAStatus::fromServiceSpecificError(static_cast(EvsResult::OWNERSHIP_LOST)); - } - - if (mStreamState != STOPPED) { - LOG(ERROR) << "Ignoring startVideoStream call when a stream is already running."; - return ScopedAStatus::fromServiceSpecificError( - static_cast(EvsResult::STREAM_ALREADY_RUNNING)); - } - - // If the client never indicated otherwise, configure ourselves for a single streaming buffer - if (mFramesAllowed < kMinimumBuffersInFlight && - !setAvailableFrames_Locked(kMinimumBuffersInFlight)) { - LOG(ERROR) << "Failed to start stream because we couldn't get a graphics buffer"; - return ScopedAStatus::fromServiceSpecificError( - static_cast(EvsResult::BUFFER_NOT_AVAILABLE)); - } - - // Record the user's callback for use when we have a frame ready - mStream = cb; - - // Start the frame generation thread - mStreamState = RUNNING; - mCaptureThread = std::thread([this]() { generateFrames(); }); - - return ScopedAStatus::ok(); -} - -ScopedAStatus EvsMockCamera::doneWithFrame(const std::vector& list) { - std::lock_guard lock(mAccessLock); - for (const auto& desc : list) { - returnBufferLocked(desc.bufferId); - } - - return ScopedAStatus::ok(); -} - -ScopedAStatus EvsMockCamera::stopVideoStream() { - LOG(DEBUG) << __FUNCTION__; - return stopVideoStream_impl(); -} - -ScopedAStatus EvsMockCamera::stopVideoStream_impl() { - std::unique_lock lock(mAccessLock); - - if (mStreamState != RUNNING) { - // Safely return here because a stream is not running. - return ScopedAStatus::ok(); - } - - // Tell the GenerateFrames loop we want it to stop - mStreamState = STOPPING; - - // Block outside the mutex until the "stop" flag has been acknowledged - // We won't send any more frames, but the client might still get some already in flight - LOG(DEBUG) << "Waiting for stream thread to end..."; - lock.unlock(); - if (mCaptureThread.joinable()) { - mCaptureThread.join(); - } - lock.lock(); - - mStreamState = STOPPED; - mStream = nullptr; - LOG(DEBUG) << "Stream marked STOPPED."; - - return ScopedAStatus::ok(); -} - ScopedAStatus EvsMockCamera::getExtendedInfo(int32_t opaqueIdentifier, std::vector* opaqueValue) { const auto it = mExtInfo.find(opaqueIdentifier); @@ -264,14 +118,6 @@ ScopedAStatus EvsMockCamera::getPhysicalCameraInfo([[maybe_unused]] const std::s return ScopedAStatus::ok(); } -ScopedAStatus EvsMockCamera::pauseVideoStream() { - return ScopedAStatus::fromServiceSpecificError(static_cast(EvsResult::NOT_SUPPORTED)); -} - -ScopedAStatus EvsMockCamera::resumeVideoStream() { - return ScopedAStatus::fromServiceSpecificError(static_cast(EvsResult::NOT_SUPPORTED)); -} - ScopedAStatus EvsMockCamera::setPrimaryClient() { /* Because EVS HW module reference implementation expects a single client at * a time, this returns a success code always. @@ -346,232 +192,27 @@ ScopedAStatus EvsMockCamera::getIntParameter(CameraParam id, std::vector& buffers, - int32_t* _aidl_return) { - size_t numBuffersToAdd = buffers.size(); - if (numBuffersToAdd < 1) { - LOG(DEBUG) << "Ignoring a request to import external buffers with an empty list."; - return ScopedAStatus::ok(); - } - - std::lock_guard lock(mAccessLock); - if (numBuffersToAdd > (kMaxBuffersInFlight - mFramesAllowed)) { - numBuffersToAdd -= (kMaxBuffersInFlight - mFramesAllowed); - LOG(WARNING) << "Exceed the limit on the number of buffers. " << numBuffersToAdd - << " buffers will be imported only."; - } - - ::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get(); - const size_t before = mFramesAllowed; - for (size_t i = 0; i < numBuffersToAdd; ++i) { - auto& b = buffers[i]; - const AHardwareBuffer_Desc* pDesc = - reinterpret_cast(&b.buffer.description); - - buffer_handle_t handleToImport = ::android::dupFromAidl(b.buffer.handle); - buffer_handle_t handleToStore = nullptr; - if (handleToImport == nullptr) { - LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer " << b.bufferId; - continue; - } - - ::android::status_t result = - mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers, - pDesc->format, pDesc->usage, pDesc->stride, &handleToStore); - if (result != ::android::NO_ERROR || handleToStore == nullptr) { - LOG(WARNING) << "Failed to import a buffer " << b.bufferId; - continue; - } - - bool stored = false; - for (auto&& rec : mBuffers) { - if (rec.handle != nullptr) { - continue; - } - - // Use this existing entry. - rec.handle = handleToStore; - rec.inUse = false; - stored = true; - break; - } - - if (!stored) { - // Add a BufferRecord wrapping this handle to our set of available buffers. - mBuffers.push_back(BufferRecord(handleToStore)); - } - ++mFramesAllowed; - } - - *_aidl_return = mFramesAllowed - before; - return ScopedAStatus::ok(); -} - -bool EvsMockCamera::setAvailableFrames_Locked(unsigned bufferCount) { - if (bufferCount < 1) { - LOG(ERROR) << "Ignoring request to set buffer count to zero"; - return false; - } - if (bufferCount > kMaxBuffersInFlight) { - LOG(ERROR) << "Rejecting buffer request in excess of internal limit"; - return false; - } - - // Is an increase required? - if (mFramesAllowed < bufferCount) { - // An increase is required - auto needed = bufferCount - mFramesAllowed; - LOG(INFO) << "Allocating " << needed << " buffers for camera frames"; - - auto added = increaseAvailableFrames_Locked(needed); - if (added != needed) { - // If we didn't add all the frames we needed, then roll back to the previous state - LOG(ERROR) << "Rolling back to previous frame queue size"; - decreaseAvailableFrames_Locked(added); - return false; - } - } else if (mFramesAllowed > bufferCount) { - // A decrease is required - auto framesToRelease = mFramesAllowed - bufferCount; - LOG(INFO) << "Returning " << framesToRelease << " camera frame buffers"; - - auto released = decreaseAvailableFrames_Locked(framesToRelease); - if (released != framesToRelease) { - // This shouldn't happen with a properly behaving client because the client - // should only make this call after returning sufficient outstanding buffers - // to allow a clean resize. - LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?"; - } - } - - return true; -} - -unsigned EvsMockCamera::increaseAvailableFrames_Locked(unsigned numToAdd) { - // Acquire the graphics buffer allocator - ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get()); - - unsigned added = 0; - while (added < numToAdd) { - unsigned pixelsPerLine = 0; - buffer_handle_t memHandle = nullptr; - auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, &memHandle, - &pixelsPerLine, 0, "EvsMockCamera"); - if (result != ::android::NO_ERROR) { - LOG(ERROR) << "Error " << result << " allocating " << mWidth << " x " << mHeight - << " graphics buffer"; - break; - } - if (memHandle == nullptr) { - LOG(ERROR) << "We didn't get a buffer handle back from the allocator"; - break; - } - if (mStride > 0) { - if (mStride != pixelsPerLine) { - LOG(ERROR) << "We did not expect to get buffers with different strides!"; - } - } else { - // Gralloc defines stride in terms of pixels per line - mStride = pixelsPerLine; - } - - // Find a place to store the new buffer - auto stored = false; - for (auto&& rec : mBuffers) { - if (rec.handle == nullptr) { - // Use this existing entry - rec.handle = memHandle; - rec.inUse = false; - stored = true; - break; - } - } - if (!stored) { - // Add a BufferRecord wrapping this handle to our set of available buffers - mBuffers.push_back(BufferRecord(memHandle)); - } - - ++mFramesAllowed; - ++added; - } - - return added; -} - -unsigned EvsMockCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) { - // Acquire the graphics buffer allocator - ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get()); - - unsigned removed = 0; - for (auto&& rec : mBuffers) { - // Is this record not in use, but holding a buffer that we can free? - if ((rec.inUse == false) && (rec.handle != nullptr)) { - // Release buffer and update the record so we can recognize it as "empty" - alloc.free(rec.handle); - rec.handle = nullptr; - - --mFramesAllowed; - ++removed; - - if (removed == numToRemove) { - break; - } - } - } - - return removed; -} - // This is the asynchronous frame generation thread that runs in parallel with the // main serving thread. There is one for each active camera instance. void EvsMockCamera::generateFrames() { LOG(DEBUG) << "Frame generation loop started."; - unsigned idx = 0; while (true) { - bool timeForFrame = false; const nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); - - // Lock scope for updating shared state + std::size_t bufferId = kInvalidBufferID; + buffer_handle_t bufferHandle = nullptr; { - std::lock_guard lock(mAccessLock); - - if (mStreamState != RUNNING) { - // Break out of our main thread loop + std::lock_guard lock(mMutex); + if (mStreamState != StreamState::RUNNING) { break; } - - // Are we allowed to issue another buffer? - if (mFramesInUse >= mFramesAllowed) { - // Can't do anything right now -- skip this frame - LOG(WARNING) << "Skipped a frame because too many are in flight."; - } else { - // Identify an available buffer to fill - for (idx = 0; idx < mBuffers.size(); idx++) { - if (!mBuffers[idx].inUse) { - if (mBuffers[idx].handle != nullptr) { - // Found an available record, so stop looking - break; - } - } - } - if (idx >= mBuffers.size()) { - // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed - ALOGE("Failed to find an available buffer slot\n"); - } else { - // We're going to make the frame busy - mBuffers[idx].inUse = true; - mFramesInUse++; - timeForFrame = true; - } - } + std::tie(bufferId, bufferHandle) = useBuffer_unsafe(); } - if (timeForFrame) { + if (bufferHandle != nullptr) { using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat; // Assemble the buffer description we'll transmit below - buffer_handle_t memHandle = mBuffers[idx].handle; BufferDesc newBuffer = { .buffer = { @@ -584,39 +225,31 @@ void EvsMockCamera::generateFrames() { .usage = static_cast(mUsage), .stride = static_cast(mStride), }, - .handle = ::android::dupToAidl(memHandle), + .handle = ::android::dupToAidl(bufferHandle), }, - .bufferId = static_cast(idx), + .bufferId = static_cast(bufferId), .deviceId = mDescription.id, .timestamp = static_cast(::android::elapsedRealtimeNano() * 1e+3), // timestamps is in microseconds }; // Write test data into the image buffer - fillMockFrame(memHandle, reinterpret_cast( - &newBuffer.buffer.description)); + fillMockFrame(bufferHandle, reinterpret_cast( + &newBuffer.buffer.description)); - // Issue the (asynchronous) callback to the client -- can't be holding the lock - auto flag = false; - if (mStream) { - std::vector frames; - frames.push_back(std::move(newBuffer)); - flag = mStream->deliverFrame(frames).isOk(); - } + std::vector frames; + frames.push_back(std::move(newBuffer)); - if (flag) { - LOG(DEBUG) << "Delivered " << memHandle << ", id = " << mBuffers[idx].handle; + // Issue the (asynchronous) callback to the client -- can't be holding the lock + if (mStream && mStream->deliverFrame(frames).isOk()) { + LOG(DEBUG) << "Delivered " << bufferHandle << ", id = " << bufferId; } else { // This can happen if the client dies and is likely unrecoverable. // To avoid consuming resources generating failing calls, we stop sending // frames. Note, however, that the stream remains in the "STREAMING" state // until cleaned up on the main thread. LOG(ERROR) << "Frame delivery call failed in the transport layer."; - - // Since we didn't actually deliver it, mark the frame as available - std::lock_guard lock(mAccessLock); - mBuffers[idx].inUse = false; - mFramesInUse--; + doneWithFrame(frames); } } @@ -671,34 +304,45 @@ void EvsMockCamera::fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_ mapper.unlock(handle); } -void EvsMockCamera::returnBufferLocked(const uint32_t bufferId) { - if (bufferId >= mBuffers.size()) { - ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)", bufferId, - mBuffers.size() - 1); - return; - } +::android::status_t EvsMockCamera::allocateOneFrame(buffer_handle_t* handle) { + static auto& alloc = ::android::GraphicBufferAllocator::get(); + unsigned pixelsPerLine = 0; + const auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, handle, &pixelsPerLine, + 0, "EvsMockCamera"); + if (mStride < mWidth) { + // Gralloc defines stride in terms of pixels per line + mStride = pixelsPerLine; + } else if (mStride != pixelsPerLine) { + LOG(ERROR) << "We did not expect to get buffers with different strides!"; + } + return result; +} - if (!mBuffers[bufferId].inUse) { - ALOGE("ignoring doneWithFrame called on frame %d which is already free", bufferId); - return; +bool EvsMockCamera::startVideoStreamImpl_locked( + const std::shared_ptr& receiver, ndk::ScopedAStatus& /* status */, + std::unique_lock& /* lck */) { + mStream = receiver; + mCaptureThread = std::thread([this]() { generateFrames(); }); + return true; +} + +bool EvsMockCamera::stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */, + std::unique_lock& lck) { + lck.unlock(); + if (mCaptureThread.joinable()) { + mCaptureThread.join(); } + lck.lock(); + return true; +} - // Mark the frame as available - mBuffers[bufferId].inUse = false; - mFramesInUse--; - - // If this frame's index is high in the array, try to move it down - // to improve locality after mFramesAllowed has been reduced. - if (bufferId >= mFramesAllowed) { - // Find an empty slot lower in the array (which should always exist in this case) - for (auto&& rec : mBuffers) { - if (rec.handle == nullptr) { - rec.handle = mBuffers[bufferId].handle; - mBuffers[bufferId].handle = nullptr; - break; - } - } +bool EvsMockCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) { + if (!Base::postVideoStreamStop_locked(status, lck)) { + return false; } + mStream = nullptr; + return true; } std::shared_ptr EvsMockCamera::Create(const char* deviceName) { diff --git a/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8181e4733e98e79f594416d4004baedd96fea9d4 --- /dev/null +++ b/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp @@ -0,0 +1,507 @@ +/* + * Copyright (C) 2023 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 "EvsVideoEmulatedCamera.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::automotive::evs::implementation { + +namespace { +struct FormatDeleter { + void operator()(AMediaFormat* format) const { AMediaFormat_delete(format); } +}; +} // namespace + +EvsVideoEmulatedCamera::EvsVideoEmulatedCamera(Sigil, const char* deviceName, + std::unique_ptr& camInfo) + : mVideoFileName(deviceName), mCameraInfo(camInfo) { + mDescription.id = mVideoFileName; + + /* set camera metadata */ + if (camInfo) { + uint8_t* ptr = reinterpret_cast(camInfo->characteristics); + const size_t len = get_camera_metadata_size(camInfo->characteristics); + mDescription.metadata.insert(mDescription.metadata.end(), ptr, ptr + len); + } + + initializeParameters(); +} + +bool EvsVideoEmulatedCamera::initialize() { + // Open file. + mVideoFd = open(mVideoFileName.c_str(), 0, O_RDONLY); + if (mVideoFd < 0) { + PLOG(ERROR) << __func__ << ": Failed to open video file \"" << mVideoFileName << "\"."; + return false; + } + + // Initialize Media Extractor. + { + mVideoExtractor.reset(AMediaExtractor_new()); + off64_t filesize = lseek64(mVideoFd, 0, SEEK_END); + lseek(mVideoFd, 0, SEEK_SET); + const media_status_t status = + AMediaExtractor_setDataSourceFd(mVideoExtractor.get(), mVideoFd, 0, filesize); + if (status != AMEDIA_OK) { + LOG(ERROR) << __func__ + << ": Received error when initializing media extractor. Error code: " + << status << "."; + return false; + } + } + + // Initialize Media Codec and file format. + std::unique_ptr format; + const char* mime; + bool selected = false; + int numTracks = AMediaExtractor_getTrackCount(mVideoExtractor.get()); + for (int i = 0; i < numTracks; i++) { + format.reset(AMediaExtractor_getTrackFormat(mVideoExtractor.get(), i)); + if (!AMediaFormat_getString(format.get(), AMEDIAFORMAT_KEY_MIME, &mime)) { + LOG(ERROR) << __func__ << ": Error in fetching format string"; + continue; + } + if (!::android::base::StartsWith(mime, "video/")) { + continue; + } + const media_status_t status = AMediaExtractor_selectTrack(mVideoExtractor.get(), i); + if (status != AMEDIA_OK) { + LOG(ERROR) << __func__ + << ": Media extractor returned error to select track. Error Code: " << status + << "."; + return false; + } + selected = true; + break; + } + if (!selected) { + LOG(ERROR) << __func__ << ": No video track in video file \"" << mVideoFileName << "\"."; + return false; + } + + mVideoCodec.reset(AMediaCodec_createDecoderByType(mime)); + if (!mVideoCodec) { + LOG(ERROR) << __func__ << ": Unable to create decoder."; + return false; + } + + mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary test value + mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE | + GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY; + mFormat = HAL_PIXEL_FORMAT_YCBCR_420_888; + AMediaFormat_setInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible); + { + const media_status_t status = + AMediaCodec_configure(mVideoCodec.get(), format.get(), nullptr, nullptr, 0); + if (status != AMEDIA_OK) { + LOG(ERROR) << __func__ + << ": Received error in configuring mCodec. Error code: " << status << "."; + return false; + } + } + format.reset(AMediaCodec_getOutputFormat(mVideoCodec.get())); + AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_WIDTH, &mWidth); + AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_HEIGHT, &mHeight); + return true; +} + +void EvsVideoEmulatedCamera::generateFrames() { + while (true) { + { + std::lock_guard lock(mMutex); + if (mStreamState != StreamState::RUNNING) { + return; + } + } + renderOneFrame(); + } +} + +void EvsVideoEmulatedCamera::onCodecInputAvailable(const int32_t index) { + const size_t sampleSize = AMediaExtractor_getSampleSize(mVideoExtractor.get()); + const int64_t presentationTime = AMediaExtractor_getSampleTime(mVideoExtractor.get()); + size_t bufferSize = 0; + uint8_t* const codecInputBuffer = + AMediaCodec_getInputBuffer(mVideoCodec.get(), index, &bufferSize); + if (sampleSize > bufferSize) { + LOG(ERROR) << __func__ << ": Buffer is not large enough."; + } + if (presentationTime < 0) { + AMediaCodec_queueInputBuffer(mVideoCodec.get(), index, /* offset = */ 0, + /* size = */ 0, presentationTime, + AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); + LOG(INFO) << __func__ << ": Reaching the end of stream."; + return; + } + const size_t readSize = + AMediaExtractor_readSampleData(mVideoExtractor.get(), codecInputBuffer, sampleSize); + const media_status_t status = AMediaCodec_queueInputBuffer( + mVideoCodec.get(), index, /*offset = */ 0, readSize, presentationTime, /* flags = */ 0); + if (status != AMEDIA_OK) { + LOG(ERROR) << __func__ + << ": Received error in queueing input buffer. Error code: " << status; + } +} + +void EvsVideoEmulatedCamera::onCodecOutputAvailable(const int32_t index, + const AMediaCodecBufferInfo& info) { + using std::chrono::duration_cast; + using std::chrono::microseconds; + using std::chrono::nanoseconds; + using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat; + using ::aidl::android::hardware::graphics::common::BufferUsage; + + size_t decodedOutSize = 0; + uint8_t* const codecOutputBuffer = + AMediaCodec_getOutputBuffer(mVideoCodec.get(), index, &decodedOutSize) + info.offset; + + std::size_t renderBufferId = static_cast(-1); + buffer_handle_t renderBufferHandle = nullptr; + { + std::lock_guard lock(mMutex); + if (mStreamState != StreamState::RUNNING) { + return; + } + std::tie(renderBufferId, renderBufferHandle) = useBuffer_unsafe(); + } + if (!renderBufferHandle) { + LOG(ERROR) << __func__ << ": Camera failed to get an available render buffer."; + return; + } + std::vector renderBufferDescs; + renderBufferDescs.push_back({ + .buffer = + { + .description = + { + .width = static_cast(mWidth), + .height = static_cast(mHeight), + .layers = 1, + .format = static_cast(mFormat), + .usage = static_cast(mUsage), + .stride = static_cast(mStride), + }, + .handle = ::android::dupToAidl(renderBufferHandle), + }, + .bufferId = static_cast(renderBufferId), + .deviceId = mDescription.id, + .timestamp = duration_cast(nanoseconds(::android::elapsedRealtimeNano())) + .count(), + }); + + // Lock our output buffer for writing + uint8_t* pixels = nullptr; + int32_t bytesPerStride = 0; + auto& mapper = ::android::GraphicBufferMapper::get(); + mapper.lock(renderBufferHandle, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER, + ::android::Rect(mWidth, mHeight), (void**)&pixels, nullptr, &bytesPerStride); + + // If we failed to lock the pixel buffer, we're about to crash, but log it first + if (!pixels) { + LOG(ERROR) << __func__ << ": Camera failed to gain access to image buffer for writing"; + return; + } + + std::size_t ySize = mHeight * mStride; + std::size_t uvSize = ySize / 4; + + std::memcpy(pixels, codecOutputBuffer, ySize); + pixels += ySize; + + uint8_t* u_head = codecOutputBuffer + ySize; + uint8_t* v_head = u_head + uvSize; + + for (size_t i = 0; i < uvSize; ++i) { + *(pixels++) = *(u_head++); + *(pixels++) = *(v_head++); + } + + const auto status = + AMediaCodec_releaseOutputBuffer(mVideoCodec.get(), index, /* render = */ false); + if (status != AMEDIA_OK) { + LOG(ERROR) << __func__ + << ": Received error in releasing output buffer. Error code: " << status; + } + + // Release our output buffer + mapper.unlock(renderBufferHandle); + + // Issue the (asynchronous) callback to the client -- can't be holding the lock + if (mStream && mStream->deliverFrame(renderBufferDescs).isOk()) { + LOG(DEBUG) << __func__ << ": Delivered " << renderBufferHandle + << ", id = " << renderBufferId; + } else { + // This can happen if the client dies and is likely unrecoverable. + // To avoid consuming resources generating failing calls, we stop sending + // frames. Note, however, that the stream remains in the "STREAMING" state + // until cleaned up on the main thread. + LOG(ERROR) << __func__ << ": Frame delivery call failed in the transport layer."; + doneWithFrame(renderBufferDescs); + } +} + +void EvsVideoEmulatedCamera::renderOneFrame() { + using std::chrono::duration_cast; + using std::chrono::microseconds; + using namespace std::chrono_literals; + + // push to codec input + while (true) { + int codecInputBufferIdx = + AMediaCodec_dequeueInputBuffer(mVideoCodec.get(), /* timeoutUs = */ 0); + if (codecInputBufferIdx < 0) { + if (codecInputBufferIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) { + LOG(ERROR) << __func__ + << ": Received error in AMediaCodec_dequeueInputBuffer. Error code: " + << codecInputBufferIdx; + } + break; + } + onCodecInputAvailable(codecInputBufferIdx); + AMediaExtractor_advance(mVideoExtractor.get()); + } + + // pop from codec output + + AMediaCodecBufferInfo info; + int codecOutputputBufferIdx = AMediaCodec_dequeueOutputBuffer( + mVideoCodec.get(), &info, /* timeoutUs = */ duration_cast(1ms).count()); + if (codecOutputputBufferIdx < 0) { + if (codecOutputputBufferIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) { + LOG(ERROR) << __func__ + << ": Received error in AMediaCodec_dequeueOutputBuffer. Error code: " + << codecOutputputBufferIdx; + } + return; + } + onCodecOutputAvailable(codecOutputputBufferIdx, info); +} + +void EvsVideoEmulatedCamera::initializeParameters() { + mParams.emplace( + CameraParam::BRIGHTNESS, + new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255)); + mParams.emplace( + CameraParam::CONTRAST, + new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255)); + mParams.emplace( + CameraParam::SHARPNESS, + new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255)); +} + +::android::status_t EvsVideoEmulatedCamera::allocateOneFrame(buffer_handle_t* handle) { + static auto& alloc = ::android::GraphicBufferAllocator::get(); + unsigned pixelsPerLine = 0; + const auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, handle, &pixelsPerLine, + 0, "EvsVideoEmulatedCamera"); + if (mStride == 0) { + // Gralloc defines stride in terms of pixels per line + mStride = pixelsPerLine; + } else if (mStride != pixelsPerLine) { + LOG(ERROR) << "We did not expect to get buffers with different strides!"; + } + return result; +} + +bool EvsVideoEmulatedCamera::startVideoStreamImpl_locked( + const std::shared_ptr& receiver, ndk::ScopedAStatus& /* status */, + std::unique_lock& /* lck */) { + mStream = receiver; + + const media_status_t status = AMediaCodec_start(mVideoCodec.get()); + if (status != AMEDIA_OK) { + LOG(ERROR) << __func__ << ": Received error in starting decoder. Error code: " << status + << "."; + return false; + } + mCaptureThread = std::thread([this]() { generateFrames(); }); + + return true; +} + +bool EvsVideoEmulatedCamera::stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */, + std::unique_lock& lck) { + const media_status_t status = AMediaCodec_stop(mVideoCodec.get()); + lck.unlock(); + if (mCaptureThread.joinable()) { + mCaptureThread.join(); + } + lck.lock(); + return status == AMEDIA_OK; +} + +bool EvsVideoEmulatedCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) { + if (!Base::postVideoStreamStop_locked(status, lck)) { + return false; + } + mStream = nullptr; + return true; +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::forcePrimaryClient( + const std::shared_ptr& /* display */) { + /* Because EVS HW module reference implementation expects a single client at + * a time, this returns a success code always. + */ + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::getCameraInfo(evs::CameraDesc* _aidl_return) { + *_aidl_return = mDescription; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::getExtendedInfo(int32_t opaqueIdentifier, + std::vector* value) { + const auto it = mExtInfo.find(opaqueIdentifier); + if (it == mExtInfo.end()) { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::INVALID_ARG)); + } else { + *value = mExtInfo[opaqueIdentifier]; + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::getIntParameter(evs::CameraParam id, + std::vector* value) { + const auto it = mParams.find(id); + if (it == mParams.end()) { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::NOT_SUPPORTED)); + } + value->push_back(it->second->value); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::getIntParameterRange(evs::CameraParam id, + evs::ParameterRange* _aidl_return) { + const auto it = mParams.find(id); + if (it == mParams.end()) { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::NOT_SUPPORTED)); + } + _aidl_return->min = it->second->range.min; + _aidl_return->max = it->second->range.max; + _aidl_return->step = it->second->range.step; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::getParameterList( + std::vector* _aidl_return) { + if (mCameraInfo) { + _aidl_return->resize(mCameraInfo->controls.size()); + std::size_t idx = 0; + for (const auto& [name, range] : mCameraInfo->controls) { + (*_aidl_return)[idx++] = name; + } + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::getPhysicalCameraInfo(const std::string& /* deviceId */, + evs::CameraDesc* _aidl_return) { + return getCameraInfo(_aidl_return); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::setExtendedInfo( + int32_t opaqueIdentifier, const std::vector& opaqueValue) { + mExtInfo.insert_or_assign(opaqueIdentifier, opaqueValue); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::setIntParameter(evs::CameraParam id, int32_t value, + std::vector* effectiveValue) { + const auto it = mParams.find(id); + if (it == mParams.end()) { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::NOT_SUPPORTED)); + } + // Rounding down to the closest value. + int32_t candidate = value / it->second->range.step * it->second->range.step; + if (candidate < it->second->range.min || candidate > it->second->range.max) { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(EvsResult::INVALID_ARG)); + } + it->second->value = candidate; + effectiveValue->push_back(candidate); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::setPrimaryClient() { + /* Because EVS HW module reference implementation expects a single client at + * a time, this returns a success code always. + */ + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus EvsVideoEmulatedCamera::unsetPrimaryClient() { + /* Because EVS HW module reference implementation expects a single client at + * a time, there is no chance that this is called by the secondary client and + * therefore returns a success code always. + */ + return ndk::ScopedAStatus::ok(); +} + +std::shared_ptr EvsVideoEmulatedCamera::Create(const char* deviceName) { + std::unique_ptr nullCamInfo = nullptr; + return Create(deviceName, nullCamInfo); +} + +std::shared_ptr EvsVideoEmulatedCamera::Create( + const char* deviceName, std::unique_ptr& camInfo, + const evs::Stream* /* streamCfg */) { + std::shared_ptr c = + ndk::SharedRefBase::make(Sigil{}, deviceName, camInfo); + if (!c) { + LOG(ERROR) << "Failed to instantiate EvsVideoEmulatedCamera."; + return nullptr; + } + if (!c->initialize()) { + LOG(ERROR) << "Failed to initialize EvsVideoEmulatedCamera."; + return nullptr; + } + return c; +} + +void EvsVideoEmulatedCamera::shutdown() { + mVideoCodec.reset(); + mVideoExtractor.reset(); + close(mVideoFd); + mVideoFd = 0; + Base::shutdown(); +} + +} // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/evs/aidl/impl/default/src/GlWrapper.cpp b/automotive/evs/aidl/impl/default/src/GlWrapper.cpp index 0ee5ecb7346528193dd2e5e8b7e7a6b31d5677bb..a9d02138b25ef7987ac54a1eabd222fbb201a7f7 100644 --- a/automotive/evs/aidl/impl/default/src/GlWrapper.cpp +++ b/automotive/evs/aidl/impl/default/src/GlWrapper.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -183,20 +184,6 @@ GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) { return program; } -::android::sp convertNativeHandleToHGBP(const NativeHandle& aidlHandle) { - native_handle_t* handle = ::android::dupFromAidl(aidlHandle); - if (handle->numFds != 0 || handle->numInts < std::ceil(sizeof(size_t) / sizeof(int))) { - LOG(ERROR) << "Invalid native handle"; - return nullptr; - } - ::android::hardware::hidl_vec halToken; - halToken.setToExternal(reinterpret_cast(const_cast(&(handle->data[1]))), - handle->data[0]); - ::android::sp hgbp = - HGraphicBufferProducer::castFrom(::android::retrieveHalInterface(halToken)); - return std::move(hgbp); -} - } // namespace namespace aidl::android::hardware::automotive::evs::implementation { @@ -226,30 +213,19 @@ bool GlWrapper::initialize(const std::shared_ptr& pWindowProxy } LOG(INFO) << "Display resolution is " << mWidth << "x" << mHeight; - NativeHandle aidlHandle; - status = pWindowProxy->getHGraphicBufferProducer(displayId, &aidlHandle); + aidl::android::view::Surface shimSurface; + status = pWindowProxy->getSurface(displayId, &shimSurface); if (!status.isOk()) { - LOG(ERROR) << "Failed to get IGraphicBufferProducer from ICarDisplayProxy."; - return false; - } - - mGfxBufferProducer = convertNativeHandleToHGBP(aidlHandle); - if (!mGfxBufferProducer) { - LOG(ERROR) << "Failed to convert a NativeHandle to HGBP."; + LOG(ERROR) << "Failed to obtain the surface."; return false; } - mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer); - if (mSurfaceHolder == nullptr) { - LOG(ERROR) << "Failed to get a Surface from HGBP."; - return false; - } - - mWindow = getNativeWindow(mSurfaceHolder.get()); + mWindow = shimSurface.get(); if (mWindow == nullptr) { LOG(ERROR) << "Failed to get a native window from Surface."; return false; } + ANativeWindow_acquire(mWindow); // Set up our OpenGL ES context associated with the default display mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); @@ -350,7 +326,12 @@ void GlWrapper::shutdown() { mDisplay = EGL_NO_DISPLAY; // Release the window - mSurfaceHolder = nullptr; + if (mWindow == nullptr) { + return; + } + + ANativeWindow_release(mWindow); + mWindow = nullptr; } void GlWrapper::showWindow(const std::shared_ptr& pWindowProxy, uint64_t id) { diff --git a/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b4676ea8a3743096bb969de66194dda2434ea45 --- /dev/null +++ b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2023 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 "EvsCamera.h" + +#include +#include + +#include +#include +#include + +namespace aidl::android::hardware::automotive::evs::implementation { + +class EvsCameraForTest : public EvsCamera { + public: + using EvsCamera::increaseAvailableFrames_unsafe; + using EvsCamera::returnBuffer_unsafe; + using EvsCamera::useBuffer_unsafe; + + ~EvsCameraForTest() override { shutdown(); } + + ::android::status_t allocateOneFrame(buffer_handle_t* handle) override { + static std::intptr_t handle_cnt = 0; + *handle = reinterpret_cast(++handle_cnt); + return ::android::OK; + } + + void freeOneFrame(const buffer_handle_t /* handle */) override { + // Nothing to free because the handles are fake. + } + + void checkBufferOrder() { + for (std::size_t idx = 0; idx < mBuffers.size(); ++idx) { + const auto& buffer = mBuffers[idx]; + EXPECT_EQ(idx < mFramesInUse, buffer.inUse); + EXPECT_EQ(idx < mAvailableFrames, buffer.handle != nullptr); + EXPECT_LE(mFramesInUse, mAvailableFrames); + } + } + + MOCK_METHOD(::ndk::ScopedAStatus, forcePrimaryClient, + (const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>& + in_display), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getCameraInfo, + (::aidl::android::hardware::automotive::evs::CameraDesc * _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getExtendedInfo, + (int32_t in_opaqueIdentifier, std::vector* _aidl_return), (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getIntParameter, + (::aidl::android::hardware::automotive::evs::CameraParam in_id, + std::vector* _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getIntParameterRange, + (::aidl::android::hardware::automotive::evs::CameraParam in_id, + ::aidl::android::hardware::automotive::evs::ParameterRange* _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getParameterList, + (std::vector<::aidl::android::hardware::automotive::evs::CameraParam> * + _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getPhysicalCameraInfo, + (const std::string& in_deviceId, + ::aidl::android::hardware::automotive::evs::CameraDesc* _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, setExtendedInfo, + (int32_t in_opaqueIdentifier, const std::vector& in_opaqueValue), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, setIntParameter, + (::aidl::android::hardware::automotive::evs::CameraParam in_id, int32_t in_value, + std::vector* _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, setPrimaryClient, (), (override)); + MOCK_METHOD(::ndk::ScopedAStatus, unsetPrimaryClient, (), (override)); + MOCK_METHOD(bool, startVideoStreamImpl_locked, + (const std::shared_ptr& receiver, ndk::ScopedAStatus& status, + std::unique_lock& lck), + (override)); + MOCK_METHOD(bool, stopVideoStreamImpl_locked, + (ndk::ScopedAStatus & status, std::unique_lock& lck), (override)); +}; + +TEST(EvsCameraBufferTest, ChangeBufferPoolSize) { + auto evsCam = ndk::SharedRefBase::make(); + EXPECT_TRUE(evsCam->setMaxFramesInFlight(100).isOk()); + evsCam->checkBufferOrder(); + EXPECT_TRUE(evsCam->setMaxFramesInFlight(50).isOk()); + evsCam->checkBufferOrder(); + + // 2 buffers in use. + const auto [id1, handle1] = evsCam->useBuffer_unsafe(); + const auto [id2, handle2] = evsCam->useBuffer_unsafe(); + std::ignore = evsCam->useBuffer_unsafe(); + + // It allows you to set the buffer pool size to 1, but it will keep the space for the in use + // buffers. + EXPECT_TRUE(evsCam->setMaxFramesInFlight(1).isOk()); + evsCam->checkBufferOrder(); + + evsCam->returnBuffer_unsafe(id1); + evsCam->checkBufferOrder(); + evsCam->returnBuffer_unsafe(id2); + evsCam->checkBufferOrder(); +} + +TEST(EvsCameraBufferTest, UseAndReturn) { + constexpr std::size_t kNumOfHandles = 20; + auto evsCam = ndk::SharedRefBase::make(); + + // Our "fake handles" of this test case is 1 to kNumOfHandles. + for (std::size_t i = 1; i <= kNumOfHandles; ++i) { + evsCam->increaseAvailableFrames_unsafe(reinterpret_cast(i)); + } + evsCam->checkBufferOrder(); + + { + std::vector> inUseIDHandlePairs; + std::unordered_set inUseIDs; + std::unordered_set inUseHandles; + for (std::size_t i = 0; i < kNumOfHandles; ++i) { + const auto [id, handle] = evsCam->useBuffer_unsafe(); + const std::size_t handleInt = reinterpret_cast(handle); + EXPECT_TRUE(EvsCamera::IsBufferIDValid(id)); + EXPECT_NE(handle, nullptr); + EXPECT_LT(id, kNumOfHandles); + + // handleInt must be between [1, kNumOfHandles] as we "allocated" above. + EXPECT_LT(0u, handleInt); + EXPECT_LE(handleInt, kNumOfHandles); + + inUseIDHandlePairs.push_back({id, handleInt}); + EXPECT_TRUE(inUseIDs.insert(id).second); + EXPECT_TRUE(inUseHandles.insert(handleInt).second); + evsCam->checkBufferOrder(); + } + // Return buffers in the order of acquiring. + for (const auto [id, handleInt] : inUseIDHandlePairs) { + evsCam->returnBuffer_unsafe(id); + evsCam->checkBufferOrder(); + } + } + + { + std::vector> inUseIDHandlePairs; + std::unordered_set inUseIDs; + std::unordered_set inUseHandles; + for (std::size_t i = 0; i < kNumOfHandles; ++i) { + const auto [id, handle] = evsCam->useBuffer_unsafe(); + const std::size_t handleInt = reinterpret_cast(handle); + EXPECT_TRUE(EvsCamera::IsBufferIDValid(id)); + EXPECT_NE(handle, nullptr); + EXPECT_LT(id, kNumOfHandles); + + // handleInt must be between [1, kNumOfHandles] as we "allocated" above. + EXPECT_LT(0u, handleInt); + EXPECT_LE(handleInt, kNumOfHandles); + + inUseIDHandlePairs.push_back({id, handleInt}); + EXPECT_TRUE(inUseIDs.insert(id).second); + EXPECT_TRUE(inUseHandles.insert(handleInt).second); + evsCam->checkBufferOrder(); + } + // Return buffers in the reverse order of acquiring. + std::reverse(inUseIDHandlePairs.begin(), inUseIDHandlePairs.end()); + for (const auto [id, handleInt] : inUseIDHandlePairs) { + evsCam->returnBuffer_unsafe(id); + evsCam->checkBufferOrder(); + } + } + + { + // Making sure the handles are still in [1, kNumOfHandles] and IDs are still [0, + // kNumOfHandles). The mapping may be different, though. + std::vector> inUseIDHandlePairs; + std::unordered_set inUseIDs; + std::unordered_set inUseHandles; + for (std::size_t i = 0; i < kNumOfHandles; ++i) { + const auto [id, handle] = evsCam->useBuffer_unsafe(); + const std::size_t handleInt = reinterpret_cast(handle); + EXPECT_TRUE(EvsCamera::IsBufferIDValid(id)); + EXPECT_NE(handle, nullptr); + EXPECT_LT(id, kNumOfHandles); + + // handleInt must be between [1, kNumOfHandles] as we "allocated" above. + EXPECT_LT(0u, handleInt); + EXPECT_LE(handleInt, kNumOfHandles); + + inUseIDHandlePairs.push_back({id, handleInt}); + EXPECT_TRUE(inUseIDs.insert(id).second); + EXPECT_TRUE(inUseHandles.insert(handleInt).second); + evsCam->checkBufferOrder(); + } + } +} + +} // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp b/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1925c793f7837648e36bbc81e66f9e10e0d982fb --- /dev/null +++ b/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2023 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 "EvsCamera.h" + +#include +#include + +#include +#include +#include + +namespace aidl::android::hardware::automotive::evs::implementation { + +class EvsCameraForTest : public EvsCamera { + private: + using Base = EvsCamera; + + public: + using EvsCamera::mStreamState; + using EvsCamera::shutdown; + using EvsCamera::StreamState; + + ~EvsCameraForTest() override { shutdown(); } + + ::android::status_t allocateOneFrame(buffer_handle_t* handle) override { + static std::intptr_t handle_cnt = 0; + *handle = reinterpret_cast(++handle_cnt); + return ::android::OK; + } + + void freeOneFrame(const buffer_handle_t /* handle */) override { + // Nothing to free because the handles are fake. + } + + bool preVideoStreamStart_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck) override { + mPreStartCalled = true; + EXPECT_EQ(mStreamState, StreamState::STOPPED); + EXPECT_FALSE(mStreamStarted); + EXPECT_FALSE(mStreamStopped); + return Base::preVideoStreamStart_locked(receiver, status, lck); + } + + bool startVideoStreamImpl_locked(const std::shared_ptr& /* receiver */, + ndk::ScopedAStatus& /* status */, + std::unique_lock& /* lck */) override { + EXPECT_EQ(mStreamState, StreamState::RUNNING); + EXPECT_FALSE(mStreamStarted); + EXPECT_FALSE(mStreamStopped); + mStreamStarted = true; + return true; + } + + bool postVideoStreamStart_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck) override { + mPostStartCalled = true; + EXPECT_EQ(mStreamState, StreamState::RUNNING); + EXPECT_TRUE(mStreamStarted); + EXPECT_FALSE(mStreamStopped); + return Base::postVideoStreamStart_locked(receiver, status, lck); + } + + bool preVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) override { + // Skip the check if stop was called before. + if (!mPreStopCalled) { + mPreStopCalled = true; + EXPECT_EQ(mStreamState, StreamState::RUNNING); + EXPECT_TRUE(mStreamStarted); + EXPECT_FALSE(mStreamStopped); + } + return Base::preVideoStreamStop_locked(status, lck); + } + + bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */, + std::unique_lock& /* lck */) override { + EXPECT_EQ(mStreamState, StreamState::STOPPING); + EXPECT_TRUE(mStreamStarted); + EXPECT_FALSE(mStreamStopped); + mStreamStopped = true; + return true; + } + + bool postVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) override { + mPostStopCalled = true; + const auto ret = Base::postVideoStreamStop_locked(status, lck); + EXPECT_EQ(mStreamState, StreamState::STOPPED); + EXPECT_TRUE(mStreamStarted); + EXPECT_TRUE(mStreamStopped); + return ret; + } + + MOCK_METHOD(::ndk::ScopedAStatus, forcePrimaryClient, + (const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>& + in_display), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getCameraInfo, + (::aidl::android::hardware::automotive::evs::CameraDesc * _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getExtendedInfo, + (int32_t in_opaqueIdentifier, std::vector* _aidl_return), (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getIntParameter, + (::aidl::android::hardware::automotive::evs::CameraParam in_id, + std::vector* _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getIntParameterRange, + (::aidl::android::hardware::automotive::evs::CameraParam in_id, + ::aidl::android::hardware::automotive::evs::ParameterRange* _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getParameterList, + (std::vector<::aidl::android::hardware::automotive::evs::CameraParam> * + _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getPhysicalCameraInfo, + (const std::string& in_deviceId, + ::aidl::android::hardware::automotive::evs::CameraDesc* _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, setExtendedInfo, + (int32_t in_opaqueIdentifier, const std::vector& in_opaqueValue), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, setIntParameter, + (::aidl::android::hardware::automotive::evs::CameraParam in_id, int32_t in_value, + std::vector* _aidl_return), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, setPrimaryClient, (), (override)); + MOCK_METHOD(::ndk::ScopedAStatus, unsetPrimaryClient, (), (override)); + + bool mStreamStarted = false; + bool mStreamStopped = false; + bool mPreStartCalled = false; + bool mPostStartCalled = false; + bool mPreStopCalled = false; + bool mPostStopCalled = false; +}; + +class MockEvsCameraStream : public evs::IEvsCameraStream { + MOCK_METHOD(::ndk::SpAIBinder, asBinder, (), (override)); + MOCK_METHOD(bool, isRemote, (), (override)); + MOCK_METHOD( + ::ndk::ScopedAStatus, deliverFrame, + (const std::vector<::aidl::android::hardware::automotive::evs::BufferDesc>& in_buffer), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, notify, + (const ::aidl::android::hardware::automotive::evs::EvsEventDesc& in_event), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceVersion, (int32_t * _aidl_return), (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceHash, (std::string * _aidl_return), (override)); +}; + +using StreamState = EvsCameraForTest::StreamState; + +TEST(EvsCameraStateTest, StateChangeHooks) { + auto evsCam = ndk::SharedRefBase::make(); + auto mockStream = ndk::SharedRefBase::make(); + EXPECT_FALSE(evsCam->mPreStartCalled); + EXPECT_FALSE(evsCam->mPostStartCalled); + EXPECT_FALSE(evsCam->mPreStopCalled); + EXPECT_FALSE(evsCam->mPostStopCalled); + EXPECT_FALSE(evsCam->mStreamStarted); + EXPECT_FALSE(evsCam->mStreamStopped); + EXPECT_EQ(evsCam->mStreamState, StreamState::STOPPED); + evsCam->startVideoStream(mockStream); + + EXPECT_TRUE(evsCam->mPreStartCalled); + EXPECT_TRUE(evsCam->mPostStartCalled); + EXPECT_FALSE(evsCam->mPreStopCalled); + EXPECT_FALSE(evsCam->mPostStopCalled); + EXPECT_TRUE(evsCam->mStreamStarted); + EXPECT_FALSE(evsCam->mStreamStopped); + EXPECT_EQ(evsCam->mStreamState, StreamState::RUNNING); + evsCam->stopVideoStream(); + + EXPECT_TRUE(evsCam->mPreStartCalled); + EXPECT_TRUE(evsCam->mPostStartCalled); + EXPECT_TRUE(evsCam->mPreStopCalled); + EXPECT_TRUE(evsCam->mPostStopCalled); + EXPECT_TRUE(evsCam->mStreamStarted); + EXPECT_TRUE(evsCam->mStreamStopped); + EXPECT_EQ(evsCam->mStreamState, StreamState::STOPPED); + + evsCam->shutdown(); + EXPECT_EQ(evsCam->mStreamState, StreamState::DEAD); +} + +} // namespace aidl::android::hardware::automotive::evs::implementation diff --git a/automotive/remoteaccess/Android.bp b/automotive/remoteaccess/Android.bp index 7cd6f60fe9a722a1a85393b7f9377480bffe94ca..e1e90414c6f1e25b4ecf14cbe189645ee1650da3 100644 --- a/automotive/remoteaccess/Android.bp +++ b/automotive/remoteaccess/Android.bp @@ -42,6 +42,5 @@ aidl_interface { imports: [], }, ], - frozen: true, - + frozen: false, } diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl index b0935c2fb23bb4f708d125e39ee8020e927fab80..ccfa22de4ea9cecb5155dab7df153a11e849c112 100644 --- a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl +++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl @@ -40,4 +40,10 @@ interface IRemoteAccess { void setRemoteTaskCallback(android.hardware.automotive.remoteaccess.IRemoteTaskCallback callback); void clearRemoteTaskCallback(); void notifyApStateChange(in android.hardware.automotive.remoteaccess.ApState state); + boolean isTaskScheduleSupported(); + void scheduleTask(in android.hardware.automotive.remoteaccess.ScheduleInfo scheduleInfo); + void unscheduleTask(String clientId, String scheduleId); + void unscheduleAllTasks(String clientId); + boolean isTaskScheduled(String clientId, String scheduleId); + List getAllScheduledTasks(String clientId); } diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ScheduleInfo.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ScheduleInfo.aidl new file mode 100644 index 0000000000000000000000000000000000000000..a929e10827bab7fd44d83cbe06736ef281cae648 --- /dev/null +++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ScheduleInfo.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.remoteaccess; +@JavaDerive(equals=true, toString=true) @VintfStability +parcelable ScheduleInfo { + String clientId; + String scheduleId; + byte[] taskData; + int count; + long startTimeInEpochSeconds; + long periodicInSeconds; +} diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl index 0f4125f4751ea4ff4aca6f71e244e129de98554c..4912651294c9535d752d2b669134f89a064615ef 100644 --- a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl +++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl @@ -18,6 +18,7 @@ package android.hardware.automotive.remoteaccess; import android.hardware.automotive.remoteaccess.ApState; import android.hardware.automotive.remoteaccess.IRemoteTaskCallback; +import android.hardware.automotive.remoteaccess.ScheduleInfo; /** * Interface representing a remote wakeup client. @@ -96,4 +97,69 @@ interface IRemoteAccess { *

If {@code isWakeupRequired} is false, it must not try to wake up AP. */ void notifyApStateChange(in ApState state); + + /** + * Returns whether task scheduling is supported. + * + *

If this returns {@code true}, user may use {@link scheduleTask} to schedule a task to be + * executed at a later time. If the device is off when the task is scheduled to be executed, + * the device will be woken up to execute the task. + * + * @return {@code true} if serverless remote task scheduling is supported. + */ + boolean isTaskScheduleSupported(); + + /** + * Schedules a task to be executed later even when the vehicle is off. + * + *

If {@link isTaskScheduleSupported} returns {@code false}. This is no-op. + * + *

This sends a scheduled task message to a device external to Android so that the device + * can wake up Android and deliver the task through {@link IRemoteTaskCallback}. + * + *

Note that the scheduled task execution is on a best-effort basis. Multiple situations + * might cause the task not to execute successfully: + * + *