diff options
author | Haamed Gheibi <haamed@google.com> | 2022-02-09 14:35:06 -0800 |
---|---|---|
committer | Haamed Gheibi <haamed@google.com> | 2022-02-09 14:41:16 -0800 |
commit | ab52181d73b04e131fd72e32d69b5123a5d6892b (patch) | |
tree | 0ac86b537180b6fb97716b3058dfae44af9eaac7 | |
parent | f99b35c293439db0b7436b47b939eb8c7bf21b51 (diff) | |
parent | 4d2548cfa7b86b79a516be9b60f6b666cc9af682 (diff) |
Merge TP1A.220126.001
Change-Id: Ibf6bd2c20d9927fde8b2a05dde2b58bd8faea20f
423 files changed, 22841 insertions, 1893 deletions
diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml index f035bafa29..ae571258dc 100644 --- a/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml +++ b/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml @@ -34,5 +34,6 @@ <test class="com.android.tradefed.testtype.GTest" > <option name="native-test-device-path" value="/data/local/tmp" /> <option name="module-name" value="VtsHalAudioV6_0TargetTest" /> + <option name="native-test-timeout" value="5m" /> </test> </configuration> diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml index 6635f3194a..55dbaf1e78 100644 --- a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml +++ b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml @@ -34,5 +34,6 @@ <test class="com.android.tradefed.testtype.GTest" > <option name="native-test-device-path" value="/data/local/tmp" /> <option name="module-name" value="VtsHalAudioV7_0TargetTest" /> + <option name="native-test-timeout" value="5m" /> </test> </configuration> diff --git a/automotive/evs/aidl/Android.bp b/automotive/evs/aidl/Android.bp new file mode 100644 index 0000000000..3c0aa13f49 --- /dev/null +++ b/automotive/evs/aidl/Android.bp @@ -0,0 +1,51 @@ +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // 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"], +} + +aidl_interface { + name: "android.hardware.automotive.evs", + vendor_available: true, + srcs: [ + "android/hardware/automotive/evs/*.aidl" + ], + stability: "vintf", + imports: [ + "android.hardware.common-V2", + "android.hardware.graphics.common-V3", + ], + backend: { + java: { + // android.hardware.graphics.common package is not enabled + // for Java backend. + enabled: false, + }, + cpp: { + enabled: false, + }, + ndk: { + vndk: { + enabled: false, + }, + min_sdk_version: "29" + }, + }, +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/BufferDesc.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/BufferDesc.aidl new file mode 100644 index 0000000000..31acdb8e27 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/BufferDesc.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable BufferDesc { + android.hardware.graphics.common.HardwareBuffer buffer; + int pixelSizeBytes; + int bufferId; + @utf8InCpp String deviceId; + long timestamp; + byte[] metadata; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/CameraDesc.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/CameraDesc.aidl new file mode 100644 index 0000000000..4dadeb88b2 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/CameraDesc.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable CameraDesc { + @utf8InCpp String id; + int vendorFlags; + byte[] metadata; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/CameraParam.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/CameraParam.aidl new file mode 100644 index 0000000000..ae4ce77192 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/CameraParam.aidl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@Backing(type="int") @VintfStability +enum CameraParam { + BRIGHTNESS = 0, + CONTRAST = 1, + AUTOGAIN = 2, + GAIN = 3, + AUTO_WHITE_BALANCE = 4, + WHITE_BALANCE_TEMPERATURE = 5, + SHARPNESS = 6, + AUTO_EXPOSURE = 7, + ABSOLUTE_EXPOSURE = 8, + ABSOLUTE_FOCUS = 9, + AUTO_FOCUS = 10, + ABSOLUTE_ZOOM = 11, +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DeviceStatus.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DeviceStatus.aidl new file mode 100644 index 0000000000..cc066ac6db --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DeviceStatus.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable DeviceStatus { + @utf8InCpp String id; + android.hardware.automotive.evs.DeviceStatusType status; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DeviceStatusType.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DeviceStatusType.aidl new file mode 100644 index 0000000000..d0f1d8e1d6 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DeviceStatusType.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@Backing(type="int") @VintfStability +enum DeviceStatusType { + CAMERA_AVAILABLE = 0, + CAMERA_NOT_AVAILABLE = 1, + DISPLAY_AVAILABLE = 2, + DISPLAY_NOT_AVAILABLE = 3, +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DisplayDesc.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DisplayDesc.aidl new file mode 100644 index 0000000000..4ac029e479 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DisplayDesc.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable DisplayDesc { + @utf8InCpp String id; + int width; + int height; + android.hardware.automotive.evs.Rotation orientation; + int vendorFlags; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DisplayState.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DisplayState.aidl new file mode 100644 index 0000000000..a5f43095c8 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/DisplayState.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@Backing(type="int") @VintfStability +enum DisplayState { + NOT_OPEN = 0, + NOT_VISIBLE = 1, + VISIBLE_ON_NEXT_FRAME = 2, + VISIBLE = 3, + DEAD = 4, +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsEventDesc.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsEventDesc.aidl new file mode 100644 index 0000000000..09b2b9db13 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsEventDesc.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable EvsEventDesc { + android.hardware.automotive.evs.EvsEventType aType; + @utf8InCpp String deviceId; + int[] payload; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsEventType.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsEventType.aidl new file mode 100644 index 0000000000..052a6b3969 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsEventType.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@Backing(type="int") @VintfStability +enum EvsEventType { + STREAM_STARTED = 0, + STREAM_STOPPED = 1, + FRAME_DROPPED = 2, + TIMEOUT = 3, + PARAMETER_CHANGED = 4, + MASTER_RELEASED = 5, + STREAM_ERROR = 6, +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsResult.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsResult.aidl new file mode 100644 index 0000000000..a0418a94ef --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/EvsResult.aidl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@Backing(type="int") @VintfStability +enum EvsResult { + OK = 0, + INVALID_ARG = 1, + STREAM_ALREADY_RUNNING = 2, + BUFFER_NOT_AVAILABLE = 3, + OWNERSHIP_LOST = 4, + UNDERLYING_SERVICE_ERROR = 5, + PERMISSION_DENIED = 6, + RESOURCE_NOT_AVAILABLE = 7, + RESOURCE_BUSY = 8, + NOT_IMPLEMENTED = 9, + NOT_SUPPORTED = 10, +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsCamera.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsCamera.aidl new file mode 100644 index 0000000000..ce1b97d16c --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsCamera.aidl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +interface IEvsCamera { + void doneWithFrame(in android.hardware.automotive.evs.BufferDesc[] buffer); + void forcePrimaryClient(in android.hardware.automotive.evs.IEvsDisplay display); + android.hardware.automotive.evs.CameraDesc getCameraInfo(); + byte[] getExtendedInfo(in int opaqueIdentifier); + int[] getIntParameter(in android.hardware.automotive.evs.CameraParam id); + android.hardware.automotive.evs.ParameterRange getIntParameterRange(in android.hardware.automotive.evs.CameraParam id); + android.hardware.automotive.evs.CameraParam[] getParameterList(); + android.hardware.automotive.evs.CameraDesc getPhysicalCameraInfo(in String deviceId); + int importExternalBuffers(in android.hardware.automotive.evs.BufferDesc[] buffers); + void pauseVideoStream(); + void resumeVideoStream(); + void setExtendedInfo(in int opaqueIdentifier, in byte[] opaqueValue); + int[] setIntParameter(in android.hardware.automotive.evs.CameraParam id, in int value); + void setPrimaryClient(); + void setMaxFramesInFlight(in int bufferCount); + void startVideoStream(in android.hardware.automotive.evs.IEvsCameraStream receiver); + void stopVideoStream(); + void unsetPrimaryClient(); +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsCameraStream.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsCameraStream.aidl new file mode 100644 index 0000000000..6e2e64a3c4 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsCameraStream.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +interface IEvsCameraStream { + oneway void deliverFrame(in android.hardware.automotive.evs.BufferDesc[] buffer); + oneway void notify(in android.hardware.automotive.evs.EvsEventDesc event); +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsDisplay.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsDisplay.aidl new file mode 100644 index 0000000000..9b538d43a2 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsDisplay.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +interface IEvsDisplay { + android.hardware.automotive.evs.DisplayDesc getDisplayInfo(); + android.hardware.automotive.evs.DisplayState getDisplayState(); + android.hardware.automotive.evs.BufferDesc getTargetBuffer(); + void returnTargetBufferForDisplay(in android.hardware.automotive.evs.BufferDesc buffer); + void setDisplayState(in android.hardware.automotive.evs.DisplayState state); +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsEnumerator.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsEnumerator.aidl new file mode 100644 index 0000000000..a79c68d898 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsEnumerator.aidl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +interface IEvsEnumerator { + void closeCamera(in android.hardware.automotive.evs.IEvsCamera carCamera); + void closeDisplay(in android.hardware.automotive.evs.IEvsDisplay display); + void closeUltrasonicsArray(in android.hardware.automotive.evs.IEvsUltrasonicsArray evsUltrasonicsArray); + android.hardware.automotive.evs.CameraDesc[] getCameraList(); + byte[] getDisplayIdList(); + android.hardware.automotive.evs.DisplayState getDisplayState(); + android.hardware.automotive.evs.Stream[] getStreamList(in android.hardware.automotive.evs.CameraDesc description); + android.hardware.automotive.evs.UltrasonicsArrayDesc[] getUltrasonicsArrayList(); + boolean isHardware(); + android.hardware.automotive.evs.IEvsCamera openCamera(in String cameraId, in android.hardware.automotive.evs.Stream streamCfg); + android.hardware.automotive.evs.IEvsDisplay openDisplay(in byte id); + android.hardware.automotive.evs.IEvsUltrasonicsArray openUltrasonicsArray(in String ultrasonicsArrayId); + void registerStatusCallback(in android.hardware.automotive.evs.IEvsEnumeratorStatusCallback callback); +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.aidl new file mode 100644 index 0000000000..c39a4e855e --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +interface IEvsEnumeratorStatusCallback { + oneway void deviceStatusChanged(in android.hardware.automotive.evs.DeviceStatus[] status); +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsUltrasonicsArray.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsUltrasonicsArray.aidl new file mode 100644 index 0000000000..1183ab392c --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsUltrasonicsArray.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +interface IEvsUltrasonicsArray { + void doneWithDataFrame(in android.hardware.automotive.evs.UltrasonicsDataFrameDesc dataFrameDesc); + android.hardware.automotive.evs.UltrasonicsArrayDesc getUltrasonicArrayInfo(); + void setMaxFramesInFlight(in int bufferCount); + void startStream(in android.hardware.automotive.evs.IEvsUltrasonicsArrayStream stream); + void stopStream(); +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsUltrasonicsArrayStream.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsUltrasonicsArrayStream.aidl new file mode 100644 index 0000000000..510b0a4692 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/IEvsUltrasonicsArrayStream.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +interface IEvsUltrasonicsArrayStream { + oneway void deliverDataFrame(in android.hardware.automotive.evs.UltrasonicsDataFrameDesc dataFrameDesc); + oneway void notify(in android.hardware.automotive.evs.EvsEventDesc event); +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/ParameterRange.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/ParameterRange.aidl new file mode 100644 index 0000000000..44e9b59276 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/ParameterRange.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable ParameterRange { + int min; + int max; + int step; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Rotation.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Rotation.aidl new file mode 100644 index 0000000000..91971fc5e7 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Rotation.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@Backing(type="int") @VintfStability +enum Rotation { + ROTATION_0 = 0, + ROTATION_90 = 1, + ROTATION_180 = 2, + ROTATION_270 = 3, +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/RotationQuaternion.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/RotationQuaternion.aidl new file mode 100644 index 0000000000..d9c8b6e247 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/RotationQuaternion.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable RotationQuaternion { + float x; + float y; + float z; + float w; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/SensorPose.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/SensorPose.aidl new file mode 100644 index 0000000000..4ead9eaf2e --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/SensorPose.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable SensorPose { + android.hardware.automotive.evs.RotationQuaternion rotation; + android.hardware.automotive.evs.Translation translation; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Stream.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Stream.aidl new file mode 100644 index 0000000000..a7804121b3 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Stream.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable Stream { + int id; + android.hardware.automotive.evs.StreamType streamType; + int width; + int height; + android.hardware.graphics.common.PixelFormat format; + android.hardware.graphics.common.BufferUsage usage; + android.hardware.automotive.evs.Rotation rotation; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/StreamType.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/StreamType.aidl new file mode 100644 index 0000000000..9819c8910b --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/StreamType.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@Backing(type="int") @VintfStability +enum StreamType { + OUTPUT = 0, + INPUT = 1, +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Translation.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Translation.aidl new file mode 100644 index 0000000000..488d80f479 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/Translation.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable Translation { + float x; + float y; + float z; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicSensor.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicSensor.aidl new file mode 100644 index 0000000000..23f81f8093 --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicSensor.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable UltrasonicSensor { + android.hardware.automotive.evs.SensorPose pose; + float maxRangeMm; + float angleOfMeasurement; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicsArrayDesc.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicsArrayDesc.aidl new file mode 100644 index 0000000000..4a988759fd --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicsArrayDesc.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable UltrasonicsArrayDesc { + @utf8InCpp String ultrasonicsArrayId; + int maxReadingsPerSensorCount; + int maxReceiversCount; + android.hardware.automotive.evs.UltrasonicSensor[] sensors; +} diff --git a/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicsDataFrameDesc.aidl b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicsDataFrameDesc.aidl new file mode 100644 index 0000000000..35ec84bdcc --- /dev/null +++ b/automotive/evs/aidl/aidl_api/android.hardware.automotive.evs/current/android/hardware/automotive/evs/UltrasonicsDataFrameDesc.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.evs; +@VintfStability +parcelable UltrasonicsDataFrameDesc { + long timestampNs; + int id; + byte[] transmittersIdList; + byte[] receiversIdList; + int[] receiversReadingsCountList; + android.hardware.common.Ashmem waveformsData; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/BufferDesc.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/BufferDesc.aidl new file mode 100644 index 0000000000..0604abe1dc --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/BufferDesc.aidl @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.graphics.common.HardwareBuffer; + +/** + * Structure representing an image buffer through our APIs + * + * In addition to the handle to the graphics memory, we need to retain + * the properties of the buffer for easy reference and reconstruction of + * an ANativeWindowBuffer object on the remote side of API calls. + * (Not least because OpenGL expect an ANativeWindowBuffer* for us as a + * texture via eglCreateImageKHR()). + */ +@VintfStability +parcelable BufferDesc { + /** + * Stable AIDL counter part of AHardwareBuffer. Please see + * hardware/interfaces/graphics/common/aidl/android/hardware/graphics/common/HardwareBuffer.aidl + * for more details. + */ + HardwareBuffer buffer; + /** + * The size of a pixel in the units of bytes. + */ + int pixelSizeBytes; + /** + * Opaque value from driver + */ + int bufferId; + /** + * Unique identifier of the physical camera device that produces this buffer. + */ + @utf8InCpp + String deviceId; + /** + * Time that this buffer is being filled in the units of microseconds and must be + * obtained from android::elapsedRealtimeNanos() or its equivalents. + */ + long timestamp; + /** + * Frame metadata. This is opaque to EvsManager. + */ + byte[] metadata; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/CameraDesc.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/CameraDesc.aidl new file mode 100644 index 0000000000..2f500a73c5 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/CameraDesc.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * Structure describing the basic properties of an EVS camera. + * + * The HAL is responsible for filling out this structure for each + * EVS camera in the system. + */ +@VintfStability +parcelable CameraDesc { + /** + * Unique identifier for camera devices. This may be a path to detected + * camera device; for example, "/dev/video0". + */ + @utf8InCpp + String id; + /** + * Opaque value from driver. Vendor may use this field to store additional + * information; for example, sensor and bridge chip id. + */ + int vendorFlags; + /** + * Store camera metadata such as lens characteristics. + */ + byte[] metadata; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/CameraParam.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/CameraParam.aidl new file mode 100644 index 0000000000..15500b2def --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/CameraParam.aidl @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * EVS camera parameter + */ +@VintfStability +@Backing(type="int") +enum CameraParam { + /** + * The brightness of image frames + */ + BRIGHTNESS, + /** + * The contrast of image frames + */ + CONTRAST, + /** + * Automatic gain/exposure control + */ + AUTOGAIN, + /** + * Gain control + */ + GAIN, + /** + * Automatic Whitebalance + */ + AUTO_WHITE_BALANCE, + /** + * Manual white balance setting as a color temperature in Kelvin. + */ + WHITE_BALANCE_TEMPERATURE, + /** + * Image sharpness adjustment + */ + SHARPNESS, + /** + * Auto Exposure Control modes; auto, manual, shutter priority, or + * aperture priority. + */ + AUTO_EXPOSURE, + /** + * Manual exposure time of the camera + */ + ABSOLUTE_EXPOSURE, + /** + * Sets the focal point of the camera to the specified position. This + * parameter may not be effective when auto focus is enabled. + */ + ABSOLUTE_FOCUS, + /** + * Enables continuous automatic focus adjustments. + */ + AUTO_FOCUS, + /** + * Specifies the objective lens focal length as an absolute value. + */ + ABSOLUTE_ZOOM, +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/DeviceStatus.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/DeviceStatus.aidl new file mode 100644 index 0000000000..535ace30ac --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/DeviceStatus.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.DeviceStatusType; + +/** + * The status of the devices, as sent by EVS HAL through the + * IEvsEnumeratorCallback::deviceStatusChanged() call. + */ +@VintfStability +parcelable DeviceStatus { + /** + * The identifier of a device that has transitioned to a new status. + */ + @utf8InCpp + String id; + /** + * A new status of this device + */ + DeviceStatusType status; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/DeviceStatusType.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/DeviceStatusType.aidl new file mode 100644 index 0000000000..902b31b15e --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/DeviceStatusType.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * The status of the devices available through the EVS + */ +@VintfStability +@Backing(type="int") +enum DeviceStatusType { + /** + * A camera device is available and ready to be used. + */ + CAMERA_AVAILABLE, + /** + * A camera device is not available; e.g. disconnected from the system. + */ + CAMERA_NOT_AVAILABLE, + /** + * A display device is available and ready to be used. + */ + DISPLAY_AVAILABLE, + /** + * A display device is not available; e.g. disconnected from the + * system. + */ + DISPLAY_NOT_AVAILABLE, +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/DisplayDesc.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/DisplayDesc.aidl new file mode 100644 index 0000000000..0b4243b8d9 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/DisplayDesc.aidl @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.Rotation; + +/** + * Structure describing the basic properties of an EVS display + * + * The HAL is responsible for filling out this structure to describe + * the EVS display. As an implementation detail, this may be a physical + * display or a virtual display that is overlaid or mixed with another + * presentation device. + */ +@VintfStability +parcelable DisplayDesc { + /** + * Unique identifier for the display + */ + @utf8InCpp + String id; + /** + * The width of the display + */ + int width; + /** + * The height of the display + */ + int height; + /** + * Counterclock-wise orientation of the display + */ + Rotation orientation; + /** + * Opaque value from driver. Vendor may use this field to store additional + * information; for example, sensor and bridge chip id. + */ + int vendorFlags; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/DisplayState.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/DisplayState.aidl new file mode 100644 index 0000000000..c242d2fb7d --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/DisplayState.aidl @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * States for control of the EVS display + * + * The DisplayInfo structure describes the basic properties of an EVS display. Any EVS + * implementation is required to have one. The HAL is responsible for filling out this + * structure to describe the EVS display. As an implementation detail, this may be a + * physical display or a virtual display that is overlaid or mixed with another + * presentation device. + */ +@VintfStability +@Backing(type="int") +enum DisplayState { + /* + * Display has not been requested by any application yet + */ + NOT_OPEN = 0, + /* + * Display is inhibited + */ + NOT_VISIBLE, + /* + * Will become visible with next frame + */ + VISIBLE_ON_NEXT_FRAME, + /* + * Display is currently active + */ + VISIBLE, + /* + * Driver is in an undefined state. Interface should be closed. + */ + DEAD, +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventDesc.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventDesc.aidl new file mode 100644 index 0000000000..ebff98f077 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventDesc.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.EvsEventType; + +/** + * Structure that describes informative events occurred during EVS is streaming + */ +@VintfStability +parcelable EvsEventDesc { + /** + * Type of an informative event + */ + EvsEventType aType; + /** + * Device identifier + */ + @utf8InCpp + String deviceId; + /** + * Possible additional vendor information that is opaque to the EvsManager + */ + int[] payload; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventType.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventType.aidl new file mode 100644 index 0000000000..3a493af61b --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventType.aidl @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * Types of informative streaming events + */ +@VintfStability +@Backing(type="int") +enum EvsEventType { + /** + * Video stream is started + */ + STREAM_STARTED = 0, + /** + * Video stream is stopped + */ + STREAM_STOPPED, + /** + * Video frame is dropped + */ + FRAME_DROPPED, + /** + * Timeout happens + */ + TIMEOUT, + /** + * Camera parameter is changed; payload contains a changed parameter ID and + * its value + */ + PARAMETER_CHANGED, + /** + * Master role has become available + */ + MASTER_RELEASED, + /** + * Any other erroneous streaming events + */ + STREAM_ERROR, +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/EvsResult.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/EvsResult.aidl new file mode 100644 index 0000000000..c355be35c2 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/EvsResult.aidl @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * Error codes used in EVS HAL interface. + */ +@VintfStability +@Backing(type="int") +enum EvsResult { + OK = 0, + /** + * Given arguments are invalid + */ + INVALID_ARG, + /** + * Requested stream is already running + */ + STREAM_ALREADY_RUNNING, + /** + * Buffer is not available; e.g. failed to allocate + */ + BUFFER_NOT_AVAILABLE, + /** + * Ownership has been expired or stolen by other clients + */ + OWNERSHIP_LOST, + /** + * A dependent service fails to handle a request + */ + UNDERLYING_SERVICE_ERROR, + /** + * Permission denied + */ + PERMISSION_DENIED, + /** + * Either the camera or the display is not available + */ + RESOURCE_NOT_AVAILABLE, + /** + * Device or resource busy + */ + RESOURCE_BUSY, + /** + * A method is not implemented yet + */ + NOT_IMPLEMENTED, + /** + * Requested functionality is not supported + */ + NOT_SUPPORTED, +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCamera.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCamera.aidl new file mode 100644 index 0000000000..080dd75247 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCamera.aidl @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.BufferDesc; +import android.hardware.automotive.evs.CameraDesc; +import android.hardware.automotive.evs.CameraParam; +import android.hardware.automotive.evs.IEvsCameraStream; +import android.hardware.automotive.evs.IEvsDisplay; +import android.hardware.automotive.evs.ParameterRange; + +/** + * Represents a single camera and is the primary interface for capturing images. + */ +@VintfStability +interface IEvsCamera { + /** + * Returns frames that were delivered to the IEvsCameraStream. + * + * When done consuming a frame delivered to the IEvsCameraStream + * interface, it must be returned to the IEvsCamera for reuse. + * A small, finite number of buffers are available (possibly as small + * as one), and if the supply is exhausted, no further frames may be + * delivered until a buffer is returned. + * + * @param in buffer Buffers to be returned. + */ + void doneWithFrame(in BufferDesc[] buffer); + + /** + * Sets to be the primary client forcibly. + * + * The client, which owns the display, has a high priority and can take over + * a primary client role from other clients without the display. + * + * @param in display IEvsDisplay handle. If a given display is in either + * NOT_VISIBLE, VISIBLE_ON_NEXT_FRAME, or VISIBLE state, the + * calling client is considered as the high priority client + * and therefore allowed to take over a primary client role from + * existing primary client. + * @throws EvsResult::INVALID_ARG if a given display handle is null or invalid states. + */ + void forcePrimaryClient(in IEvsDisplay display); + + /** + * Returns the description of this camera. + * + * @return The description of this camera. This must be the same value as + * reported by IEvsEnumerator::getCameraList(). + */ + CameraDesc getCameraInfo(); + + /** + * Request driver specific information from the HAL implementation. + * + * The values allowed for opaqueIdentifier are driver specific, + * but no value passed in may crash the driver. + * + * @param in opaqueIdentifier An unique identifier of the information to + * request. + * @return Requested information. Zero-size vector is returned if the driver does + * not recognize a given identifier. + * @throws EvsResult::INVALID_ARG for any unrecognized opaqueIdentifier. + */ + byte[] getExtendedInfo(in int opaqueIdentifier); + + /** + * Retrieves values of given camera parameter. The driver must report + * EvsResult::INVALID_ARG if a request parameter is not supported. + * + * @param in id The identifier of camera parameter, CameraParam enum. + * @return Values of requested camera parameter, the same number of values as + * backing camera devices. + * @throws EvsResult::INVALID_ARG for any unrecognized parameter. + * EvsResult::UNDERLYING_SERVICE_ERROR for any other failures. + */ + int[] getIntParameter(in CameraParam id); + + /** + * Requests a valid value range of a camera parameter + * + * @param in id The identifier of camera parameter, CameraParam enum. + * @return ParameterRange of a requested CameraParam + */ + ParameterRange getIntParameterRange(in CameraParam id); + + /** + * Retrieves a list of parameters this camera supports. + * + * @return A list of CameraParam that this camera supports. + */ + CameraParam[] getParameterList(); + + /** + * Returns the description of the physical camera device that backs this + * logical camera. + * + * If a requested device does not either exist or back this logical device, + * this method returns a null camera descriptor. And, if this is called on + * a physical camera device, this method is the same as getCameraInfo() + * method if a given device ID is matched. Otherwise, this will return a + * null camera descriptor. + * + * @param in deviceId Physical camera device identifier string. + * @return The description of a member physical camera device. + * This must be the same value as reported by IEvsEnumerator::getCameraList(). + */ + CameraDesc getPhysicalCameraInfo(in String deviceId); + + /** + * Import external buffers to capture frames + * + * This API must be called with a physical camera device identifier. + * + * @param in buffers A list of buffers allocated by the caller. EvsCamera + * will use these buffers to capture frames, in addition to + * other buffers already in its buffer pool. + * @return The amount of buffer pool size changes after importing given buffers. + */ + int importExternalBuffers(in BufferDesc[] buffers); + + /** + * Requests to pause EVS camera stream events. + * + * Like stopVideoStream(), events may continue to arrive for some time + * after this call returns. Delivered frame buffers must be returned. + */ + void pauseVideoStream(); + + /** + * Requests to resume EVS camera stream. + */ + void resumeVideoStream(); + + /** + * Send a driver specific value to the HAL implementation. + * + * This extension is provided to facilitate car specific + * extensions, but no HAL implementation may require this call + * in order to function in a default state. + * INVALID_ARG is returned if the opaqueValue is not meaningful to + * the driver implementation. + * + * @param in opaqueIdentifier An unique identifier of the information to + * program. + * in opaqueValue A value to program. + * @throws EvsResult::INVALID_ARG if this call fails to set a parameter. + */ + void setExtendedInfo(in int opaqueIdentifier, in byte[] opaqueValue); + + /** + * Requests to set a camera parameter. + * + * Only a request from the primary client will be processed successfully. + * When this method is called on a logical camera device, it will be forwarded + * to each physical device and, if it fails to program any physical device, + * it will return an error code with the same number of effective values as + * the number of backing camera devices. + * + * @param in id The identifier of camera parameter, CameraParam enum. + * @param in value A desired parameter value. + * @return Programmed parameter values. This may differ from what the client + * gives if, for example, the driver does not support a target parameter. + * @throws EvsResult::INVALID_ARG if either the request is not made by the primary + * client, or a requested parameter is not supported. + * EvsResult::UNDERLYING_SERVICE_ERROR if it fails to program a value by any + * other reason. + */ + int[] setIntParameter(in CameraParam id, in int value); + + /** + * Requests to be the primary client. + * + * When multiple clients subscribe to a single camera hardware and one of + * them adjusts a camera parameter such as the contrast, it may disturb + * other clients' operations. Therefore, the client must call this method + * to be a primary client. Once it becomes a primary client, it will be able to + * change camera parameters until either it dies or explicitly gives up the + * role. + * + * @throws EvsResult::OWNERSHIP_LOST if there is already the primary client. + */ + void setPrimaryClient(); + + /** + * Specifies the depth of the buffer chain the camera is asked to support. + * + * Up to this many frames may be held concurrently by the client of IEvsCamera. + * If this many frames have been delivered to the receiver without being returned + * by doneWithFrame, the stream must skip frames until a buffer is returned for reuse. + * It is legal for this call to come at any time, even while streams are already running, + * in which case buffers should be added or removed from the chain as appropriate. + * If no call is made to this entry point, the IEvsCamera must support at least one + * frame by default. More is acceptable. + * + * @param in bufferCount Number of buffers the client of IEvsCamera may hold concurrently. + * @throws EvsResult::BUFFER_NOT_AVAILABLE if the client cannot increase the max frames. + * EvsResult::INVALID_ARG if the client cannot decrease the max frames. + * EvsResult::OWNERSHIP_LOST if we lost an ownership of a target camera. + */ + void setMaxFramesInFlight(in int bufferCount); + + /** + * Request to start EVS camera stream from this camera. + * + * The IEvsCameraStream must begin receiving calls with various events + * including new image frame ready until stopVideoStream() is called. + * + * @param in receiver IEvsCameraStream implementation. + * @throws EvsResult::OWNERSHIP_LOST if we lost an ownership of a target camera. + * EvsResult::STREAM_ALREADY_RUNNING if a video stream has been started already. + * EvsResult::BUFFER_NOT_AVAILABLE if it fails to secure a minimum number of + * buffers to run a video stream. + * EvsResult::UNDERLYING_SERVICE_ERROR for all other failures. + */ + void startVideoStream(in IEvsCameraStream receiver); + + /** + * Stop the delivery of EVS camera frames. + * + * Because delivery is asynchronous, frames may continue to arrive for + * some time after this call returns. Each must be returned until the + * closure of the stream is signaled to the IEvsCameraStream. + * This function cannot fail and is simply ignored if the stream isn't running. + */ + void stopVideoStream(); + + /** + * Retires from the primary client role. + * + * @throws EvsResult::INVALID_ARG if the caller client is not a primary client. + */ + void unsetPrimaryClient(); +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCameraStream.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCameraStream.aidl new file mode 100644 index 0000000000..2c2b44caf5 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCameraStream.aidl @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.BufferDesc; +import android.hardware.automotive.evs.EvsEventDesc; +import android.hardware.graphics.common.HardwareBuffer; + +/** + * Implemented on client side to receive asynchronous streaming event deliveries. + */ +@VintfStability +oneway interface IEvsCameraStream { + /** + * Receives calls from the HAL each time video frames is ready for inspection. + * Buffer handles received by this method must be returned via calls to + * IEvsCamera::doneWithFrame(). When the video stream is stopped via a call + * to IEvsCamera::stopVideoStream(), this callback may continue to happen for + * some time as the pipeline drains. Each frame must still be returned. + * When the last frame in the stream has been delivered, STREAM_STOPPED + * event must be delivered. No further frame deliveries may happen + * thereafter. + * + * A camera device will deliver the same number of frames as number of + * backing physical camera devices; it means, a physical camera device + * sends always a single frame and a logical camera device sends multiple + * frames as many as number of backing physical camera devices. + * + * @param in buffer Buffer descriptors of delivered image frames. + */ + void deliverFrame(in BufferDesc[] buffer); + + /** + * Receives calls from the HAL each time an event happens. + * + * @param in event EVS event with possible event information. + */ + void notify(in EvsEventDesc event); +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsDisplay.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsDisplay.aidl new file mode 100644 index 0000000000..8d57014df5 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsDisplay.aidl @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.BufferDesc; +import android.hardware.automotive.evs.DisplayDesc; +import android.hardware.automotive.evs.DisplayState; + +/** + * Represents a single display. + */ +@VintfStability +interface IEvsDisplay { + /** + * Returns the description of this display. + * + * @return The information of this display including id, current mode, current state, + * and additional vendor-specific information. + * @throws EvsResult::UNDERLYING_SERVICE_ERROR if it fails to read a display information. + */ + DisplayDesc getDisplayInfo(); + + /** + * This call requests the current state of the display + * + * The HAL implementation should report the actual current state, which might + * transiently differ from the most recently requested state. Note, however, that + * the logic responsible for changing display states should generally live above + * the device layer, making it undesirable for the HAL implementation to spontaneously + * change display states. + * + * @return Current DisplayState of this Display. + */ + DisplayState getDisplayState(); + + /** + * This call returns a handle to a frame buffer associated with the display. + * + * @return A handle to a frame buffer. The returned buffer may be locked and + * written to by software and/or GL. This buffer must be returned via + * a call to returnTargetBufferForDisplay() even if the display is no + * longer visible. + * @throws EvsResult::OWNERSHIP_LOST if a display is in DisplayState::DEAD. + * EvsResult::BUFFER_NOT_AVAILABLE if no buffer is available. + * EvsResult::UNDERLYING_SERVICE_ERROR for any other failures. + */ + BufferDesc getTargetBuffer(); + + /** + * This call tells the display that the buffer is ready for display. + * + * The buffer is no longer valid for use by the client after this call. + * There is no maximum time the caller may hold onto the buffer before making this + * call. The buffer may be returned at any time and in any DisplayState, but all + * buffers are expected to be returned before the IEvsDisplay interface is destroyed. + * + * @param in buffer A buffer handle to the frame that is ready for display. + * @throws EvsResult::INVALID_ARG if a given buffer is unknown or invalid. + * EvsResult::OWNERSHIP_LOST if a display is in DisplayState::DEAD. + * EvsResult::UNDERLYING_SERVICE_ERROR for any other failures. + */ + void returnTargetBufferForDisplay(in BufferDesc buffer); + + /** + * Clients may set the display state to express their desired state. + * + * The HAL implementation must gracefully accept a request for any state while in + * any other state, although the response may be to defer or ignore the request. The display + * is defined to start in the NOT_VISIBLE state upon initialization. The client is + * then expected to request the VISIBLE_ON_NEXT_FRAME state, and then begin providing + * video. When the display is no longer required, the client is expected to request + * the NOT_VISIBLE state after passing the last video frame. + * Returns INVALID_ARG if the requested state is not a recognized value. + * + * @param in state Desired new DisplayState. + * @throws EvsResult::INVALID_ARG if a given state is invalid. + * EvsResult::OWNERSHIP_LOST if a display is in DisplayState::DEAD. + */ + void setDisplayState(in DisplayState state); +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsEnumerator.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsEnumerator.aidl new file mode 100644 index 0000000000..8e380e0bd4 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsEnumerator.aidl @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.CameraDesc; +import android.hardware.automotive.evs.DisplayState; +import android.hardware.automotive.evs.IEvsCamera; +import android.hardware.automotive.evs.IEvsDisplay; +import android.hardware.automotive.evs.IEvsEnumeratorStatusCallback; +import android.hardware.automotive.evs.IEvsUltrasonicsArray; +import android.hardware.automotive.evs.Stream; +import android.hardware.automotive.evs.UltrasonicsArrayDesc; + +/** + * Provides the mechanism for EVS camera and ultrasonics array discovery + */ +@VintfStability +interface IEvsEnumerator { + /** + * Return the specified IEvsCamera interface as no longer in use + * + * When the IEvsCamera object is no longer required, it must be released. + * NOTE: Video streaming must be cleanly stopped before making this call. + * + * @param in carCamera EvsCamera object to be closed. + * @throws EvsResult::INVALID_ARG if a given camera object is invalid. + */ + void closeCamera(in IEvsCamera carCamera); + + /** + * Return the specified IEvsDisplay interface as no longer in use + * + * When the IEvsDisplay object is no longer required, it must be released. + * NOTE: All buffers must have been returned to the display before making this call. + * + * @param in display EvsDisplay object to be closed. + */ + void closeDisplay(in IEvsDisplay display); + + /** + * Return the specified IEvsUltrasonicsArray interface as no longer in use + * + * When the IEvsUltrasonicsArray object is no longer required, it must be released. + * NOTE: Data streaming must be cleanly stopped before making this call. + * + * @param in evsUltrasonicsArray EvsUltrasonics array object to be closed. + */ + void closeUltrasonicsArray(in IEvsUltrasonicsArray evsUltrasonicsArray); + + /** + * Returns a list of all EVS cameras available to the system + * + * @return A list of cameras availale for EVS service. + * @throws EvsResult::PERMISSION_DENIED if the process is not permitted to enumerate + * camera devices. + */ + CameraDesc[] getCameraList(); + + /** + * Returns a list of all EVS displays available to the system + * + * @return Identifiers of available displays. + */ + byte[] getDisplayIdList(); + + /** + * This call requests the current state of the display + * + * If there is no open display, this returns DisplayState::NOT_OPEN. otherwise, it returns + * the actual state of the active display. This call is replicated on the IEvsEnumerator + * interface in order to allow secondary clients to monitor the state of the EVS display + * without acquiring exclusive ownership of the display. + * + * @return Current DisplayState of this Display. + * @throws EvsResult::OWNERSHIP_LOST if current display is inactive + * EvsResult::PERMISSION_DENIED if the process is not permitted to do this operation. + */ + DisplayState getDisplayState(); + + /** + * Return a list of the stream configurations a target camera device supports + * + * @param in description A target camera descriptor + * @return A list of stream configurations supported by a given camera device + */ + Stream[] getStreamList(in CameraDesc description); + + /** + * Returns a list of all ultrasonics array available to the system. + * Will return an empty vector if ultrasonics is not supported. + * + * @return A list of ultrasonics available for EVS service. + */ + UltrasonicsArrayDesc[] getUltrasonicsArrayList(); + + /** + * Tells whether this is EvsManager or HAL implementation. + * + * @return False for EvsManager implementations and true for all others. + */ + boolean isHardware(); + + /** + * Gets the IEvsCamera associated with a cameraId from a CameraDesc + * + * Given a camera's unique cameraId from CameraDesc, returns the + * IEvsCamera interface associated with the specified camera. When + * done using the camera, the caller may release it by calling closeCamera(). + * + * @param in cameraId A unique identifier of the camera. + * @param in streamCfg A stream configuration the client wants to use. + * @return EvsCamera object associated with a given cameraId. + * Returned object would be null if a camera device does not support a + * given stream configuration or is already configured differently by + * another client. + * @throws EvsResult::PERMISSION_DENIED if the process is not permitted to use camera + * devices. + * EveResult::INVALID_ARG if it fails to open a camera with a given id. + */ + IEvsCamera openCamera(in String cameraId, in Stream streamCfg); + + /** + * Get exclusive access to IEvsDisplay for the system + * + * There can be more than one EVS display objects for the system and this function + * requests access to the display identified by a given ID. If the target EVS display + * is not available or is already in use the old instance shall be closed and give + * the new caller exclusive access. + * When done using the display, the caller may release it by calling closeDisplay(). + * + * @param in id Target display identifier. + * @return EvsDisplay object to be used. + * @throws EvsResult::INVALID_ARG if no display with a given id exists + */ + IEvsDisplay openDisplay(in byte id); + + /** + * Gets the IEvsUltrasonicsArray associated with a ultrasonicsArrayId from a + * UltrasonicsDataDesc + * + * @param in ultrasonicsArrayId A unique identifier of the ultrasonic array. + * @return IEvsUltrasonicsArray object associated with a given ultrasonicsArrayId. + */ + IEvsUltrasonicsArray openUltrasonicsArray(in String ultrasonicsArrayId); + + /** + * Registers a callback to listen to devices' status changes + * + * @param in callback IEvsEnumeratorStatusCallback implementation + */ + void registerStatusCallback(in IEvsEnumeratorStatusCallback callback); +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.aidl new file mode 100644 index 0000000000..26ccf72f08 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.DeviceStatus; + +/** + * Implemented on client side to receive asynchronous notifications from + * IEvsEnumreator. + */ +@VintfStability +oneway interface IEvsEnumeratorStatusCallback { + /** + * Receives calls from the HAL each time a status of camera devices is + * changed. + * + * @param in status A list of newly updated device status + */ + void deviceStatusChanged(in DeviceStatus[] status); +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsUltrasonicsArray.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsUltrasonicsArray.aidl new file mode 100644 index 0000000000..40de313abf --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsUltrasonicsArray.aidl @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.IEvsUltrasonicsArrayStream; +import android.hardware.automotive.evs.UltrasonicsArrayDesc; +import android.hardware.automotive.evs.UltrasonicsDataFrameDesc; + +/** + * HAL interface for ultrasonics sensor array. + */ +@VintfStability +interface IEvsUltrasonicsArray { + /** + * Notifies the UltrasonicsDataDesc is consumed that was received from + * IEvsUltrasonicsArrayStream + * + * @param in dataFrameDesc Ultrasonics data descriptor + */ + void doneWithDataFrame(in UltrasonicsDataFrameDesc dataFrameDesc); + + /** + * Returns the ultrasonic sensor array information + * + * @throws The description of this ultrasonic array. This must be the same + * value as reported by IEvsEnumerator::getUltrasonicsArrayList(). + */ + UltrasonicsArrayDesc getUltrasonicArrayInfo(); + + /** + * Specifies the depth of the buffer chain the ultrasonic sensors is + * asked to support + * + * Up to this many data frames may be held concurrently by the client of IEvsUltrasonicsArray. + * If this many frames have been delivered to the receiver without being returned + * by doneWithFrame, the stream must skip frames until a buffer is returned for reuse. + * It is legal for this call to come at any time, even while streams are already running, + * in which case buffers should be added or removed from the chain as appropriate. + * If no call is made to this entry point, the IEvsUltrasonicsArray must support at least one + * data frame by default. More is acceptable. + * + * @param in bufferCount Number of buffers the client of IEvsUltrasonicsArray may hold + * concurrently. + * @throws EvsResult::INVALID_ARG on invalid bufferCount. + */ + void setMaxFramesInFlight(in int bufferCount); + + /** + * Requests to start the stream + * + * @param in stream Implementation of IEvsUltrasonicsArrayStream. + * @throws EvsResult::STREAM_ALREADY_RUNNING if stream is already running + */ + void startStream(in IEvsUltrasonicsArrayStream stream); + + /** + * Requests to stop the delivery of the ultrasonic array data frames + * + * Because delivery is asynchronous, frames may continue to arrive for + * some time after this call returns. Each must be returned until the + * closure of the stream is signaled to the IEvsCameraStream. + * This function cannot fail and is ignored if the stream isn't running. + */ + void stopStream(); +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsUltrasonicsArrayStream.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsUltrasonicsArrayStream.aidl new file mode 100644 index 0000000000..bc31a6ba95 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsUltrasonicsArrayStream.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.EvsEventDesc; +import android.hardware.automotive.evs.UltrasonicsDataFrameDesc; + +/** + * Implemented on client side to receive asynchronous ultrasonic data + * deliveries. + */ +@VintfStability +interface IEvsUltrasonicsArrayStream { + /** + * Receives calls from the HAL each time a data frame is ready + * + * @param in dataFrameDesc Ultrasonic array data frame descriptor + */ + oneway void deliverDataFrame(in UltrasonicsDataFrameDesc dataFrameDesc); + + /** + * Receives calls from the HAL each time an event happens + * + * @param in event Event EVS event with possible event information + */ + oneway void notify(in EvsEventDesc event); +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/ParameterRange.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/ParameterRange.aidl new file mode 100644 index 0000000000..b08fcbd975 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/ParameterRange.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * Represent a valid range of CameraParam + */ +@VintfStability +parcelable ParameterRange { + /** + * Lower bound of a valid value range + */ + int min; + /** + * Upper bound of a valid value range + */ + int max; + /** + * A value of unit increment + */ + int step; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/Rotation.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/Rotation.aidl new file mode 100644 index 0000000000..dede39e15e --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/Rotation.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * Rotation: + * + * The required counterclockwise rotation of EVS camera stream and display. + */ +@VintfStability +@Backing(type="int") +enum Rotation { + /** No rotation */ + ROTATION_0 = 0, + /** Rotate by 90 degree counterclockwise */ + ROTATION_90 = 1, + /** Rotate by 180 degree counterclockwise */ + ROTATION_180 = 2, + /** Rotate by 270 degree counterclockwise */ + ROTATION_270 = 3 + +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/RotationQuaternion.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/RotationQuaternion.aidl new file mode 100644 index 0000000000..b80343bf7c --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/RotationQuaternion.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * Structure for rotation expressed as quaternions. + * Convention used: Unit quaternion with hamilton convention. + */ +@VintfStability +parcelable RotationQuaternion { + float x; + float y; + float z; + float w; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/SensorPose.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/SensorPose.aidl new file mode 100644 index 0000000000..26c33399b8 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/SensorPose.aidl @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.RotationQuaternion; +import android.hardware.automotive.evs.Translation; + +/** + * Provides the orientation and location of a car sensor relative to the android automotive + * coordinate system: + * https://source.android.com/devices/sensors/sensor-types#auto_axes + * The sensor pose defines the transformation to be applied to the android automotive axes to + * obtain the sensor local axes. + * The pose consists of rotation, (specified as a quaternions) and translation + * (vector with x, y, z). + * This rotation and translation applied to the sensor data in the sensor's local coordinate + * system transform the data to the automotive coordinate system. + * i.e. loc = ( Rot * Psensor ) + Trans + * Here loc is a point in automotive coordinate system and Psensor is a point in the sensor's + * coordinate system. + * Example: + * For a sensor on the front bumper and on the left corner of the car with its X axis pointing to + * the front, the sensor is located at (-2, 4, 0) meters w.r.t android automotive axes and the + * sensor local axes has a rotation of 90 degrees counter-clockwise w.r.t android automotive axes + * when viewing the car from top on the +Z axis side: + * + * ↑X sensor + * Y←∘______ + * | | front + * | car | + * | ↑Y | + * | ∘→X | rear + * |______| + * + * For this example the rotation and translation will be: + * Rotation = + 90 degrees around Z axis = (0.7071, 0, 0, 0.7071) as a unit quaternion. + * Translation = (-2, 4, 0) in meters = (-2000, 4000, 0) in milli-meters. + * Note: Every sensor type must specify its own pose. + */ +@VintfStability +parcelable SensorPose { + /** + * Rotation part of the sensor pose, expressed as a unit quaternion. + */ + RotationQuaternion rotation; + /** + * Translation part of the sensor pose, in (x, y, z) format with milli-meter units. + */ + Translation translation; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/Stream.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/Stream.aidl new file mode 100644 index 0000000000..ae5c7f0748 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/Stream.aidl @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.Rotation; +import android.hardware.automotive.evs.StreamType; +import android.hardware.graphics.common.BufferUsage; +import android.hardware.graphics.common.PixelFormat; + +/** + * Stream: + * + * Structure that describes a EVS Camera stream + */ +@VintfStability +parcelable Stream { + /** + * Stream ID - a non-negative integer identifier for a stream. + * + * The identical stream ID must reference the same stream, with the same + * width/height/format, across consecutive calls to configureStreams. + * + * If previously-used stream ID is not used in a new call to + * configureStreams, then that stream is no longer active. Such a stream ID + * may be reused in a future configureStreams with a new + * width/height/format. + * + */ + int id; + /** + * The type of the stream (input vs output, etc). + */ + StreamType streamType; + /** + * The width in pixels of the buffers in this stream. + */ + int width; + /** + * The height in pixels of the buffers in this stream. + */ + int height; + /** + * The frame rate of this stream in frames-per-second + / + int framerate; + /** + * The pixel format form the buffers in this stream. + */ + PixelFormat format; + /** + * The gralloc usage flags for this stream, as needed by the consumer of + * the stream. + */ + BufferUsage usage; + /** + * The required output rotation of the stream. + * + * This must be inspected by HAL along with stream with and height. + */ + Rotation rotation; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/StreamType.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/StreamType.aidl new file mode 100644 index 0000000000..c028a5c43f --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/StreamType.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * StreamType: + * + * The type of the camera stream, which defines whether the EVS client device is + * the producer or the consumer for that stream, and how the buffers of the + * stream relate to the other streams. + */ +@VintfStability +@Backing(type="int") +enum StreamType { + /** + * This stream is an output stream; the EVS HAL device must fill buffers + * from this stream with newly captured or reprocessed image data. + */ + OUTPUT = 0, + + /** + * This stream is an input stream; the EVS HAL device must read buffers + * from this stream and send them through the camera processing pipeline, + * as if the buffer was a newly captured image from the imager. + */ + INPUT = 1 +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/Translation.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/Translation.aidl new file mode 100644 index 0000000000..14b14db0c9 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/Translation.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +/** + * Structure for translation with x, y and z units. + */ +@VintfStability +parcelable Translation { + float x; + float y; + float z; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicSensor.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicSensor.aidl new file mode 100644 index 0000000000..712411bd6d --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicSensor.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.SensorPose; + +/** + * Structure that contains all information of an ultrasonic sensor. + */ +@VintfStability +parcelable UltrasonicSensor { + /** + * Pose provides the orientation and location of the ultrasonic sensor within the car. + * The +Y axis points along the center of the beam spread the X axis to the right and the Z + * axis in the up direction. + */ + SensorPose pose; + /** + * Maximum range of the sensor in milli-metres. + */ + float maxRangeMm; + /** + * Half-angle of the angle of measurement of the sensor, relative to the + * sensor’s x axis, in radians. + */ + float angleOfMeasurement; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicsArrayDesc.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicsArrayDesc.aidl new file mode 100644 index 0000000000..d4f0663b27 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicsArrayDesc.aidl @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.automotive.evs.UltrasonicSensor; + +/** + * Structure identifies and describes an ultrasonics array in the car. + * + * A ultrasonics array represents a group of ultrasonic sensors within the + * car. These may be sensors that are physically connected to the same hardware + * control unit or represent a logical group of sensors like front and back. + * The HAL is responsible for filling out this structure for each Ultrasonics + * Array. + */ +@VintfStability +parcelable UltrasonicsArrayDesc { + /** + * Unique identifier for the ultrasonic array. This may be a path or name of the + * physical control device or a string identifying a logical group of sensors forming an array + * such as "front_array" and "back_array". + */ + @utf8InCpp + String ultrasonicsArrayId; + /** + * Maximum number of readings (points on waveform) provided per sensor in + * each data frame. Used by client to pre-allocate required memory buffer for + * incoming data. + */ + int maxReadingsPerSensorCount; + /** + * Maximum number of receiver sensors in a data frame. Must be between 1 + * and sensorCount. Used by client to pre-allocate required memory buffer for + * incoming data. + */ + int maxReceiversCount; + /** + * The order of sensors specified must be in clockwise order around the car, starting + * from front left-most sensor. + */ + UltrasonicSensor[] sensors; +} diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicsDataFrameDesc.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicsDataFrameDesc.aidl new file mode 100644 index 0000000000..e546db92c2 --- /dev/null +++ b/automotive/evs/aidl/android/hardware/automotive/evs/UltrasonicsDataFrameDesc.aidl @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.evs; + +import android.hardware.common.Ashmem; + +/** + * Structure that describes the data frame received from an ultrasonics array. + * + * Each data frame returned consists of received waveform signals from a subset + * of sensors in an array as indicated by the receiversIdList. The signal is + * transmitted at a particular time instant indicated by timestampNs from a + * subset of sensors in the array as provided in the transmittersIdList. + */ +@VintfStability +parcelable UltrasonicsDataFrameDesc { + /** + * Timestamp of the start of the transmit signal for this data frame. + * Timestamp unit is nanoseconds and is obtained from android::elapsedRealtimeNanos(). + * timeOfFlight readings are future-deltas to this timestamp. + */ + long timestampNs; + /** + * Identifier of data frame. Used by implementation for managing multiple frames in flight. + */ + int id; + /** + * List of indexes of sensors in range [0, sensorCount - 1] that + * transmitted the signal for this data frame. + */ + byte[] transmittersIdList; + /** + * List of indexes of sensors in range [0, sensorCount - 1] that received + * the signal. The order of ids must match the order of the waveforms in the + * waveformsData. + * Size of list is upper bound by maxReceiversCount. + */ + byte[] receiversIdList; + /** + * List of the number of readings corresponding to each ultrasonics sensor in + * the receiversIdList. Order of the readings count must match the order in + * receiversIdList. + * Size of list is upper bound by maxReadingsPerSensorCount. + */ + int[] receiversReadingsCountList; + /** + * Shared memory object containing the waveforms data. Contains one waveform + * for each sensor specified in receiversIdList, in order. + * Each waveform is represented by a number of readings, which are sample + * points on the waveform. The number of readings for each waveform is as + * specified in the receiversReadingsCountList. + * Each reading is a pair of time Of flight and resonance. + * Time of flight (float): Time between transmit and receive signal in nanoseconds. + * Resonance (float): Resonance at time on waveform in range [0.0, 1.0]. + * + * The structure of shared memory (example with 2 waveforms, each with 2 readings): + * + * Byte: | 0 | 1-4 | 5-8 | 9-12 | 13-16 || 17 | 18-21 | 22-25 | 26-29 | 30-33 | + * Data: | RecId1 | TOF1 | RES1 | TOF2 | RES2 || RecId2 | TOF1 | RES1 | TOF2 | RES2 | + * | Waveform1 || Waveform2 | + * Here: + * RecId : Receiver's Id. Order matches the receiversIdList, type uint8_t + * TOF : Time of flight, type float (4 bytes) + * RES : Resonance, type float (4 bytes) + * Note: All readings and waveforms are contigious with no padding. + */ + Ashmem waveformsData; +} diff --git a/automotive/evs/aidl/impl/Android.bp b/automotive/evs/aidl/impl/Android.bp new file mode 100644 index 0000000000..7eb01166c3 --- /dev/null +++ b/automotive/evs/aidl/impl/Android.bp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_defaults { + name: "EvsHalDefaults", + static_libs: [ + "android.hardware.automotive.evs-V1-ndk", + "android.hardware.common-V2-ndk", + "android.hardware.graphics.common-V3-ndk", + ], + shared_libs: [ + "libbase", + "liblog", + "libutils", + ], + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wthread-safety", + ], +} diff --git a/automotive/evs/aidl/impl/default/Android.bp b/automotive/evs/aidl/impl/default/Android.bp new file mode 100644 index 0000000000..dbe0314118 --- /dev/null +++ b/automotive/evs/aidl/impl/default/Android.bp @@ -0,0 +1,36 @@ +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // 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"], +} + +cc_binary { + name: "android.hardware.automotive.evs-aidl-default-service", + defaults: ["EvsHalDefaults"], + local_include_dirs: ["include"], + vintf_fragments: ["evs-default-service.xml"], + init_rc: ["evs-default-service.rc"], + vendor: true, + relative_install_path: "hw", + srcs: ["src/*.cpp"], + shared_libs: [ + "libbinder_ndk", + ], +} diff --git a/automotive/evs/aidl/impl/default/evs-default-service.rc b/automotive/evs/aidl/impl/default/evs-default-service.rc new file mode 100644 index 0000000000..ea8e6892dc --- /dev/null +++ b/automotive/evs/aidl/impl/default/evs-default-service.rc @@ -0,0 +1,5 @@ +service vendor.evs-hal-default /vendor/bin/hw/android.hardware.automotive.evs-aidl-default-service + class early_hal + user automotive_evs + group automotive_evs + disabled diff --git a/automotive/evs/aidl/impl/default/evs-default-service.xml b/automotive/evs/aidl/impl/default/evs-default-service.xml new file mode 100644 index 0000000000..96ff9f6576 --- /dev/null +++ b/automotive/evs/aidl/impl/default/evs-default-service.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.automotive.evs</name> + <transport>hwbinder</transport> + <version>1</version> + <interface> + <name>IEvsEnumerator</name> + <instance>hw/0</instance> + </interface> + </hal> +</manifest> diff --git a/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h b/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h new file mode 100644 index 0000000000..8bcd867d48 --- /dev/null +++ b/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_ +#define android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_ + +#include <aidl/android/hardware/automotive/evs/BnEvsEnumerator.h> + +namespace aidl::android::hardware::automotive::evs::implementation { + +class DefaultEvsEnumerator final + : public ::aidl::android::hardware::automotive::evs::BnEvsEnumerator { + ::ndk::ScopedAStatus isHardware(bool* flag) override; + ::ndk::ScopedAStatus openCamera( + const std::string& cameraId, + const ::aidl::android::hardware::automotive::evs::Stream& streamConfig, + std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>* obj) override; + ::ndk::ScopedAStatus closeCamera( + const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>& obj) + override; + ::ndk::ScopedAStatus getCameraList( + std::vector<::aidl::android::hardware::automotive::evs::CameraDesc>* list) override; + ::ndk::ScopedAStatus getStreamList( + const ::aidl::android::hardware::automotive::evs::CameraDesc& desc, + std::vector<::aidl::android::hardware::automotive::evs::Stream>* _aidl_return) override; + ::ndk::ScopedAStatus openDisplay( + int8_t displayId, + std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>* obj) override; + ::ndk::ScopedAStatus closeDisplay( + const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>& obj) + override; + ::ndk::ScopedAStatus getDisplayIdList(std::vector<uint8_t>* list) override; + ::ndk::ScopedAStatus getDisplayState( + ::aidl::android::hardware::automotive::evs::DisplayState* state) override; + ::ndk::ScopedAStatus registerStatusCallback( + const std::shared_ptr< + ::aidl::android::hardware::automotive::evs::IEvsEnumeratorStatusCallback>& + callback) override; + ::ndk::ScopedAStatus openUltrasonicsArray( + const std::string& id, + std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>* obj) + override; + ::ndk::ScopedAStatus closeUltrasonicsArray( + const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>& + arr) override; + ::ndk::ScopedAStatus getUltrasonicsArrayList( + std::vector<::aidl::android::hardware::automotive::evs::UltrasonicsArrayDesc>* list) + override; +}; + +} // namespace aidl::android::hardware::automotive::evs::implementation + +#endif // android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_ diff --git a/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp b/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp new file mode 100644 index 0000000000..2ff6d59af2 --- /dev/null +++ b/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO(b/203661081): Remove below lines to disable compiler warnings. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" + +#define LOG_TAG "DefaultEvsEnumerator" + +#include <DefaultEvsEnumerator.h> + +namespace aidl::android::hardware::automotive::evs::implementation { + +using ::ndk::ScopedAStatus; + +ScopedAStatus DefaultEvsEnumerator::isHardware(bool* flag) { + // This returns true always. + *flag = true; + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::openCamera(const std::string& cameraId, + const Stream& streamConfig, + std::shared_ptr<IEvsCamera>* obj) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::closeCamera(const std::shared_ptr<IEvsCamera>& obj) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::getCameraList(std::vector<CameraDesc>* list) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::getStreamList(const CameraDesc& desc, + std::vector<Stream>* _aidl_return) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::openDisplay(int8_t displayId, + std::shared_ptr<IEvsDisplay>* obj) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::closeDisplay(const std::shared_ptr<IEvsDisplay>& state) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::getDisplayIdList(std::vector<uint8_t>* list) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::getDisplayState(DisplayState* state) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::registerStatusCallback( + const std::shared_ptr<IEvsEnumeratorStatusCallback>& callback) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::openUltrasonicsArray( + const std::string& id, std::shared_ptr<IEvsUltrasonicsArray>* obj) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::closeUltrasonicsArray( + const std::shared_ptr<IEvsUltrasonicsArray>& obj) { + return ScopedAStatus::ok(); +} + +ScopedAStatus DefaultEvsEnumerator::getUltrasonicsArrayList( + std::vector<UltrasonicsArrayDesc>* list) { + return ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::automotive::evs::implementation + +#pragma clang diagnostic pop diff --git a/automotive/evs/aidl/impl/default/src/service.cpp b/automotive/evs/aidl/impl/default/src/service.cpp new file mode 100644 index 0000000000..0a0913fd3c --- /dev/null +++ b/automotive/evs/aidl/impl/default/src/service.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "EvsService" + +#include <DefaultEvsEnumerator.h> + +#include <android/binder_manager.h> +#include <android/binder_process.h> +#include <utils/Log.h> + +using ::aidl::android::hardware::automotive::evs::implementation::DefaultEvsEnumerator; + +int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) { + std::shared_ptr<DefaultEvsEnumerator> vhal = ndk::SharedRefBase::make<DefaultEvsEnumerator>(); + + ALOGI("Registering as service..."); + binder_exception_t err = + AServiceManager_addService(vhal->asBinder().get(), "android.hardware.automotive.evs"); + if (err != EX_NONE) { + ALOGE("failed to register android.hardware.automotive.evs service, exception: %d", err); + return 1; + } + + if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) { + ALOGE("%s", "failed to set thread pool max thread count"); + return 1; + } + ABinderProcess_startThreadPool(); + + ALOGI("Evs Service Ready"); + + ABinderProcess_joinThreadPool(); + + ALOGI("Evs Service Exiting"); + + return 0; +} diff --git a/automotive/evs/aidl/vts/Android.bp b/automotive/evs/aidl/vts/Android.bp new file mode 100644 index 0000000000..980c6d5a5c --- /dev/null +++ b/automotive/evs/aidl/vts/Android.bp @@ -0,0 +1,53 @@ +// +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package{ + // 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"], +} + +cc_test { +name: + "VtsHalEvsTargetTest", + srcs: [ + "*.cpp", + ], + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + shared_libs: [ + "libbinder_ndk", + "libcamera_metadata", + "libui", + "libutils", + ], + static_libs: [ + "android.hardware.automotive.evs@common-default-lib", + "android.hardware.automotive.evs-V1-ndk", + "android.hardware.common-V2-ndk", + "android.hardware.graphics.common-V3-ndk", + "libaidlcommonsupport", + ], + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/automotive/evs/aidl/vts/FrameHandler.cpp b/automotive/evs/aidl/vts/FrameHandler.cpp new file mode 100644 index 0000000000..bab832b09c --- /dev/null +++ b/automotive/evs/aidl/vts/FrameHandler.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VtsHalEvsTest" + +#include "FrameHandler.h" +#include "FormatConvert.h" + +#include <aidl/android/hardware/graphics/common/HardwareBufferDescription.h> +#include <aidlcommonsupport/NativeHandle.h> +#include <android-base/logging.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferAllocator.h> + +using ::aidl::android::hardware::automotive::evs::BufferDesc; +using ::aidl::android::hardware::automotive::evs::CameraDesc; +using ::aidl::android::hardware::automotive::evs::EvsEventDesc; +using ::aidl::android::hardware::automotive::evs::EvsEventType; +using ::aidl::android::hardware::automotive::evs::IEvsCamera; +using ::aidl::android::hardware::automotive::evs::IEvsDisplay; +using ::aidl::android::hardware::graphics::common::HardwareBufferDescription; +using ::ndk::ScopedAStatus; +using std::chrono_literals::operator""s; + +FrameHandler::FrameHandler(const std::shared_ptr<IEvsCamera>& pCamera, const CameraDesc& cameraInfo, + const std::shared_ptr<IEvsDisplay>& pDisplay, BufferControlFlag mode) + : mCamera(pCamera), mCameraInfo(cameraInfo), mDisplay(pDisplay), mReturnMode(mode) { + // Nothing but member initialization here. +} + +void FrameHandler::shutdown() { + // Make sure we're not still streaming + blockingStopStream(); + + // At this point, the receiver thread is no longer running, so we can safely drop + // our remote object references so they can be freed + mCamera = nullptr; + mDisplay = nullptr; +} + +bool FrameHandler::startStream() { + // Tell the camera to start streaming + auto status = mCamera->startVideoStream(ref<FrameHandler>()); + if (!status.isOk()) { + return false; + } + + // Mark ourselves as running + mLock.lock(); + mRunning = true; + mLock.unlock(); + + return true; +} + +void FrameHandler::asyncStopStream() { + // Tell the camera to stop streaming. + // This will result in a null frame being delivered when the stream actually stops. + mCamera->stopVideoStream(); +} + +void FrameHandler::blockingStopStream() { + // Tell the stream to stop + asyncStopStream(); + + // Wait until the stream has actually stopped + std::unique_lock<std::mutex> lock(mEventLock); + if (mRunning) { + mEventSignal.wait(lock, [this]() { return !mRunning; }); + } +} + +bool FrameHandler::returnHeldBuffer() { + std::lock_guard<std::mutex> lock(mLock); + + // Return the oldest buffer we're holding + if (mHeldBuffers.empty()) { + // No buffers are currently held + return false; + } + + std::vector<BufferDesc> buffers = std::move(mHeldBuffers.front()); + mHeldBuffers.pop(); + mCamera->doneWithFrame(buffers); + + return true; +} + +bool FrameHandler::isRunning() { + std::lock_guard<std::mutex> lock(mLock); + return mRunning; +} + +void FrameHandler::waitForFrameCount(unsigned frameCount) { + // Wait until we've seen at least the requested number of frames (could be more) + std::unique_lock<std::mutex> lock(mLock); + mFrameSignal.wait(lock, [this, frameCount]() { return mFramesReceived >= frameCount; }); +} + +void FrameHandler::getFramesCounters(unsigned* received, unsigned* displayed) { + std::lock_guard<std::mutex> lock(mLock); + + if (received) { + *received = mFramesReceived; + } + if (displayed) { + *displayed = mFramesDisplayed; + } +} + +ScopedAStatus FrameHandler::deliverFrame(const std::vector<BufferDesc>& buffers) { + mLock.lock(); + // For VTS tests, FrameHandler uses a single frame among delivered frames. + auto bufferIdx = mFramesDisplayed % buffers.size(); + auto& buffer = buffers[bufferIdx]; + mLock.unlock(); + + // Store a dimension of a received frame. + mFrameWidth = buffer.buffer.description.width; + mFrameHeight = buffer.buffer.description.height; + + // If we were given an opened display at construction time, then send the received + // image back down the camera. + bool displayed = false; + if (mDisplay) { + // Get the output buffer we'll use to display the imagery + BufferDesc tgtBuffer; + auto status = mDisplay->getTargetBuffer(&tgtBuffer); + if (!status.isOk()) { + printf("Didn't get target buffer - frame lost\n"); + LOG(ERROR) << "Didn't get requested output buffer -- skipping this frame."; + } else { + // Copy the contents of the of buffer.memHandle into tgtBuffer + copyBufferContents(tgtBuffer, buffer); + + // Send the target buffer back for display + auto status = mDisplay->returnTargetBufferForDisplay(tgtBuffer); + if (!status.isOk()) { + printf("AIDL error on display buffer (%d)- frame lost\n", + status.getServiceSpecificError()); + LOG(ERROR) << "Error making the remote function call. AIDL said " + << status.getServiceSpecificError(); + } else { + // Everything looks good! + // Keep track so tests or watch dogs can monitor progress + displayed = true; + } + } + } + + mLock.lock(); + // increases counters + ++mFramesReceived; + mFramesDisplayed += (int)displayed; + mLock.unlock(); + mFrameSignal.notify_all(); + + switch (mReturnMode) { + case eAutoReturn: + // Send the camera buffer back now that the client has seen it + LOG(DEBUG) << "Calling doneWithFrame"; + mCamera->doneWithFrame(buffers); + break; + case eNoAutoReturn: + // Hang onto the buffer handles for now -- the client will return it explicitly later + // mHeldBuffers.push(buffers); + break; + } + + LOG(DEBUG) << "Frame handling complete"; + return ScopedAStatus::ok(); +} + +ScopedAStatus FrameHandler::notify(const EvsEventDesc& event) { + // Local flag we use to keep track of when the stream is stopping + std::unique_lock<std::mutex> lock(mEventLock); + mLatestEventDesc.aType = event.aType; + mLatestEventDesc.payload[0] = event.payload[0]; + mLatestEventDesc.payload[1] = event.payload[1]; + if (mLatestEventDesc.aType == EvsEventType::STREAM_STOPPED) { + // Signal that the last frame has been received and the stream is stopped + mRunning = false; + } else if (mLatestEventDesc.aType == EvsEventType::PARAMETER_CHANGED) { + LOG(DEBUG) << "Camera parameter " << mLatestEventDesc.payload[0] << " is changed to " + << mLatestEventDesc.payload[1]; + } else { + LOG(DEBUG) << "Received an event " << eventToString(mLatestEventDesc.aType); + } + lock.unlock(); + mEventSignal.notify_one(); + + return ScopedAStatus::ok(); +} + +bool FrameHandler::copyBufferContents(const BufferDesc& tgtBuffer, const BufferDesc& srcBuffer) { + bool success = true; + const HardwareBufferDescription* pSrcDesc = + reinterpret_cast<const HardwareBufferDescription*>(&srcBuffer.buffer.description); + const HardwareBufferDescription* pTgtDesc = + reinterpret_cast<const HardwareBufferDescription*>(&tgtBuffer.buffer.description); + + // Make sure we don't run off the end of either buffer + const unsigned width = std::min(pTgtDesc->width, pSrcDesc->width); + const unsigned height = std::min(pTgtDesc->height, pSrcDesc->height); + + // FIXME: We duplicate file descriptors twice below; consider using TAKE_HANDLE + // instead of CLONE_HANDLE. + buffer_handle_t target = ::android::dupFromAidl(tgtBuffer.buffer.handle); + ::android::sp<android::GraphicBuffer> tgt = new android::GraphicBuffer( + target, android::GraphicBuffer::CLONE_HANDLE, pTgtDesc->width, pTgtDesc->height, + static_cast<android::PixelFormat>(pTgtDesc->format), pTgtDesc->layers, + static_cast<uint64_t>(pTgtDesc->usage), pTgtDesc->stride); + + buffer_handle_t source = ::android::dupFromAidl(srcBuffer.buffer.handle); + ::android::sp<android::GraphicBuffer> src = new android::GraphicBuffer( + source, android::GraphicBuffer::CLONE_HANDLE, pSrcDesc->width, pSrcDesc->height, + static_cast<android::PixelFormat>(pSrcDesc->format), pSrcDesc->layers, + static_cast<uint64_t>(pSrcDesc->usage), pSrcDesc->stride); + + // Lock our source buffer for reading (current expectation are for this to be NV21 format) + uint8_t* srcPixels = nullptr; + src->lock(GRALLOC_USAGE_SW_READ_OFTEN, (void**)&srcPixels); + + // Lock our target buffer for writing (should be either RGBA8888 or BGRA8888 format) + uint32_t* tgtPixels = nullptr; + tgt->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)&tgtPixels); + + if (srcPixels && tgtPixels) { + using namespace ::android::hardware::automotive::evs::common; + if (static_cast<android_pixel_format_t>(pTgtDesc->format) == HAL_PIXEL_FORMAT_RGBA_8888) { + if (static_cast<android_pixel_format_t>(pSrcDesc->format) == + HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21 + Utils::copyNV21toRGB32(width, height, srcPixels, tgtPixels, pTgtDesc->stride); + } else if (static_cast<android_pixel_format_t>(pSrcDesc->format) == + HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12 + Utils::copyYV12toRGB32(width, height, srcPixels, tgtPixels, pTgtDesc->stride); + } else if (static_cast<android_pixel_format_t>(pSrcDesc->format) == + HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV + Utils::copyYUYVtoRGB32(width, height, srcPixels, pSrcDesc->stride, tgtPixels, + pTgtDesc->stride); + } else if (pSrcDesc->format == pTgtDesc->format) { // 32bit RGBA + Utils::copyMatchedInterleavedFormats(width, height, srcPixels, pSrcDesc->stride, + tgtPixels, pTgtDesc->stride, + tgtBuffer.pixelSizeBytes); + } else { + LOG(ERROR) << "Camera buffer format is not supported"; + success = false; + } + } else if (static_cast<android_pixel_format_t>(pTgtDesc->format) == + HAL_PIXEL_FORMAT_BGRA_8888) { + if (static_cast<android_pixel_format_t>(pSrcDesc->format) == + HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21 + Utils::copyNV21toBGR32(width, height, srcPixels, tgtPixels, pTgtDesc->stride); + } else if (static_cast<android_pixel_format_t>(pSrcDesc->format) == + HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12 + Utils::copyYV12toBGR32(width, height, srcPixels, tgtPixels, pTgtDesc->stride); + } else if (static_cast<android_pixel_format_t>(pSrcDesc->format) == + HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV + Utils::copyYUYVtoBGR32(width, height, srcPixels, pSrcDesc->stride, tgtPixels, + pTgtDesc->stride); + } else if (pSrcDesc->format == pTgtDesc->format) { // 32bit RGBA + Utils::copyMatchedInterleavedFormats(width, height, srcPixels, pSrcDesc->stride, + tgtPixels, pTgtDesc->stride, + tgtBuffer.pixelSizeBytes); + } else { + LOG(ERROR) << "Camera buffer format is not supported"; + success = false; + } + } else { + // We always expect 32 bit RGB for the display output for now. Is there a need for 565? + LOG(ERROR) << "Diplay buffer is always expected to be 32bit RGBA"; + success = false; + } + } else { + LOG(ERROR) << "Failed to lock buffer contents for contents transfer"; + success = false; + } + + if (srcPixels) { + src->unlock(); + } + if (tgtPixels) { + tgt->unlock(); + } + + return success; +} + +void FrameHandler::getFrameDimension(unsigned* width, unsigned* height) { + if (width) { + *width = mFrameWidth; + } + + if (height) { + *height = mFrameHeight; + } +} + +bool FrameHandler::waitForEvent(const EvsEventDesc& aTargetEvent, EvsEventDesc& aReceivedEvent, + bool ignorePayload) { + // Wait until we get an expected parameter change event. + std::unique_lock<std::mutex> lock(mEventLock); + auto now = std::chrono::system_clock::now(); + bool found = false; + while (!found) { + bool result = mEventSignal.wait_until( + lock, now + 5s, [this, aTargetEvent, ignorePayload, &aReceivedEvent, &found]() { + found = (mLatestEventDesc.aType == aTargetEvent.aType) && + (ignorePayload || + (mLatestEventDesc.payload[0] == aTargetEvent.payload[0] && + mLatestEventDesc.payload[1] == aTargetEvent.payload[1])); + + aReceivedEvent.aType = mLatestEventDesc.aType; + aReceivedEvent.payload[0] = mLatestEventDesc.payload[0]; + aReceivedEvent.payload[1] = mLatestEventDesc.payload[1]; + return found; + }); + + if (!result) { + LOG(WARNING) << "A timer is expired before a target event has happened."; + break; + } + } + + return found; +} + +const char* FrameHandler::eventToString(const EvsEventType aType) { + switch (aType) { + case EvsEventType::STREAM_STARTED: + return "STREAM_STARTED"; + case EvsEventType::STREAM_STOPPED: + return "STREAM_STOPPED"; + case EvsEventType::FRAME_DROPPED: + return "FRAME_DROPPED"; + case EvsEventType::TIMEOUT: + return "TIMEOUT"; + case EvsEventType::PARAMETER_CHANGED: + return "PARAMETER_CHANGED"; + case EvsEventType::MASTER_RELEASED: + return "MASTER_RELEASED"; + default: + return "Unknown"; + } +} diff --git a/automotive/evs/aidl/vts/FrameHandler.h b/automotive/evs/aidl/vts/FrameHandler.h new file mode 100644 index 0000000000..0b959ab48d --- /dev/null +++ b/automotive/evs/aidl/vts/FrameHandler.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUTOMOTIVE_EVS_VTS_FRAMEHANDLER_H +#define AUTOMOTIVE_EVS_VTS_FRAMEHANDLER_H + +#include <aidl/android/hardware/automotive/evs/BnEvsCameraStream.h> +#include <aidl/android/hardware/automotive/evs/EvsEventDesc.h> +#include <aidl/android/hardware/automotive/evs/IEvsCamera.h> +#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h> + +#include <mutex> +#include <queue> + +/* + * FrameHandler: + * This class can be used to receive camera imagery from an IEvsCamera implementation. Given an + * IEvsDisplay instance at startup, it will forward the received imagery to the display, + * providing a trivial implementation of a rear vew camera type application. + * Note that the video frames are delivered on a background thread, while the control interface + * is actuated from the applications foreground thread. + */ +class FrameHandler : public ::aidl::android::hardware::automotive::evs::BnEvsCameraStream { + public: + enum BufferControlFlag { + eAutoReturn, + eNoAutoReturn, + }; + + FrameHandler( + const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>& pCamera, + const ::aidl::android::hardware::automotive::evs::CameraDesc& cameraInfo, + const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>& + pDisplay, + BufferControlFlag mode = eAutoReturn); + virtual ~FrameHandler() { + if (mCamera != nullptr) { + /* shutdown a camera explicitly */ + shutdown(); + } + } + + void shutdown(); + bool startStream(); + void asyncStopStream(); + void blockingStopStream(); + bool returnHeldBuffer(); + bool isRunning(); + void waitForFrameCount(unsigned frameCount); + bool waitForEvent(const ::aidl::android::hardware::automotive::evs::EvsEventDesc& aTargetEvent, + ::aidl::android::hardware::automotive::evs::EvsEventDesc& aReceivedEvent, + bool ignorePayload = false); + void getFramesCounters(unsigned* received, unsigned* displayed); + void getFrameDimension(unsigned* width, unsigned* height); + + private: + // Methods from ::aidl::android::hardware::automotive::evs::IEvsCameraStream follow. + ::ndk::ScopedAStatus deliverFrame( + const std::vector<::aidl::android::hardware::automotive::evs::BufferDesc>& buffer) + override; + ::ndk::ScopedAStatus notify( + const ::aidl::android::hardware::automotive::evs::EvsEventDesc& event) override; + + // Local implementation details + bool copyBufferContents( + const ::aidl::android::hardware::automotive::evs::BufferDesc& tgtBuffer, + const ::aidl::android::hardware::automotive::evs::BufferDesc& srcBuffer); + const char* eventToString(const ::aidl::android::hardware::automotive::evs::EvsEventType aType); + + std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera> mCamera; + ::aidl::android::hardware::automotive::evs::CameraDesc mCameraInfo; + std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay> mDisplay; + BufferControlFlag mReturnMode; + + // Since we get frames delivered to us asynchronously via the IEvsCameraStream interface, + // we need to protect all member variables that may be modified while we're streaming + // (ie: those below) + std::mutex mLock; + std::mutex mEventLock; + std::condition_variable mEventSignal; + std::condition_variable mFrameSignal; + std::queue<std::vector<::aidl::android::hardware::automotive::evs::BufferDesc>> mHeldBuffers; + + bool mRunning = false; + unsigned mFramesReceived = 0; // Simple counter -- rolls over eventually! + unsigned mFramesDisplayed = 0; // Simple counter -- rolls over eventually! + unsigned mFrameWidth = 0; + unsigned mFrameHeight = 0; + ::aidl::android::hardware::automotive::evs::EvsEventDesc mLatestEventDesc; +}; + +#endif // AUTOMOTIVE_EVS_VTS_FRAMEHANDLER_H diff --git a/automotive/evs/aidl/vts/FrameHandlerUltrasonics.cpp b/automotive/evs/aidl/vts/FrameHandlerUltrasonics.cpp new file mode 100644 index 0000000000..650f0ed501 --- /dev/null +++ b/automotive/evs/aidl/vts/FrameHandlerUltrasonics.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FrameHandlerUltrasonics.h" + +#include <aidl/android/hardware/automotive/evs/EvsEventDesc.h> +#include <aidl/android/hardware/automotive/evs/EvsEventType.h> +#include <aidl/android/hardware/automotive/evs/IEvsUltrasonicsArray.h> +#include <aidl/android/hardware/automotive/evs/UltrasonicsDataFrameDesc.h> +#include <android-base/logging.h> + +using ::aidl::android::hardware::automotive::evs::EvsEventDesc; +using ::aidl::android::hardware::automotive::evs::EvsEventType; +using ::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray; +using ::aidl::android::hardware::automotive::evs::UltrasonicsDataFrameDesc; +using ::ndk::ScopedAStatus; + +namespace { + +// Struct used by SerializeWaveformData(). +struct WaveformData { + uint8_t receiverId; + std::vector<std::pair<float, float>> readings; +}; + +} // namespace + +FrameHandlerUltrasonics::FrameHandlerUltrasonics( + const std::shared_ptr<IEvsUltrasonicsArray>& pArray) + : mEvsUltrasonicsArray(pArray), mReceiveFramesCount(0) { + // Nothing but member initialization +} + +ScopedAStatus FrameHandlerUltrasonics::notify(const EvsEventDesc& evsEvent) { + switch (evsEvent.aType) { + case EvsEventType::STREAM_STARTED: + case EvsEventType::STREAM_STOPPED: + case EvsEventType::FRAME_DROPPED: + case EvsEventType::TIMEOUT: + mReceivedEvents.emplace_back(evsEvent); + break; + default: + LOG(ERROR) << "Received unexpected event"; + } + + return ScopedAStatus::ok(); +} + +// De-serializes shared memory to vector of WaveformData. +// TODO(b/149950362): Add a common library for serializing and deserializing waveform data. +std::vector<WaveformData> DeSerializeWaveformData(std::vector<uint32_t> recvReadingsCountList, + uint8_t* pData) { + std::vector<WaveformData> waveformDataList(recvReadingsCountList.size()); + + for (int i = 0; i < waveformDataList.size(); i++) { + // Set Id + memcpy(&waveformDataList[i].receiverId, pData, sizeof(uint8_t)); + pData += sizeof(uint8_t); + + waveformDataList[i].readings.resize(recvReadingsCountList[i]); + + for (auto& reading : waveformDataList[i].readings) { + // Set the time of flight. + memcpy(&reading.first, pData, sizeof(float)); + pData += sizeof(float); + + // Set the resonance. + memcpy(&reading.second, pData, sizeof(float)); + pData += sizeof(float); + } + } + return waveformDataList; +} + +bool DataFrameValidator(const UltrasonicsDataFrameDesc& /*dataFrameDesc*/) { + // TODO(b/214026378): implement a method to validate an ultrasonics data frame + (void)DeSerializeWaveformData; + return true; +} + +ScopedAStatus FrameHandlerUltrasonics::deliverDataFrame( + const UltrasonicsDataFrameDesc& dataFrameDesc) { + LOG(DEBUG) << "FrameHandlerUltrasonics::receiveFrames"; + + mReceiveFramesCount++; + + if (!DataFrameValidator(dataFrameDesc)) { + mAllFramesValid = false; + } + + // Send done with data frame. + mEvsUltrasonicsArray->doneWithDataFrame(dataFrameDesc); + return ScopedAStatus::ok(); +} + +bool FrameHandlerUltrasonics::checkEventReceived(const EvsEventDesc& evsEvent) { + LOG(DEBUG) << "FrameHandlerUltrasonics::checkEventReceived"; + int size = mReceivedEvents.size(); // work around + LOG(DEBUG) << "Received event number: " << size; + auto iter = find(mReceivedEvents.begin(), mReceivedEvents.end(), evsEvent); + return iter != mReceivedEvents.end(); +} + +int FrameHandlerUltrasonics::getReceiveFramesCount() { + return mReceiveFramesCount; +} + +bool FrameHandlerUltrasonics::areAllFramesValid() { + return mAllFramesValid; +} diff --git a/automotive/evs/aidl/vts/FrameHandlerUltrasonics.h b/automotive/evs/aidl/vts/FrameHandlerUltrasonics.h new file mode 100644 index 0000000000..f853a00a25 --- /dev/null +++ b/automotive/evs/aidl/vts/FrameHandlerUltrasonics.h @@ -0,0 +1,52 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUTOMOTIVE_EVS_VTS_FRAMEHANDLERULTRASONICS_H +#define AUTOMOTIVE_EVS_VTS_FRAMEHANDLERULTRASONICS_H + +#include <aidl/android/hardware/automotive/evs/BnEvsUltrasonicsArrayStream.h> +#include <aidl/android/hardware/automotive/evs/IEvsUltrasonicsArray.h> + +#include <vector> + +class FrameHandlerUltrasonics + : public ::aidl::android::hardware::automotive::evs::BnEvsUltrasonicsArrayStream { + public: + FrameHandlerUltrasonics( + const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>& + pArray); + + // Implementation for ::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArrayStream + ::ndk::ScopedAStatus notify( + const ::aidl::android::hardware::automotive::evs::EvsEventDesc& event) override; + ::ndk::ScopedAStatus deliverDataFrame( + const ::aidl::android::hardware::automotive::evs::UltrasonicsDataFrameDesc& desc) + override; + + bool checkEventReceived( + const ::aidl::android::hardware::automotive::evs::EvsEventDesc& evsEvent); + int getReceiveFramesCount(); + bool areAllFramesValid(); + + private: + std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray> + mEvsUltrasonicsArray; + std::vector<::aidl::android::hardware::automotive::evs::EvsEventDesc> mReceivedEvents; + int mReceiveFramesCount; + bool mAllFramesValid = true; +}; + +#endif // AUTOMOTIVE_EVS_VTS_FRAMEHANDLERULTRASONICS_H diff --git a/automotive/evs/aidl/vts/OWNERS b/automotive/evs/aidl/vts/OWNERS new file mode 100644 index 0000000000..a104f509b2 --- /dev/null +++ b/automotive/evs/aidl/vts/OWNERS @@ -0,0 +1,3 @@ +#Bug component : 853002 +ankitarora@google.com +changyeon@google.com diff --git a/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp new file mode 100644 index 0000000000..c709d40690 --- /dev/null +++ b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp @@ -0,0 +1,2170 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FrameHandler.h" +#include "FrameHandlerUltrasonics.h" + +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> +#include <aidl/android/hardware/automotive/evs/BufferDesc.h> +#include <aidl/android/hardware/automotive/evs/CameraDesc.h> +#include <aidl/android/hardware/automotive/evs/CameraParam.h> +#include <aidl/android/hardware/automotive/evs/DisplayDesc.h> +#include <aidl/android/hardware/automotive/evs/DisplayState.h> +#include <aidl/android/hardware/automotive/evs/EvsEventDesc.h> +#include <aidl/android/hardware/automotive/evs/EvsEventType.h> +#include <aidl/android/hardware/automotive/evs/EvsResult.h> +#include <aidl/android/hardware/automotive/evs/IEvsCamera.h> +#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h> +#include <aidl/android/hardware/automotive/evs/IEvsEnumerator.h> +#include <aidl/android/hardware/automotive/evs/IEvsUltrasonicsArray.h> +#include <aidl/android/hardware/automotive/evs/ParameterRange.h> +#include <aidl/android/hardware/automotive/evs/Stream.h> +#include <aidl/android/hardware/automotive/evs/UltrasonicsArrayDesc.h> +#include <aidl/android/hardware/common/NativeHandle.h> +#include <aidl/android/hardware/graphics/common/HardwareBufferDescription.h> +#include <aidl/android/hardware/graphics/common/PixelFormat.h> +#include <aidlcommonsupport/NativeHandle.h> +#include <android-base/logging.h> +#include <android/binder_ibinder.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> +#include <android/binder_status.h> +#include <system/camera_metadata.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferAllocator.h> +#include <utils/Timers.h> + +#include <deque> +#include <thread> +#include <unordered_set> + +namespace { + +// These values are called out in the EVS design doc (as of Mar 8, 2017) +constexpr int kMaxStreamStartMilliseconds = 500; +constexpr int kMinimumFramesPerSecond = 10; +constexpr int kSecondsToMilliseconds = 1000; +constexpr int kMillisecondsToMicroseconds = 1000; +constexpr float kNanoToMilliseconds = 0.000001f; +constexpr float kNanoToSeconds = 0.000000001f; + +/* + * Please note that this is different from what is defined in + * libhardware/modules/camera/3_4/metadata/types.h; this has one additional + * field to store a framerate. + */ +typedef struct { + int32_t id; + int32_t width; + int32_t height; + int32_t format; + int32_t direction; + int32_t framerate; +} RawStreamConfig; +constexpr size_t kStreamCfgSz = sizeof(RawStreamConfig) / sizeof(int32_t); + +} // namespace + +using ::aidl::android::hardware::automotive::evs::BufferDesc; +using ::aidl::android::hardware::automotive::evs::CameraDesc; +using ::aidl::android::hardware::automotive::evs::CameraParam; +using ::aidl::android::hardware::automotive::evs::DisplayDesc; +using ::aidl::android::hardware::automotive::evs::DisplayState; +using ::aidl::android::hardware::automotive::evs::EvsEventDesc; +using ::aidl::android::hardware::automotive::evs::EvsEventType; +using ::aidl::android::hardware::automotive::evs::EvsResult; +using ::aidl::android::hardware::automotive::evs::IEvsCamera; +using ::aidl::android::hardware::automotive::evs::IEvsDisplay; +using ::aidl::android::hardware::automotive::evs::IEvsEnumerator; +using ::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray; +using ::aidl::android::hardware::automotive::evs::ParameterRange; +using ::aidl::android::hardware::automotive::evs::Stream; +using ::aidl::android::hardware::automotive::evs::UltrasonicsArrayDesc; +using ::aidl::android::hardware::graphics::common::BufferUsage; +using ::aidl::android::hardware::graphics::common::HardwareBufferDescription; +using ::aidl::android::hardware::graphics::common::PixelFormat; +using std::chrono_literals::operator""s; + +// The main test class for EVS +class EvsAidlTest : public ::testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + // Make sure we can connect to the enumerator + std::string service_name = GetParam(); + AIBinder* binder = AServiceManager_waitForService(service_name.data()); + ASSERT_NE(binder, nullptr); + mEnumerator = IEvsEnumerator::fromBinder(::ndk::SpAIBinder(binder)); + LOG(INFO) << "Test target service: " << service_name; + + ASSERT_TRUE(mEnumerator->isHardware(&mIsHwModule).isOk()); + } + + virtual void TearDown() override { + // Attempt to close any active camera + for (auto&& cam : mActiveCameras) { + if (cam != nullptr) { + mEnumerator->closeCamera(cam); + } + } + mActiveCameras.clear(); + } + + protected: + void loadCameraList() { + // SetUp() must run first! + ASSERT_NE(mEnumerator, nullptr); + + // Get the camera list + ASSERT_TRUE(mEnumerator->getCameraList(&mCameraInfo).isOk()) + << "Failed to get a list of available cameras"; + LOG(INFO) << "We have " << mCameraInfo.size() << " cameras."; + } + + void loadUltrasonicsArrayList() { + // SetUp() must run first! + ASSERT_NE(mEnumerator, nullptr); + + // Get the ultrasonics array list + ASSERT_TRUE(mEnumerator->getUltrasonicsArrayList(&mUltrasonicsArraysInfo).isOk()) + << "Failed to get a list of available ultrasonics arrays"; + LOG(INFO) << "We have " << mCameraInfo.size() << " ultrasonics arrays."; + } + + bool isLogicalCamera(const camera_metadata_t* metadata) { + if (metadata == nullptr) { + // A logical camera device must have a valid camera metadata. + return false; + } + + // Looking for LOGICAL_MULTI_CAMERA capability from metadata. + camera_metadata_ro_entry_t entry; + int rc = find_camera_metadata_ro_entry(metadata, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, + &entry); + if (rc != 0) { + // No capabilities are found. + return false; + } + + for (size_t i = 0; i < entry.count; ++i) { + uint8_t cap = entry.data.u8[i]; + if (cap == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) { + return true; + } + } + + return false; + } + + std::unordered_set<std::string> getPhysicalCameraIds(const std::string& id, bool& flag) { + std::unordered_set<std::string> physicalCameras; + const auto it = std::find_if(mCameraInfo.begin(), mCameraInfo.end(), + [&id](const CameraDesc& desc) { return id == desc.id; }); + if (it == mCameraInfo.end()) { + // Unknown camera is requested. Return an empty list. + return physicalCameras; + } + + const camera_metadata_t* metadata = reinterpret_cast<camera_metadata_t*>(&it->metadata[0]); + flag = isLogicalCamera(metadata); + if (!flag) { + // EVS assumes that the device w/o a valid metadata is a physical + // device. + LOG(INFO) << id << " is not a logical camera device."; + physicalCameras.insert(id); + return physicalCameras; + } + + // Look for physical camera identifiers + camera_metadata_ro_entry entry; + int rc = find_camera_metadata_ro_entry(metadata, ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS, + &entry); + if (rc != 0) { + LOG(ERROR) << "No physical camera ID is found for a logical camera device"; + } + + const uint8_t* ids = entry.data.u8; + size_t start = 0; + for (size_t i = 0; i < entry.count; ++i) { + if (ids[i] == '\0') { + if (start != i) { + std::string id(reinterpret_cast<const char*>(ids + start)); + physicalCameras.insert(id); + } + start = i + 1; + } + } + + LOG(INFO) << id << " consists of " << physicalCameras.size() << " physical camera devices"; + return physicalCameras; + } + + Stream getFirstStreamConfiguration(camera_metadata_t* metadata) { + Stream targetCfg = {}; + camera_metadata_entry_t streamCfgs; + if (!find_camera_metadata_entry(metadata, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + &streamCfgs)) { + // Stream configurations are found in metadata + RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32); + for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) { + if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT && + ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) { + targetCfg.width = ptr->width; + targetCfg.height = ptr->height; + targetCfg.format = static_cast<PixelFormat>(ptr->format); + break; + } + ++ptr; + } + } + + return targetCfg; + } + + // Every test needs access to the service + std::shared_ptr<IEvsEnumerator> mEnumerator; + // Empty unless/util loadCameraList() is called + std::vector<CameraDesc> mCameraInfo; + // boolean to tell current module under testing is HW module implementation + // or not + bool mIsHwModule; + // A list of active camera handles that are need to be cleaned up + std::deque<std::shared_ptr<IEvsCamera>> mActiveCameras; + // Empty unless/util loadUltrasonicsArrayList() is called + std::vector<UltrasonicsArrayDesc> mUltrasonicsArraysInfo; + // A list of active ultrasonics array handles that are to be cleaned up + std::deque<std::weak_ptr<IEvsUltrasonicsArray>> mActiveUltrasonicsArrays; +}; + +// Test cases, their implementations, and corresponding requirements are +// documented at go/aae-evs-public-api-test. + +/* + * CameraOpenClean: + * Opens each camera reported by the enumerator and then explicitly closes it via a + * call to closeCamera. Then repeats the test to ensure all cameras can be reopened. + */ +TEST_P(EvsAidlTest, CameraOpenClean) { + LOG(INFO) << "Starting CameraOpenClean test"; + + // Get the camera list + loadCameraList(); + + // Open and close each camera twice + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + auto devices = getPhysicalCameraIds(cam.id, isLogicalCam); + if (mIsHwModule && isLogicalCam) { + LOG(INFO) << "Skip a logical device, " << cam.id << " for HW target."; + continue; + } + + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + for (int pass = 0; pass < 2; pass++) { + std::shared_ptr<IEvsCamera> pCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk()); + ASSERT_NE(pCam, nullptr); + + CameraDesc cameraInfo; + for (auto&& devName : devices) { + ASSERT_TRUE(pCam->getPhysicalCameraInfo(devName, &cameraInfo).isOk()); + EXPECT_EQ(devName, cameraInfo.id); + } + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam); + + // Verify that this camera self-identifies correctly + ASSERT_TRUE(pCam->getCameraInfo(&cameraInfo).isOk()); + EXPECT_EQ(cam.id, cameraInfo.id); + + // Verify methods for extended info + const auto id = 0xFFFFFFFF; // meaningless id + std::vector<uint8_t> values; + auto status = pCam->setExtendedInfo(id, values); + if (isLogicalCam) { + EXPECT_TRUE(!status.isOk() && status.getServiceSpecificError() == + static_cast<int>(EvsResult::NOT_SUPPORTED)); + } else { + EXPECT_TRUE(status.isOk()); + } + + status = pCam->getExtendedInfo(id, &values); + if (isLogicalCam) { + EXPECT_TRUE(!status.isOk() && status.getServiceSpecificError() == + static_cast<int>(EvsResult::NOT_SUPPORTED)); + } else { + EXPECT_TRUE(status.isOk()); + } + + // Explicitly close the camera so resources are released right away + ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk()); + mActiveCameras.clear(); + } + } +} + +/* + * CameraOpenAggressive: + * Opens each camera reported by the enumerator twice in a row without an intervening closeCamera + * call. This ensures that the intended "aggressive open" behavior works. This is necessary for + * the system to be tolerant of shutdown/restart race conditions. + */ +TEST_P(EvsAidlTest, CameraOpenAggressive) { + LOG(INFO) << "Starting CameraOpenAggressive test"; + + // Get the camera list + loadCameraList(); + + // Open and close each camera twice + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + getPhysicalCameraIds(cam.id, isLogicalCam); + if (mIsHwModule && isLogicalCam) { + LOG(INFO) << "Skip a logical device, " << cam.id << " for HW target."; + continue; + } + + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + mActiveCameras.clear(); + std::shared_ptr<IEvsCamera> pCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk()); + EXPECT_NE(pCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam); + + // Verify that this camera self-identifies correctly + CameraDesc cameraInfo; + ASSERT_TRUE(pCam->getCameraInfo(&cameraInfo).isOk()); + EXPECT_EQ(cam.id, cameraInfo.id); + + std::shared_ptr<IEvsCamera> pCam2; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam2).isOk()); + EXPECT_NE(pCam2, nullptr); + EXPECT_NE(pCam, pCam2); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam2); + + auto status = pCam->setMaxFramesInFlight(2); + if (mIsHwModule) { + // Verify that the old camera rejects calls via HW module. + EXPECT_TRUE(!status.isOk() && status.getServiceSpecificError() == + static_cast<int>(EvsResult::OWNERSHIP_LOST)); + } else { + // default implementation supports multiple clients. + EXPECT_TRUE(status.isOk()); + } + + // Close the superseded camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk()); + mActiveCameras.pop_front(); + + // Verify that the second camera instance self-identifies correctly + ASSERT_TRUE(pCam2->getCameraInfo(&cameraInfo).isOk()); + EXPECT_EQ(cam.id, cameraInfo.id); + + // Close the second camera instance + ASSERT_TRUE(mEnumerator->closeCamera(pCam2).isOk()); + mActiveCameras.pop_front(); + } + + // Sleep here to ensure the destructor cleanup has time to run so we don't break follow on tests + sleep(1); // I hate that this is an arbitrary time to wait. :( b/36122635 +} + +/* + * CameraStreamPerformance: + * Measure and qualify the stream start up time and streaming frame rate of each reported camera + */ +TEST_P(EvsAidlTest, CameraStreamPerformance) { + LOG(INFO) << "Starting CameraStreamPerformance test"; + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + auto devices = getPhysicalCameraIds(cam.id, isLogicalCam); + if (mIsHwModule && isLogicalCam) { + LOG(INFO) << "Skip a logical device " << cam.id; + continue; + } + + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + std::shared_ptr<IEvsCamera> pCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk()); + EXPECT_NE(pCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam); + + // Set up a frame receiver object which will fire up its own thread + std::shared_ptr<FrameHandler> frameHandler = + std::make_shared<FrameHandler>(pCam, cam, nullptr, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandler, nullptr); + + // Start the camera's video stream + nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC); + ASSERT_TRUE(frameHandler->startStream()); + + // Ensure the first frame arrived within the expected time + frameHandler->waitForFrameCount(1); + nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t timeToFirstFrame = systemTime(SYSTEM_TIME_MONOTONIC) - start; + + // Extra delays are expected when we attempt to start a video stream on + // the logical camera device. The amount of delay is expected the + // number of physical camera devices multiplied by + // kMaxStreamStartMilliseconds at most. + EXPECT_LE(nanoseconds_to_milliseconds(timeToFirstFrame), + kMaxStreamStartMilliseconds * devices.size()); + printf("%s: Measured time to first frame %0.2f ms\n", cam.id.data(), + timeToFirstFrame * kNanoToMilliseconds); + LOG(INFO) << cam.id << ": Measured time to first frame " << std::scientific + << timeToFirstFrame * kNanoToMilliseconds << " ms."; + + // Check aspect ratio + unsigned width = 0, height = 0; + frameHandler->getFrameDimension(&width, &height); + EXPECT_GE(width, height); + + // Wait a bit, then ensure we get at least the required minimum number of frames + sleep(5); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); + + // Even when the camera pointer goes out of scope, the FrameHandler object will + // keep the stream alive unless we tell it to shutdown. + // Also note that the FrameHandle and the Camera have a mutual circular reference, so + // we have to break that cycle in order for either of them to get cleaned up. + frameHandler->shutdown(); + + unsigned framesReceived = 0; + frameHandler->getFramesCounters(&framesReceived, nullptr); + framesReceived = framesReceived - 1; // Back out the first frame we already waited for + nsecs_t runTime = end - firstFrame; + float framesPerSecond = framesReceived / (runTime * kNanoToSeconds); + printf("Measured camera rate %3.2f fps\n", framesPerSecond); + LOG(INFO) << "Measured camera rate " << std::scientific << framesPerSecond << " fps."; + EXPECT_GE(framesPerSecond, kMinimumFramesPerSecond); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk()); + mActiveCameras.clear(); + } +} + +/* + * CameraStreamBuffering: + * Ensure the camera implementation behaves properly when the client holds onto buffers for more + * than one frame time. The camera must cleanly skip frames until the client is ready again. + */ +TEST_P(EvsAidlTest, CameraStreamBuffering) { + LOG(INFO) << "Starting CameraStreamBuffering test"; + + // Arbitrary constant (should be > 1 and not too big) + static const unsigned int kBuffersToHold = 6; + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + getPhysicalCameraIds(cam.id, isLogicalCam); + if (mIsHwModule && isLogicalCam) { + LOG(INFO) << "Skip a logical device " << cam.id << " for HW target."; + continue; + } + + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + std::shared_ptr<IEvsCamera> pCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk()); + EXPECT_NE(pCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam); + + // Ask for a very large number of buffers in flight to ensure it errors correctly + auto badResult = pCam->setMaxFramesInFlight(0xFFFFFFFF); + EXPECT_TRUE(!badResult.isOk() && badResult.getServiceSpecificError() == + static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE)); + + // Now ask for exactly two buffers in flight as we'll test behavior in that case + ASSERT_TRUE(pCam->setMaxFramesInFlight(kBuffersToHold).isOk()); + + // Set up a frame receiver object which will fire up its own thread. + std::shared_ptr<FrameHandler> frameHandler = + std::make_shared<FrameHandler>(pCam, cam, nullptr, FrameHandler::eNoAutoReturn); + EXPECT_NE(frameHandler, nullptr); + + // Start the camera's video stream + ASSERT_TRUE(frameHandler->startStream()); + + // Check that the video stream stalls once we've gotten exactly the number of buffers + // we requested since we told the frameHandler not to return them. + 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"; + + // Give back one buffer + ASSERT_TRUE(frameHandler->returnHeldBuffer()); + + // Once we return a buffer, it shouldn't take more than 1/10 second to get a new one + // 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"; + + // Even when the camera pointer goes out of scope, the FrameHandler object will + // keep the stream alive unless we tell it to shutdown. + // Also note that the FrameHandle and the Camera have a mutual circular reference, so + // we have to break that cycle in order for either of them to get cleaned up. + frameHandler->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk()); + mActiveCameras.clear(); + } +} + +/* + * CameraToDisplayRoundTrip: + * End to end test of data flowing from the camera to the display. Each delivered frame of camera + * imagery is simply copied to the display buffer and presented on screen. This is the one test + * which a human could observe to see the operation of the system on the physical display. + */ +TEST_P(EvsAidlTest, CameraToDisplayRoundTrip) { + LOG(INFO) << "Starting CameraToDisplayRoundTrip test"; + + // Get the camera list + loadCameraList(); + + // Request available display IDs + uint8_t targetDisplayId = 0; + std::vector<uint8_t> displayIds; + ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk()); + EXPECT_GT(displayIds.size(), 0); + targetDisplayId = displayIds[0]; + + // Request exclusive access to the first EVS display + std::shared_ptr<IEvsDisplay> pDisplay; + ASSERT_TRUE(mEnumerator->openDisplay(targetDisplayId, &pDisplay).isOk()); + EXPECT_NE(pDisplay, nullptr); + LOG(INFO) << "Display " << targetDisplayId << " is in use."; + + // Get the display descriptor + DisplayDesc displayDesc; + ASSERT_TRUE(pDisplay->getDisplayInfo(&displayDesc).isOk()); + LOG(INFO) << " Resolution: " << displayDesc.width << "x" << displayDesc.height; + ASSERT_GT(displayDesc.width, 0); + ASSERT_GT(displayDesc.height, 0); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + getPhysicalCameraIds(cam.id, isLogicalCam); + if (mIsHwModule && isLogicalCam) { + LOG(INFO) << "Skip a logical device " << cam.id << " for HW target."; + continue; + } + + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + std::shared_ptr<IEvsCamera> pCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk()); + EXPECT_NE(pCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam); + + // Set up a frame receiver object which will fire up its own thread. + std::shared_ptr<FrameHandler> frameHandler = + std::make_shared<FrameHandler>(pCam, cam, pDisplay, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandler, nullptr); + + // Activate the display + ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME).isOk()); + + // Start the camera's video stream + ASSERT_TRUE(frameHandler->startStream()); + + // Wait a while to let the data flow + static const int kSecondsToWait = 5; + const int streamTimeMs = + kSecondsToWait * kSecondsToMilliseconds - kMaxStreamStartMilliseconds; + const unsigned minimumFramesExpected = + streamTimeMs * kMinimumFramesPerSecond / kSecondsToMilliseconds; + sleep(kSecondsToWait); + unsigned framesReceived = 0; + unsigned framesDisplayed = 0; + frameHandler->getFramesCounters(&framesReceived, &framesDisplayed); + EXPECT_EQ(framesReceived, framesDisplayed); + EXPECT_GE(framesDisplayed, minimumFramesExpected); + + // Turn off the display (yes, before the stream stops -- it should be handled) + ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::NOT_VISIBLE).isOk()); + + // Shut down the streamer + frameHandler->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk()); + mActiveCameras.clear(); + } + + // Explicitly release the display + ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk()); +} + +/* + * MultiCameraStream: + * Verify that each client can start and stop video streams on the same + * underlying camera. + */ +TEST_P(EvsAidlTest, MultiCameraStream) { + LOG(INFO) << "Starting MultiCameraStream test"; + + if (mIsHwModule) { + // This test is not for HW module implementation. + return; + } + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + // Create two camera clients. + std::shared_ptr<IEvsCamera> pCam0; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam0).isOk()); + EXPECT_NE(pCam0, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam0); + + std::shared_ptr<IEvsCamera> pCam1; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam1).isOk()); + EXPECT_NE(pCam1, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam1); + + // Set up per-client frame receiver objects which will fire up its own thread + std::shared_ptr<FrameHandler> frameHandler0 = + std::make_shared<FrameHandler>(pCam0, cam, nullptr, FrameHandler::eAutoReturn); + std::shared_ptr<FrameHandler> frameHandler1 = + std::make_shared<FrameHandler>(pCam1, cam, nullptr, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandler0, nullptr); + EXPECT_NE(frameHandler1, nullptr); + + // Start the camera's video stream via client 0 + ASSERT_TRUE(frameHandler0->startStream()); + ASSERT_TRUE(frameHandler1->startStream()); + + // Ensure the stream starts + frameHandler0->waitForFrameCount(1); + frameHandler1->waitForFrameCount(1); + + nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC); + + // Wait a bit, then ensure both clients get at least the required minimum number of frames + sleep(5); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); + unsigned framesReceived0 = 0, framesReceived1 = 0; + frameHandler0->getFramesCounters(&framesReceived0, nullptr); + frameHandler1->getFramesCounters(&framesReceived1, nullptr); + framesReceived0 = framesReceived0 - 1; // Back out the first frame we already waited for + framesReceived1 = framesReceived1 - 1; // Back out the first frame we already waited for + nsecs_t runTime = end - firstFrame; + float framesPerSecond0 = framesReceived0 / (runTime * kNanoToSeconds); + float framesPerSecond1 = framesReceived1 / (runTime * kNanoToSeconds); + LOG(INFO) << "Measured camera rate " << std::scientific << framesPerSecond0 << " fps and " + << framesPerSecond1 << " fps"; + EXPECT_GE(framesPerSecond0, kMinimumFramesPerSecond); + EXPECT_GE(framesPerSecond1, kMinimumFramesPerSecond); + + // Shutdown one client + frameHandler0->shutdown(); + + // Read frame counters again + frameHandler0->getFramesCounters(&framesReceived0, nullptr); + frameHandler1->getFramesCounters(&framesReceived1, nullptr); + + // Wait a bit again + sleep(5); + unsigned framesReceivedAfterStop0 = 0, framesReceivedAfterStop1 = 0; + frameHandler0->getFramesCounters(&framesReceivedAfterStop0, nullptr); + frameHandler1->getFramesCounters(&framesReceivedAfterStop1, nullptr); + EXPECT_EQ(framesReceived0, framesReceivedAfterStop0); + EXPECT_LT(framesReceived1, framesReceivedAfterStop1); + + // Shutdown another + frameHandler1->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam0).isOk()); + ASSERT_TRUE(mEnumerator->closeCamera(pCam1).isOk()); + mActiveCameras.clear(); + + // TODO(b/145459970, b/145457727): below sleep() is added to ensure the + // destruction of active camera objects; this may be related with two + // issues. + sleep(1); + } +} + +/* + * CameraParameter: + * Verify that a client can adjust a camera parameter. + */ +TEST_P(EvsAidlTest, CameraParameter) { + LOG(INFO) << "Starting CameraParameter test"; + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + getPhysicalCameraIds(cam.id, isLogicalCam); + if (isLogicalCam) { + // TODO(b/145465724): Support camera parameter programming on + // logical devices. + LOG(INFO) << "Skip a logical device " << cam.id; + continue; + } + + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + // Create a camera client + std::shared_ptr<IEvsCamera> pCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk()); + EXPECT_NE(pCam, nullptr); + + // Store a camera + mActiveCameras.push_back(pCam); + + // Get the parameter list + std::vector<CameraParam> cmds; + ASSERT_TRUE(pCam->getParameterList(&cmds).isOk()); + if (cmds.size() < 1) { + continue; + } + + // Set up per-client frame receiver objects which will fire up its own thread + std::shared_ptr<FrameHandler> frameHandler = + std::make_shared<FrameHandler>(pCam, cam, nullptr, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandler, nullptr); + + // Start the camera's video stream + ASSERT_TRUE(frameHandler->startStream()); + + // Ensure the stream starts + frameHandler->waitForFrameCount(1); + + // Set current client is the primary client + ASSERT_TRUE(pCam->setPrimaryClient().isOk()); + for (auto& cmd : cmds) { + // Get a valid parameter value range + ParameterRange range; + ASSERT_TRUE(pCam->getIntParameterRange(cmd, &range).isOk()); + + std::vector<int32_t> values; + if (cmd == CameraParam::ABSOLUTE_FOCUS) { + // Try to turn off auto-focus + ASSERT_TRUE(pCam->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(v, 0); + } + } + + // Try to program a parameter with a random value [minVal, maxVal] + int32_t val0 = range.min + (std::rand() % (range.max - range.min)); + + // Rounding down + val0 = val0 - (val0 % range.step); + values.clear(); + ASSERT_TRUE(pCam->setIntParameter(cmd, val0, &values).isOk()); + + values.clear(); + ASSERT_TRUE(pCam->getIntParameter(cmd, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(val0, v) << "Values are not matched."; + } + } + ASSERT_TRUE(pCam->unsetPrimaryClient().isOk()); + + // Shutdown + frameHandler->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk()); + mActiveCameras.clear(); + } +} + +/* + * CameraPrimaryClientRelease + * Verify that non-primary client gets notified when the primary client either + * terminates or releases a role. + */ +TEST_P(EvsAidlTest, CameraPrimaryClientRelease) { + LOG(INFO) << "Starting CameraPrimaryClientRelease test"; + + if (mIsHwModule) { + // This test is not for HW module implementation. + return; + } + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + getPhysicalCameraIds(cam.id, isLogicalCam); + if (isLogicalCam) { + // TODO(b/145465724): Support camera parameter programming on + // logical devices. + LOG(INFO) << "Skip a logical device " << cam.id; + continue; + } + + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + // Create two camera clients. + std::shared_ptr<IEvsCamera> pPrimaryCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pPrimaryCam).isOk()); + EXPECT_NE(pPrimaryCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pPrimaryCam); + + std::shared_ptr<IEvsCamera> pSecondaryCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pSecondaryCam).isOk()); + EXPECT_NE(pSecondaryCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pSecondaryCam); + + // Set up per-client frame receiver objects which will fire up its own thread + std::shared_ptr<FrameHandler> frameHandlerPrimary = std::make_shared<FrameHandler>( + pPrimaryCam, cam, nullptr, FrameHandler::eAutoReturn); + std::shared_ptr<FrameHandler> frameHandlerSecondary = std::make_shared<FrameHandler>( + pSecondaryCam, cam, nullptr, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandlerPrimary, nullptr); + EXPECT_NE(frameHandlerSecondary, nullptr); + + // Set one client as the primary client + ASSERT_TRUE(pPrimaryCam->setPrimaryClient().isOk()); + + // Try to set another client as the primary client. + ASSERT_FALSE(pSecondaryCam->setPrimaryClient().isOk()); + + // Start the camera's video stream via a primary client client. + ASSERT_TRUE(frameHandlerPrimary->startStream()); + + // Ensure the stream starts + frameHandlerPrimary->waitForFrameCount(1); + + // Start the camera's video stream via another client + ASSERT_TRUE(frameHandlerSecondary->startStream()); + + // Ensure the stream starts + frameHandlerSecondary->waitForFrameCount(1); + + // Non-primary client expects to receive a primary client role relesed + // notification. + EvsEventDesc aTargetEvent = {}; + EvsEventDesc aNotification = {}; + + bool listening = false; + std::mutex eventLock; + std::condition_variable eventCond; + std::thread listener = + std::thread([&aNotification, &frameHandlerSecondary, &listening, &eventCond]() { + // Notify that a listening thread is running. + listening = true; + eventCond.notify_all(); + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::MASTER_RELEASED; + if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification, true)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a listening thread starts. + std::unique_lock<std::mutex> lock(eventLock); + auto timer = std::chrono::system_clock::now(); + while (!listening) { + timer += 1s; + eventCond.wait_until(lock, timer); + } + lock.unlock(); + + // Release a primary client role. + ASSERT_TRUE(pPrimaryCam->unsetPrimaryClient().isOk()); + + // Join a listening thread. + if (listener.joinable()) { + listener.join(); + } + + // Verify change notifications. + ASSERT_EQ(EvsEventType::MASTER_RELEASED, static_cast<EvsEventType>(aNotification.aType)); + + // Non-primary becomes a primary client. + ASSERT_TRUE(pSecondaryCam->setPrimaryClient().isOk()); + + // Previous primary client fails to become a primary client. + ASSERT_FALSE(pPrimaryCam->setPrimaryClient().isOk()); + + listening = false; + listener = std::thread([&aNotification, &frameHandlerPrimary, &listening, &eventCond]() { + // Notify that a listening thread is running. + listening = true; + eventCond.notify_all(); + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::MASTER_RELEASED; + if (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification, true)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a listening thread starts. + timer = std::chrono::system_clock::now(); + lock.lock(); + while (!listening) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + // Closing current primary client. + frameHandlerSecondary->shutdown(); + + // Join a listening thread. + if (listener.joinable()) { + listener.join(); + } + + // Verify change notifications. + ASSERT_EQ(EvsEventType::MASTER_RELEASED, static_cast<EvsEventType>(aNotification.aType)); + + // Closing streams. + frameHandlerPrimary->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pPrimaryCam).isOk()); + ASSERT_TRUE(mEnumerator->closeCamera(pSecondaryCam).isOk()); + mActiveCameras.clear(); + } +} + +/* + * MultiCameraParameter: + * Verify that primary and non-primary clients behave as expected when they try to adjust + * camera parameters. + */ +TEST_P(EvsAidlTest, MultiCameraParameter) { + LOG(INFO) << "Starting MultiCameraParameter test"; + + if (mIsHwModule) { + // This test is not for HW module implementation. + return; + } + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + getPhysicalCameraIds(cam.id, isLogicalCam); + if (isLogicalCam) { + // TODO(b/145465724): Support camera parameter programming on + // logical devices. + LOG(INFO) << "Skip a logical device " << cam.id; + continue; + } + + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + // Create two camera clients. + std::shared_ptr<IEvsCamera> pPrimaryCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pPrimaryCam).isOk()); + EXPECT_NE(pPrimaryCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pPrimaryCam); + + std::shared_ptr<IEvsCamera> pSecondaryCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pSecondaryCam).isOk()); + EXPECT_NE(pSecondaryCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pSecondaryCam); + + // Get the parameter list + std::vector<CameraParam> camPrimaryCmds, camSecondaryCmds; + ASSERT_TRUE(pPrimaryCam->getParameterList(&camPrimaryCmds).isOk()); + ASSERT_TRUE(pSecondaryCam->getParameterList(&camSecondaryCmds).isOk()); + if (camPrimaryCmds.size() < 1 || camSecondaryCmds.size() < 1) { + // Skip a camera device if it does not support any parameter. + continue; + } + + // Set up per-client frame receiver objects which will fire up its own thread + std::shared_ptr<FrameHandler> frameHandlerPrimary = std::make_shared<FrameHandler>( + pPrimaryCam, cam, nullptr, FrameHandler::eAutoReturn); + std::shared_ptr<FrameHandler> frameHandlerSecondary = std::make_shared<FrameHandler>( + pSecondaryCam, cam, nullptr, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandlerPrimary, nullptr); + EXPECT_NE(frameHandlerSecondary, nullptr); + + // Set one client as the primary client. + ASSERT_TRUE(pPrimaryCam->setPrimaryClient().isOk()); + + // Try to set another client as the primary client. + ASSERT_FALSE(pSecondaryCam->setPrimaryClient().isOk()); + + // Start the camera's video stream via a primary client client. + ASSERT_TRUE(frameHandlerPrimary->startStream()); + + // Ensure the stream starts + frameHandlerPrimary->waitForFrameCount(1); + + // Start the camera's video stream via another client + ASSERT_TRUE(frameHandlerSecondary->startStream()); + + // Ensure the stream starts + frameHandlerSecondary->waitForFrameCount(1); + + int32_t val0 = 0; + std::vector<int32_t> values; + EvsEventDesc aNotification0 = {}; + EvsEventDesc aNotification1 = {}; + for (auto& cmd : camPrimaryCmds) { + // Get a valid parameter value range + ParameterRange range; + ASSERT_TRUE(pPrimaryCam->getIntParameterRange(cmd, &range).isOk()); + if (cmd == CameraParam::ABSOLUTE_FOCUS) { + // Try to turn off auto-focus + values.clear(); + ASSERT_TRUE( + pPrimaryCam->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(v, 0); + } + } + + // Calculate a parameter value to program. + val0 = range.min + (std::rand() % (range.max - range.min)); + val0 = val0 - (val0 % range.step); + + // Prepare and start event listeners. + bool listening0 = false; + bool listening1 = false; + std::condition_variable eventCond; + std::thread listener0 = std::thread([cmd, val0, &aNotification0, &frameHandlerPrimary, + &listening0, &listening1, &eventCond]() { + listening0 = true; + if (listening1) { + eventCond.notify_all(); + } + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED; + aTargetEvent.payload[0] = static_cast<uint32_t>(cmd); + aTargetEvent.payload[1] = val0; + if (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification0)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + std::thread listener1 = std::thread([cmd, val0, &aNotification1, &frameHandlerSecondary, + &listening0, &listening1, &eventCond]() { + listening1 = true; + if (listening0) { + eventCond.notify_all(); + } + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED; + aTargetEvent.payload[0] = static_cast<uint32_t>(cmd); + aTargetEvent.payload[1] = val0; + if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification1)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a listening thread starts. + std::mutex eventLock; + std::unique_lock<std::mutex> lock(eventLock); + auto timer = std::chrono::system_clock::now(); + while (!listening0 || !listening1) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + // Try to program a parameter + values.clear(); + ASSERT_TRUE(pPrimaryCam->setIntParameter(cmd, val0, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(val0, v) << "Values are not matched."; + } + + // Join a listening thread. + if (listener0.joinable()) { + listener0.join(); + } + if (listener1.joinable()) { + listener1.join(); + } + + // Verify a change notification + ASSERT_EQ(EvsEventType::PARAMETER_CHANGED, + static_cast<EvsEventType>(aNotification0.aType)); + ASSERT_EQ(EvsEventType::PARAMETER_CHANGED, + static_cast<EvsEventType>(aNotification1.aType)); + ASSERT_EQ(cmd, static_cast<CameraParam>(aNotification0.payload[0])); + ASSERT_EQ(cmd, static_cast<CameraParam>(aNotification1.payload[0])); + for (auto&& v : values) { + ASSERT_EQ(v, static_cast<int32_t>(aNotification0.payload[1])); + ASSERT_EQ(v, static_cast<int32_t>(aNotification1.payload[1])); + } + + // Clients expects to receive a parameter change notification + // whenever a primary client client adjusts it. + values.clear(); + ASSERT_TRUE(pPrimaryCam->getIntParameter(cmd, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(val0, v) << "Values are not matched."; + } + } + + // Try to adjust a parameter via non-primary client + values.clear(); + ASSERT_FALSE(pSecondaryCam->setIntParameter(camSecondaryCmds[0], val0, &values).isOk()); + + // Non-primary client attempts to be a primary client + ASSERT_FALSE(pSecondaryCam->setPrimaryClient().isOk()); + + // Primary client retires from a primary client role + bool listening = false; + std::condition_variable eventCond; + std::thread listener = + std::thread([&aNotification0, &frameHandlerSecondary, &listening, &eventCond]() { + listening = true; + eventCond.notify_all(); + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::MASTER_RELEASED; + if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification0, true)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + std::mutex eventLock; + auto timer = std::chrono::system_clock::now(); + std::unique_lock<std::mutex> lock(eventLock); + while (!listening) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + ASSERT_TRUE(pPrimaryCam->unsetPrimaryClient().isOk()); + + if (listener.joinable()) { + listener.join(); + } + ASSERT_EQ(EvsEventType::MASTER_RELEASED, static_cast<EvsEventType>(aNotification0.aType)); + + // Try to adjust a parameter after being retired + values.clear(); + ASSERT_FALSE(pPrimaryCam->setIntParameter(camPrimaryCmds[0], val0, &values).isOk()); + + // Non-primary client becomes a primary client + ASSERT_TRUE(pSecondaryCam->setPrimaryClient().isOk()); + + // Try to adjust a parameter via new primary client + for (auto& cmd : camSecondaryCmds) { + // Get a valid parameter value range + ParameterRange range; + ASSERT_TRUE(pSecondaryCam->getIntParameterRange(cmd, &range).isOk()); + + values.clear(); + if (cmd == CameraParam::ABSOLUTE_FOCUS) { + // Try to turn off auto-focus + values.clear(); + ASSERT_TRUE( + pSecondaryCam->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(v, 0); + } + } + + // Calculate a parameter value to program. This is being rounding down. + val0 = range.min + (std::rand() % (range.max - range.min)); + val0 = val0 - (val0 % range.step); + + // Prepare and start event listeners. + bool listening0 = false; + bool listening1 = false; + std::condition_variable eventCond; + std::thread listener0 = std::thread([&]() { + listening0 = true; + if (listening1) { + eventCond.notify_all(); + } + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED; + aTargetEvent.payload[0] = static_cast<uint32_t>(cmd); + aTargetEvent.payload[1] = val0; + if (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification0)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + std::thread listener1 = std::thread([&]() { + listening1 = true; + if (listening0) { + eventCond.notify_all(); + } + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED; + aTargetEvent.payload[0] = static_cast<uint32_t>(cmd); + aTargetEvent.payload[1] = val0; + if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification1)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a listening thread starts. + std::mutex eventLock; + std::unique_lock<std::mutex> lock(eventLock); + auto timer = std::chrono::system_clock::now(); + while (!listening0 || !listening1) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + // Try to program a parameter + values.clear(); + ASSERT_TRUE(pSecondaryCam->setIntParameter(cmd, val0, &values).isOk()); + + // Clients expects to receive a parameter change notification + // whenever a primary client client adjusts it. + values.clear(); + ASSERT_TRUE(pSecondaryCam->getIntParameter(cmd, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(val0, v) << "Values are not matched."; + } + + // Join a listening thread. + if (listener0.joinable()) { + listener0.join(); + } + if (listener1.joinable()) { + listener1.join(); + } + + // Verify a change notification + ASSERT_EQ(EvsEventType::PARAMETER_CHANGED, + static_cast<EvsEventType>(aNotification0.aType)); + ASSERT_EQ(EvsEventType::PARAMETER_CHANGED, + static_cast<EvsEventType>(aNotification1.aType)); + ASSERT_EQ(cmd, static_cast<CameraParam>(aNotification0.payload[0])); + ASSERT_EQ(cmd, static_cast<CameraParam>(aNotification1.payload[0])); + for (auto&& v : values) { + ASSERT_EQ(v, static_cast<int32_t>(aNotification0.payload[1])); + ASSERT_EQ(v, static_cast<int32_t>(aNotification1.payload[1])); + } + } + + // New primary client retires from the role + ASSERT_TRUE(pSecondaryCam->unsetPrimaryClient().isOk()); + + // Shutdown + frameHandlerPrimary->shutdown(); + frameHandlerSecondary->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pPrimaryCam).isOk()); + ASSERT_TRUE(mEnumerator->closeCamera(pSecondaryCam).isOk()); + mActiveCameras.clear(); + } +} + +/* + * HighPriorityCameraClient: + * EVS client, which owns the display, is priortized and therefore can take over + * a primary client role from other EVS clients without the display. + */ +TEST_P(EvsAidlTest, HighPriorityCameraClient) { + LOG(INFO) << "Starting HighPriorityCameraClient test"; + + if (mIsHwModule) { + // This test is not for HW module implementation. + return; + } + + // Get the camera list + loadCameraList(); + + // Request available display IDs + uint8_t targetDisplayId = 0; + std::vector<uint8_t> displayIds; + ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk()); + EXPECT_GT(displayIds.size(), 0); + targetDisplayId = displayIds[0]; + + // Request exclusive access to the EVS display + std::shared_ptr<IEvsDisplay> pDisplay; + ASSERT_TRUE(mEnumerator->openDisplay(targetDisplayId, &pDisplay).isOk()); + EXPECT_NE(pDisplay, nullptr); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + // Create two clients + std::shared_ptr<IEvsCamera> pCam0; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam0).isOk()); + EXPECT_NE(pCam0, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam0); + + std::shared_ptr<IEvsCamera> pCam1; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam1).isOk()); + EXPECT_NE(pCam1, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam1); + + // Get the parameter list; this test will use the first command in both + // lists. + std::vector<CameraParam> cam0Cmds, cam1Cmds; + ASSERT_TRUE(pCam0->getParameterList(&cam0Cmds).isOk()); + ASSERT_TRUE(pCam1->getParameterList(&cam1Cmds).isOk()); + if (cam0Cmds.size() < 1 || cam1Cmds.size() < 1) { + // Cannot execute this test. + return; + } + + // Set up a frame receiver object which will fire up its own thread. + std::shared_ptr<FrameHandler> frameHandler0 = + std::make_shared<FrameHandler>(pCam0, cam, nullptr, FrameHandler::eAutoReturn); + std::shared_ptr<FrameHandler> frameHandler1 = + std::make_shared<FrameHandler>(pCam1, cam, nullptr, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandler0, nullptr); + EXPECT_NE(frameHandler1, nullptr); + + // Activate the display + ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME).isOk()); + + // Start the camera's video stream + ASSERT_TRUE(frameHandler0->startStream()); + ASSERT_TRUE(frameHandler1->startStream()); + + // Ensure the stream starts + frameHandler0->waitForFrameCount(1); + frameHandler1->waitForFrameCount(1); + + // Client 1 becomes a primary client and programs a parameter. + + // Get a valid parameter value range + ParameterRange range; + ASSERT_TRUE(pCam1->getIntParameterRange(cam1Cmds[0], &range).isOk()); + + // Client1 becomes a primary client + ASSERT_TRUE(pCam1->setPrimaryClient().isOk()); + + std::vector<int32_t> values; + EvsEventDesc aTargetEvent = {}; + EvsEventDesc aNotification = {}; + bool listening = false; + std::mutex eventLock; + std::condition_variable eventCond; + if (cam1Cmds[0] == CameraParam::ABSOLUTE_FOCUS) { + std::thread listener = + std::thread([&frameHandler0, &aNotification, &listening, &eventCond] { + listening = true; + eventCond.notify_all(); + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED; + aTargetEvent.payload[0] = static_cast<uint32_t>(CameraParam::AUTO_FOCUS); + aTargetEvent.payload[1] = 0; + if (!frameHandler0->waitForEvent(aTargetEvent, aNotification)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a lister starts. + std::unique_lock<std::mutex> lock(eventLock); + auto timer = std::chrono::system_clock::now(); + while (!listening) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + // Try to turn off auto-focus + ASSERT_TRUE(pCam1->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(v, 0); + } + + // Join a listener + if (listener.joinable()) { + listener.join(); + } + + // Make sure AUTO_FOCUS is off. + ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), + EvsEventType::PARAMETER_CHANGED); + } + + // Try to program a parameter with a random value [minVal, maxVal] after + // rounding it down. + int32_t val0 = range.min + (std::rand() % (range.max - range.min)); + val0 = val0 - (val0 % range.step); + + std::thread listener = std::thread( + [&frameHandler1, &aNotification, &listening, &eventCond, &cam1Cmds, val0] { + listening = true; + eventCond.notify_all(); + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED; + aTargetEvent.payload[0] = static_cast<uint32_t>(cam1Cmds[0]); + aTargetEvent.payload[1] = val0; + if (!frameHandler1->waitForEvent(aTargetEvent, aNotification)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a lister starts. + listening = false; + std::unique_lock<std::mutex> lock(eventLock); + auto timer = std::chrono::system_clock::now(); + while (!listening) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + values.clear(); + ASSERT_TRUE(pCam1->setIntParameter(cam1Cmds[0], val0, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(val0, v); + } + + // Join a listener + if (listener.joinable()) { + listener.join(); + } + + // Verify a change notification + ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), EvsEventType::PARAMETER_CHANGED); + ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]), cam1Cmds[0]); + for (auto&& v : values) { + ASSERT_EQ(v, static_cast<int32_t>(aNotification.payload[1])); + } + + listener = std::thread([&frameHandler1, &aNotification, &listening, &eventCond] { + listening = true; + eventCond.notify_all(); + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::MASTER_RELEASED; + if (!frameHandler1->waitForEvent(aTargetEvent, aNotification, true)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a lister starts. + listening = false; + lock.lock(); + timer = std::chrono::system_clock::now(); + while (!listening) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + // Client 0 steals a primary client role + ASSERT_TRUE(pCam0->forcePrimaryClient(pDisplay).isOk()); + + // Join a listener + if (listener.joinable()) { + listener.join(); + } + + ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), EvsEventType::MASTER_RELEASED); + + // Client 0 programs a parameter + val0 = range.min + (std::rand() % (range.max - range.min)); + + // Rounding down + val0 = val0 - (val0 % range.step); + + if (cam0Cmds[0] == CameraParam::ABSOLUTE_FOCUS) { + std::thread listener = + std::thread([&frameHandler1, &aNotification, &listening, &eventCond] { + listening = true; + eventCond.notify_all(); + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED; + aTargetEvent.payload[0] = static_cast<uint32_t>(CameraParam::AUTO_FOCUS); + aTargetEvent.payload[1] = 0; + if (!frameHandler1->waitForEvent(aTargetEvent, aNotification)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a lister starts. + std::unique_lock<std::mutex> lock(eventLock); + auto timer = std::chrono::system_clock::now(); + while (!listening) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + // Try to turn off auto-focus + values.clear(); + ASSERT_TRUE(pCam0->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk()); + for (auto&& v : values) { + EXPECT_EQ(v, 0); + } + + // Join a listener + if (listener.joinable()) { + listener.join(); + } + + // Make sure AUTO_FOCUS is off. + ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), + EvsEventType::PARAMETER_CHANGED); + } + + listener = std::thread( + [&frameHandler0, &aNotification, &listening, &eventCond, &cam0Cmds, val0] { + listening = true; + eventCond.notify_all(); + + EvsEventDesc aTargetEvent; + aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED; + aTargetEvent.payload[0] = static_cast<uint32_t>(cam0Cmds[0]); + aTargetEvent.payload[1] = val0; + if (!frameHandler0->waitForEvent(aTargetEvent, aNotification)) { + LOG(WARNING) << "A timer is expired before a target event is fired."; + } + }); + + // Wait until a lister starts. + listening = false; + timer = std::chrono::system_clock::now(); + lock.lock(); + while (!listening) { + eventCond.wait_until(lock, timer + 1s); + } + lock.unlock(); + + values.clear(); + ASSERT_TRUE(pCam0->setIntParameter(cam0Cmds[0], val0, &values).isOk()); + + // Join a listener + if (listener.joinable()) { + listener.join(); + } + // Verify a change notification + ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), EvsEventType::PARAMETER_CHANGED); + ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]), cam0Cmds[0]); + for (auto&& v : values) { + ASSERT_EQ(v, static_cast<int32_t>(aNotification.payload[1])); + } + + // Turn off the display (yes, before the stream stops -- it should be handled) + ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::NOT_VISIBLE).isOk()); + + // Shut down the streamer + frameHandler0->shutdown(); + frameHandler1->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam0).isOk()); + ASSERT_TRUE(mEnumerator->closeCamera(pCam1).isOk()); + mActiveCameras.clear(); + } + + // Explicitly release the display + ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk()); +} + +/* + * CameraUseStreamConfigToDisplay: + * End to end test of data flowing from the camera to the display. Similar to + * CameraToDisplayRoundTrip test case but this case retrieves available stream + * configurations from EVS and uses one of them to start a video stream. + */ +TEST_P(EvsAidlTest, CameraUseStreamConfigToDisplay) { + LOG(INFO) << "Starting CameraUseStreamConfigToDisplay test"; + + // Get the camera list + loadCameraList(); + + // Request available display IDs + uint8_t targetDisplayId = 0; + std::vector<uint8_t> displayIds; + ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk()); + EXPECT_GT(displayIds.size(), 0); + targetDisplayId = displayIds[0]; + + // Request exclusive access to the EVS display + std::shared_ptr<IEvsDisplay> pDisplay; + ASSERT_TRUE(mEnumerator->openDisplay(targetDisplayId, &pDisplay).isOk()); + EXPECT_NE(pDisplay, nullptr); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + // choose a configuration that has a frame rate faster than minReqFps. + Stream targetCfg = {}; + const int32_t minReqFps = 15; + int32_t maxArea = 0; + camera_metadata_entry_t streamCfgs; + bool foundCfg = false; + if (!find_camera_metadata_entry(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()), + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + &streamCfgs)) { + // Stream configurations are found in metadata + RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32); + for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) { + if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT && + ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) { + if (ptr->width * ptr->height > maxArea && ptr->framerate >= minReqFps) { + targetCfg.width = ptr->width; + targetCfg.height = ptr->height; + + maxArea = ptr->width * ptr->height; + foundCfg = true; + } + } + ++ptr; + } + } + targetCfg.format = static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888); + + if (!foundCfg) { + // Current EVS camera does not provide stream configurations in the + // metadata. + continue; + } + + std::shared_ptr<IEvsCamera> pCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk()); + EXPECT_NE(pCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam); + + // Set up a frame receiver object which will fire up its own thread. + std::shared_ptr<FrameHandler> frameHandler = + std::make_shared<FrameHandler>(pCam, cam, pDisplay, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandler, nullptr); + + // Activate the display + ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME).isOk()); + + // Start the camera's video stream + ASSERT_TRUE(frameHandler->startStream()); + + // Wait a while to let the data flow + static const int kSecondsToWait = 5; + const int streamTimeMs = + kSecondsToWait * kSecondsToMilliseconds - kMaxStreamStartMilliseconds; + const unsigned minimumFramesExpected = + streamTimeMs * kMinimumFramesPerSecond / kSecondsToMilliseconds; + sleep(kSecondsToWait); + unsigned framesReceived = 0; + unsigned framesDisplayed = 0; + frameHandler->getFramesCounters(&framesReceived, &framesDisplayed); + EXPECT_EQ(framesReceived, framesDisplayed); + EXPECT_GE(framesDisplayed, minimumFramesExpected); + + // Turn off the display (yes, before the stream stops -- it should be handled) + ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::NOT_VISIBLE).isOk()); + + // Shut down the streamer + frameHandler->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk()); + mActiveCameras.clear(); + } + + // Explicitly release the display + ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk()); +} + +/* + * MultiCameraStreamUseConfig: + * Verify that each client can start and stop video streams on the same + * underlying camera with same configuration. + */ +TEST_P(EvsAidlTest, MultiCameraStreamUseConfig) { + LOG(INFO) << "Starting MultiCameraStream test"; + + if (mIsHwModule) { + // This test is not for HW module implementation. + return; + } + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + // choose a configuration that has a frame rate faster than minReqFps. + Stream targetCfg = {}; + const int32_t minReqFps = 15; + int32_t maxArea = 0; + camera_metadata_entry_t streamCfgs; + bool foundCfg = false; + if (!find_camera_metadata_entry(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()), + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + &streamCfgs)) { + // Stream configurations are found in metadata + RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32); + for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) { + if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT && + ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) { + if (ptr->width * ptr->height > maxArea && ptr->framerate >= minReqFps) { + targetCfg.width = ptr->width; + targetCfg.height = ptr->height; + + maxArea = ptr->width * ptr->height; + foundCfg = true; + } + } + ++ptr; + } + } + targetCfg.format = static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888); + + if (!foundCfg) { + LOG(INFO) << "Device " << cam.id + << " does not provide a list of supported stream configurations, skipped"; + continue; + } + + // Create the first camera client with a selected stream configuration. + std::shared_ptr<IEvsCamera> pCam0; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam0).isOk()); + EXPECT_NE(pCam0, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam0); + + // Try to create the second camera client with different stream + // configuration. + int32_t id = targetCfg.id; + targetCfg.id += 1; // EVS manager sees only the stream id. + std::shared_ptr<IEvsCamera> pCam1; + ASSERT_FALSE(mEnumerator->openCamera(cam.id, targetCfg, &pCam1).isOk()); + + // Try again with same stream configuration. + targetCfg.id = id; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam1).isOk()); + EXPECT_NE(pCam1, nullptr); + + // Set up per-client frame receiver objects which will fire up its own thread + std::shared_ptr<FrameHandler> frameHandler0 = + std::make_shared<FrameHandler>(pCam0, cam, nullptr, FrameHandler::eAutoReturn); + std::shared_ptr<FrameHandler> frameHandler1 = + std::make_shared<FrameHandler>(pCam1, cam, nullptr, FrameHandler::eAutoReturn); + EXPECT_NE(frameHandler0, nullptr); + EXPECT_NE(frameHandler1, nullptr); + + // Start the camera's video stream via client 0 + ASSERT_TRUE(frameHandler0->startStream()); + ASSERT_TRUE(frameHandler1->startStream()); + + // Ensure the stream starts + frameHandler0->waitForFrameCount(1); + frameHandler1->waitForFrameCount(1); + + nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC); + + // Wait a bit, then ensure both clients get at least the required minimum number of frames + sleep(5); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); + unsigned framesReceived0 = 0, framesReceived1 = 0; + frameHandler0->getFramesCounters(&framesReceived0, nullptr); + frameHandler1->getFramesCounters(&framesReceived1, nullptr); + framesReceived0 = framesReceived0 - 1; // Back out the first frame we already waited for + framesReceived1 = framesReceived1 - 1; // Back out the first frame we already waited for + nsecs_t runTime = end - firstFrame; + float framesPerSecond0 = framesReceived0 / (runTime * kNanoToSeconds); + float framesPerSecond1 = framesReceived1 / (runTime * kNanoToSeconds); + LOG(INFO) << "Measured camera rate " << std::scientific << framesPerSecond0 << " fps and " + << framesPerSecond1 << " fps"; + EXPECT_GE(framesPerSecond0, kMinimumFramesPerSecond); + EXPECT_GE(framesPerSecond1, kMinimumFramesPerSecond); + + // Shutdown one client + frameHandler0->shutdown(); + + // Read frame counters again + frameHandler0->getFramesCounters(&framesReceived0, nullptr); + frameHandler1->getFramesCounters(&framesReceived1, nullptr); + + // Wait a bit again + sleep(5); + unsigned framesReceivedAfterStop0 = 0, framesReceivedAfterStop1 = 0; + frameHandler0->getFramesCounters(&framesReceivedAfterStop0, nullptr); + frameHandler1->getFramesCounters(&framesReceivedAfterStop1, nullptr); + EXPECT_EQ(framesReceived0, framesReceivedAfterStop0); + EXPECT_LT(framesReceived1, framesReceivedAfterStop1); + + // Shutdown another + frameHandler1->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam0).isOk()); + ASSERT_TRUE(mEnumerator->closeCamera(pCam1).isOk()); + mActiveCameras.clear(); + } +} + +/* + * LogicalCameraMetadata: + * Opens logical camera reported by the enumerator and validate its metadata by + * checking its capability and locating supporting physical camera device + * identifiers. + */ +TEST_P(EvsAidlTest, LogicalCameraMetadata) { + LOG(INFO) << "Starting LogicalCameraMetadata test"; + + // Get the camera list + loadCameraList(); + + // Open and close each camera twice + for (auto&& cam : mCameraInfo) { + bool isLogicalCam = false; + auto devices = getPhysicalCameraIds(cam.id, isLogicalCam); + if (isLogicalCam) { + ASSERT_GE(devices.size(), 1) << "Logical camera device must have at least one physical " + "camera device ID in its metadata."; + } + } +} + +/* + * CameraStreamExternalBuffering: + * This is same with CameraStreamBuffering except frame buffers are allocated by + * the test client and then imported by EVS framework. + */ +TEST_P(EvsAidlTest, CameraStreamExternalBuffering) { + LOG(INFO) << "Starting CameraStreamExternalBuffering test"; + + // Arbitrary constant (should be > 1 and not too big) + static const unsigned int kBuffersToHold = 3; + + // Get the camera list + loadCameraList(); + + // Acquire the graphics buffer allocator + android::GraphicBufferAllocator& alloc(android::GraphicBufferAllocator::get()); + const auto usage = + GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_OFTEN; + + // Test each reported camera + for (auto&& cam : mCameraInfo) { + // Read a target resolution from the metadata + Stream targetCfg = getFirstStreamConfiguration( + reinterpret_cast<camera_metadata_t*>(cam.metadata.data())); + ASSERT_GT(targetCfg.width, 0); + ASSERT_GT(targetCfg.height, 0); + + // Allocate buffers to use + std::vector<BufferDesc> buffers; + buffers.resize(kBuffersToHold); + for (auto i = 0; i < kBuffersToHold; ++i) { + unsigned pixelsPerLine; + buffer_handle_t memHandle = nullptr; + android::status_t result = + alloc.allocate(targetCfg.width, targetCfg.height, + static_cast<android::PixelFormat>(targetCfg.format), + /* layerCount = */ 1, usage, &memHandle, &pixelsPerLine, + /* graphicBufferId = */ 0, + /* requestorName = */ "CameraStreamExternalBufferingTest"); + if (result != android::NO_ERROR) { + LOG(ERROR) << __FUNCTION__ << " failed to allocate memory."; + // Release previous allocated buffers + for (auto j = 0; j < i; j++) { + alloc.free(::android::dupFromAidl(buffers[i].buffer.handle)); + } + return; + } else { + BufferDesc buf; + HardwareBufferDescription* pDesc = + reinterpret_cast<HardwareBufferDescription*>(&buf.buffer.description); + pDesc->width = targetCfg.width; + pDesc->height = targetCfg.height; + pDesc->layers = 1; + pDesc->format = targetCfg.format; + pDesc->usage = static_cast<BufferUsage>(usage); + pDesc->stride = pixelsPerLine; + buf.buffer.handle = ::android::dupToAidl(memHandle); + buf.bufferId = i; // Unique number to identify this buffer + buffers[i] = std::move(buf); + } + } + + bool isLogicalCam = false; + getPhysicalCameraIds(cam.id, isLogicalCam); + + std::shared_ptr<IEvsCamera> pCam; + ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk()); + EXPECT_NE(pCam, nullptr); + + // Store a camera handle for a clean-up + mActiveCameras.push_back(pCam); + + // Request to import buffers + int delta = 0; + auto status = pCam->importExternalBuffers(buffers, &delta); + if (isLogicalCam) { + ASSERT_FALSE(status.isOk()); + continue; + } + + ASSERT_TRUE(status.isOk()); + EXPECT_GE(delta, kBuffersToHold); + + // Set up a frame receiver object which will fire up its own thread. + std::shared_ptr<FrameHandler> frameHandler = + std::make_shared<FrameHandler>(pCam, cam, nullptr, FrameHandler::eNoAutoReturn); + EXPECT_NE(frameHandler, nullptr); + + // Start the camera's video stream + ASSERT_TRUE(frameHandler->startStream()); + + // Check that the video stream stalls once we've gotten exactly the number of buffers + // we requested since we told the frameHandler not to return them. + 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_LE(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit"; + + // Give back one buffer + EXPECT_TRUE(frameHandler->returnHeldBuffer()); + + // Once we return a buffer, it shouldn't take more than 1/10 second to get a new one + // filled since we require 10fps minimum -- but give a 10% allowance just in case. + unsigned framesReceivedAfter = 0; + usleep(110 * kMillisecondsToMicroseconds); + frameHandler->getFramesCounters(&framesReceivedAfter, nullptr); + EXPECT_EQ(framesReceived + 1, framesReceivedAfter) << "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. + // Also note that the FrameHandle and the Camera have a mutual circular reference, so + // we have to break that cycle in order for either of them to get cleaned up. + frameHandler->shutdown(); + + // Explicitly release the camera + ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk()); + mActiveCameras.clear(); + // Release buffers + for (auto& b : buffers) { + alloc.free(::android::dupFromAidl(b.buffer.handle)); + } + buffers.resize(0); + } +} + +/* + * UltrasonicsArrayOpenClean: + * Opens each ultrasonics arrays reported by the enumerator and then explicitly closes it via a + * call to closeUltrasonicsArray. Then repeats the test to ensure all ultrasonics arrays + * can be reopened. + */ +TEST_P(EvsAidlTest, UltrasonicsArrayOpenClean) { + LOG(INFO) << "Starting UltrasonicsArrayOpenClean test"; + + // Get the ultrasonics array list + loadUltrasonicsArrayList(); + + // Open and close each ultrasonics array twice + for (auto&& ultraInfo : mUltrasonicsArraysInfo) { + for (int pass = 0; pass < 2; pass++) { + std::shared_ptr<IEvsUltrasonicsArray> pUltrasonicsArray; + ASSERT_TRUE( + mEnumerator + ->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId, &pUltrasonicsArray) + .isOk()); + EXPECT_NE(pUltrasonicsArray, nullptr); + + // Verify that this ultrasonics array self-identifies correctly + UltrasonicsArrayDesc desc; + ASSERT_TRUE(pUltrasonicsArray->getUltrasonicArrayInfo(&desc).isOk()); + EXPECT_EQ(ultraInfo.ultrasonicsArrayId, desc.ultrasonicsArrayId); + LOG(DEBUG) << "Found ultrasonics array " << ultraInfo.ultrasonicsArrayId; + + // Explicitly close the ultrasonics array so resources are released right away + ASSERT_TRUE(mEnumerator->closeUltrasonicsArray(pUltrasonicsArray).isOk()); + } + } +} + +// Starts a stream and verifies all data received is valid. +TEST_P(EvsAidlTest, UltrasonicsVerifyStreamData) { + LOG(INFO) << "Starting UltrasonicsVerifyStreamData"; + + // Get the ultrasonics array list + loadUltrasonicsArrayList(); + + // For each ultrasonics array. + for (auto&& ultraInfo : mUltrasonicsArraysInfo) { + LOG(DEBUG) << "Testing ultrasonics array: " << ultraInfo.ultrasonicsArrayId; + + std::shared_ptr<IEvsUltrasonicsArray> pUltrasonicsArray; + ASSERT_TRUE( + mEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId, &pUltrasonicsArray) + .isOk()); + EXPECT_NE(pUltrasonicsArray, nullptr); + + std::shared_ptr<FrameHandlerUltrasonics> frameHandler = + std::make_shared<FrameHandlerUltrasonics>(pUltrasonicsArray); + EXPECT_NE(frameHandler, nullptr); + + // Start stream. + ASSERT_TRUE(pUltrasonicsArray->startStream(frameHandler).isOk()); + + // Wait 5 seconds to receive frames. + sleep(5); + + // Stop stream. + ASSERT_TRUE(pUltrasonicsArray->stopStream().isOk()); + + EXPECT_GT(frameHandler->getReceiveFramesCount(), 0); + EXPECT_TRUE(frameHandler->areAllFramesValid()); + + // Explicitly close the ultrasonics array so resources are released right away + ASSERT_TRUE(mEnumerator->closeUltrasonicsArray(pUltrasonicsArray).isOk()); + } +} + +// Sets frames in flight before and after start of stream and verfies success. +TEST_P(EvsAidlTest, UltrasonicsSetFramesInFlight) { + LOG(INFO) << "Starting UltrasonicsSetFramesInFlight"; + + // Get the ultrasonics array list + loadUltrasonicsArrayList(); + + // For each ultrasonics array. + for (auto&& ultraInfo : mUltrasonicsArraysInfo) { + LOG(DEBUG) << "Testing ultrasonics array: " << ultraInfo.ultrasonicsArrayId; + + std::shared_ptr<IEvsUltrasonicsArray> pUltrasonicsArray; + ASSERT_TRUE( + mEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId, &pUltrasonicsArray) + .isOk()); + EXPECT_NE(pUltrasonicsArray, nullptr); + + ASSERT_TRUE(pUltrasonicsArray->setMaxFramesInFlight(10).isOk()); + + std::shared_ptr<FrameHandlerUltrasonics> frameHandler = + std::make_shared<FrameHandlerUltrasonics>(pUltrasonicsArray); + EXPECT_NE(frameHandler, nullptr); + + // Start stream. + ASSERT_TRUE(pUltrasonicsArray->startStream(frameHandler).isOk()); + ASSERT_TRUE(pUltrasonicsArray->setMaxFramesInFlight(5).isOk()); + + // Stop stream. + ASSERT_TRUE(pUltrasonicsArray->stopStream().isOk()); + + // Explicitly close the ultrasonics array so resources are released right away + ASSERT_TRUE(mEnumerator->closeUltrasonicsArray(pUltrasonicsArray).isOk()); + } +} + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EvsAidlTest); +INSTANTIATE_TEST_SUITE_P( + PerInstance, EvsAidlTest, + testing::ValuesIn(android::getAidlHalInstanceNames(IEvsEnumerator::descriptor)), + android::PrintInstanceNameToString); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +} diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto deleted file mode 100644 index 58daca619c..0000000000 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -syntax = "proto2"; - -package vhal_proto; - -// CMD messages are from workstation --> VHAL -// RESP messages are from VHAL --> workstation -enum MsgType { - GET_CONFIG_CMD = 0; - GET_CONFIG_RESP = 1; - GET_CONFIG_ALL_CMD = 2; - GET_CONFIG_ALL_RESP = 3; - GET_PROPERTY_CMD = 4; - GET_PROPERTY_RESP = 5; - GET_PROPERTY_ALL_CMD = 6; - GET_PROPERTY_ALL_RESP = 7; - SET_PROPERTY_CMD = 8; - SET_PROPERTY_RESP = 9; - SET_PROPERTY_ASYNC = 10; - DEBUG_CMD = 11; - DEBUG_RESP = 12; -} -enum Status { - RESULT_OK = 0; - ERROR_UNKNOWN = 1; - ERROR_UNIMPLEMENTED_CMD = 2; - ERROR_INVALID_PROPERTY = 3; - ERROR_INVALID_AREA_ID = 4; - ERROR_PROPERTY_UNINITIALIZED = 5; - ERROR_WRITE_ONLY_PROPERTY = 6; - ERROR_MEMORY_ALLOC_FAILED = 7; - ERROR_INVALID_OPERATION = 8; -} - -enum VehiclePropStatus { - AVAILABLE = 0; - UNAVAILABLE = 1; - ERROR = 2; -} - -message VehicleAreaConfig { - required int32 area_id = 1; - optional sint32 min_int32_value = 2; - optional sint32 max_int32_value = 3; - optional sint64 min_int64_value = 4; - optional sint64 max_int64_value = 5; - optional float min_float_value = 6; - optional float max_float_value = 7; -} - -message VehiclePropConfig { - required int32 prop = 1; - optional int32 access = 2; - optional int32 change_mode = 3; - optional int32 value_type = 4; - optional int32 supported_areas = 5; // Deprecated - DO NOT USE - repeated VehicleAreaConfig area_configs = 6; - optional int32 config_flags = 7; - repeated int32 config_array = 8; - optional string config_string = 9; - optional float min_sample_rate = 10; - optional float max_sample_rate = 11; -}; - -message VehiclePropValue { - // common data - required int32 prop = 1; - optional int32 value_type = 2; - optional int64 timestamp = 3; // required for valid data from HAL, skipped for set - optional VehiclePropStatus status = 10; // required for valid data from HAL, skipped for set - - // values - optional int32 area_id = 4; - repeated sint32 int32_values = 5; // this also covers boolean value. - repeated sint64 int64_values = 6; - repeated float float_values = 7; - optional string string_value = 8; - optional bytes bytes_value = 9; -}; - -// This structure is used to notify what values to get from the Vehicle HAL -message VehiclePropGet { - required int32 prop = 1; - optional int32 area_id = 2; -}; - -message EmulatorMessage { - required MsgType msg_type = 1; - optional Status status = 2; // Only for RESP messages - repeated VehiclePropGet prop = 3; // Provided for getConfig, getProperty commands - repeated VehiclePropConfig config = 4; - repeated VehiclePropValue value = 5; - repeated string debug_commands = 6; // Required for debug command - optional string debug_result = 7; // Required for debug RESP messages -}; diff --git a/automotive/vehicle/OWNERS b/automotive/vehicle/OWNERS new file mode 100644 index 0000000000..429ec396ba --- /dev/null +++ b/automotive/vehicle/OWNERS @@ -0,0 +1,3 @@ +ericjeong@google.com +keunyoung@google.com +shanyu@google.com diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h index cab184bb49..578d045376 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h @@ -23,7 +23,9 @@ #include <IVehicleHardware.h> #include <VehicleHalTypes.h> #include <VehiclePropertyStore.h> +#include <android-base/parseint.h> #include <android-base/result.h> +#include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> #include <map> @@ -37,7 +39,7 @@ namespace automotive { namespace vehicle { namespace fake { -class FakeVehicleHardware final : public IVehicleHardware { +class FakeVehicleHardware : public IVehicleHardware { public: FakeVehicleHardware(); @@ -78,13 +80,21 @@ class FakeVehicleHardware final : public IVehicleHardware { void registerOnPropertySetErrorEvent( std::unique_ptr<const PropertySetErrorCallback> callback) override; + protected: + // mValuePool is also used in mServerSidePropStore. + const std::shared_ptr<VehiclePropValuePool> mValuePool; + const std::shared_ptr<VehiclePropertyStore> mServerSidePropStore; + + ::android::base::Result<VehiclePropValuePool::RecyclableType> getValue( + const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const; + + ::android::base::Result<void> setValue( + const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value); + private: // Expose private methods to unit test. friend class FakeVehicleHardwareTestHelper; - // mValuePool is also used in mServerSidePropStore. - const std::shared_ptr<VehiclePropValuePool> mValuePool; - const std::shared_ptr<VehiclePropertyStore> mServerSidePropStore; const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame; const std::unique_ptr<FakeUserHal> mFakeUserHal; std::mutex mCallbackLock; @@ -120,6 +130,35 @@ class FakeVehicleHardware final : public IVehicleHardware { ::android::base::Result<VehiclePropValuePool::RecyclableType> getUserHalProp( const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const; bool isHvacPropAndHvacNotAvailable(int32_t propId); + + std::string dumpAllProperties(); + std::string dumpOnePropertyByConfig( + int rowNumber, + const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config); + std::string dumpOnePropertyById(int32_t propId, int32_t areaId); + std::string dumpHelp(); + std::string dumpListProperties(); + std::string dumpSpecificProperty(const std::vector<std::string>& options); + std::string dumpSetProperties(const std::vector<std::string>& options); + + template <typename T> + ::android::base::Result<T> safelyParseInt(int index, const std::string& s) { + T out; + if (!::android::base::ParseInt(s, &out)) { + return ::android::base::Error() << ::android::base::StringPrintf( + "non-integer argument at index %d: %s\n", index, s.c_str()); + } + return out; + } + ::android::base::Result<float> safelyParseFloat(int index, const std::string& s); + std::vector<std::string> getOptionValues(const std::vector<std::string>& options, + size_t* index); + ::android::base::Result<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> + parseSetPropOptions(const std::vector<std::string>& options); + ::android::base::Result<std::vector<uint8_t>> parseHexString(const std::string& s); + + ::android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& options, + size_t minSize); }; } // namespace fake diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp index e75f0e7c2c..097257e3c0 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#define LOG_TAG "FakeVehicleHardware" +#define FAKE_VEHICLEHARDWARE_DEBUG false // STOPSHIP if true. + #include "FakeVehicleHardware.h" #include <DefaultConfig.h> @@ -22,7 +25,9 @@ #include <PropertyUtils.h> #include <VehicleHalTypes.h> #include <VehicleUtils.h> +#include <android-base/parsedouble.h> #include <android-base/properties.h> +#include <android-base/strings.h> #include <utils/Log.h> #include <utils/SystemClock.h> @@ -56,12 +61,31 @@ using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; +using ::android::base::EqualsIgnoreCase; using ::android::base::Error; +using ::android::base::ParseFloat; using ::android::base::Result; +using ::android::base::StartsWith; +using ::android::base::StringPrintf; const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/"; const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override"; +// A list of supported options for "--set" command. +const std::unordered_set<std::string> SET_PROP_OPTIONS = { + // integer. + "-i", + // 64bit integer. + "-i64", + // float. + "-f", + // string. + "-s", + // bytes in hex format, e.g. 0xDEADBEEF. + "-b", + // Area id in integer. + "-a"}; + } // namespace void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) { @@ -381,44 +405,26 @@ Result<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValue& v StatusCode FakeVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback, const std::vector<SetValueRequest>& requests) { - std::vector<VehiclePropValue> updatedValues; std::vector<SetValueResult> results; for (auto& request : requests) { const VehiclePropValue& value = request.value; int propId = value.prop; - ALOGD("Set value for property ID: %d", propId); + if (FAKE_VEHICLEHARDWARE_DEBUG) { + ALOGD("Set value for property ID: %d", propId); + } SetValueResult setValueResult; setValueResult.requestId = request.requestId; - setValueResult.status = StatusCode::OK; - - bool isSpecialValue = false; - auto setSpecialValueResult = maybeSetSpecialValue(value, &isSpecialValue); - if (isSpecialValue) { - if (!setSpecialValueResult.ok()) { - ALOGE("failed to set special value for property ID: %d, error: %s, status: %d", - propId, getErrorMsg(setSpecialValueResult).c_str(), - getIntErrorCode(setSpecialValueResult)); - setValueResult.status = getErrorCode(setSpecialValueResult); - } - - // Special values are already handled. - results.push_back(std::move(setValueResult)); - continue; + if (auto result = setValue(value); !result.ok()) { + ALOGE("failed to set value, error: %s, code: %d", getErrorMsg(result).c_str(), + getIntErrorCode(result)); + setValueResult.status = getErrorCode(result); + } else { + setValueResult.status = StatusCode::OK; } - auto updatedValue = mValuePool->obtain(value); - int64_t timestamp = elapsedRealtimeNano(); - updatedValue->timestamp = timestamp; - - auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue)); - if (!writeResult.ok()) { - ALOGE("failed to write value into property store, error: %s, code: %d", - getErrorMsg(writeResult).c_str(), getIntErrorCode(writeResult)); - setValueResult.status = getErrorCode(writeResult); - } results.push_back(std::move(setValueResult)); } @@ -429,61 +435,378 @@ StatusCode FakeVehicleHardware::setValues(std::shared_ptr<const SetValuesCallbac return StatusCode::OK; } +Result<void> FakeVehicleHardware::setValue(const VehiclePropValue& value) { + bool isSpecialValue = false; + auto setSpecialValueResult = maybeSetSpecialValue(value, &isSpecialValue); + + if (isSpecialValue) { + if (!setSpecialValueResult.ok()) { + return Error(getIntErrorCode(setSpecialValueResult)) + << StringPrintf("failed to set special value for property ID: %d, error: %s", + value.prop, getErrorMsg(setSpecialValueResult).c_str()); + } + return {}; + } + + auto updatedValue = mValuePool->obtain(value); + int64_t timestamp = elapsedRealtimeNano(); + updatedValue->timestamp = timestamp; + + auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue)); + if (!writeResult.ok()) { + return Error(getIntErrorCode(writeResult)) + << StringPrintf("failed to write value into property store, error: %s", + getErrorMsg(writeResult).c_str()); + } + + return {}; +} + StatusCode FakeVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback, const std::vector<GetValueRequest>& requests) const { std::vector<GetValueResult> results; for (auto& request : requests) { const VehiclePropValue& value = request.prop; - ALOGD("getValues(%d)", value.prop); + + if (FAKE_VEHICLEHARDWARE_DEBUG) { + ALOGD("getValues(%d)", value.prop); + } GetValueResult getValueResult; getValueResult.requestId = request.requestId; - bool isSpecialValue = false; - - auto result = maybeGetSpecialValue(value, &isSpecialValue); - if (isSpecialValue) { - if (!result.ok()) { - ALOGE("failed to get special value: %d, error: %s, code: %d", value.prop, - getErrorMsg(result).c_str(), getIntErrorCode(result)); - getValueResult.status = getErrorCode(result); - } else { - getValueResult.status = StatusCode::OK; - getValueResult.prop = *result.value(); - } - results.push_back(std::move(getValueResult)); - continue; - } - auto readResult = mServerSidePropStore->readValue(value); - if (!readResult.ok()) { - StatusCode errorCode = getErrorCode(readResult); - if (errorCode == StatusCode::NOT_AVAILABLE) { - ALOGW("%s", "value has not been set yet"); - } else { - ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(readResult).c_str(), - toInt(errorCode)); - } - getValueResult.status = errorCode; + auto result = getValue(value); + if (!result.ok()) { + ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(result).c_str(), + getIntErrorCode(result)); + getValueResult.status = getErrorCode(result); } else { getValueResult.status = StatusCode::OK; - getValueResult.prop = *readResult.value(); + getValueResult.prop = *result.value(); } results.push_back(std::move(getValueResult)); } + // In a real VHAL implementation, getValue would be async and we would call the callback after + // we actually received the values from vehicle bus. Here we are getting the result + // synchronously so we could call the callback here. (*callback)(std::move(results)); return StatusCode::OK; } -DumpResult FakeVehicleHardware::dump(const std::vector<std::string>&) { +Result<VehiclePropValuePool::RecyclableType> FakeVehicleHardware::getValue( + const VehiclePropValue& value) const { + bool isSpecialValue = false; + auto result = maybeGetSpecialValue(value, &isSpecialValue); + if (isSpecialValue) { + if (!result.ok()) { + return Error(getIntErrorCode(result)) + << StringPrintf("failed to get special value: %d, error: %s", value.prop, + getErrorMsg(result).c_str()); + } else { + return std::move(result); + } + } + + auto readResult = mServerSidePropStore->readValue(value); + if (!readResult.ok()) { + StatusCode errorCode = getErrorCode(readResult); + if (errorCode == StatusCode::NOT_AVAILABLE) { + return Error(toInt(errorCode)) << "value has not been set yet"; + } else { + return Error(toInt(errorCode)) + << "failed to get value, error: " << getErrorMsg(readResult); + } + } + + return std::move(readResult); +} + +DumpResult FakeVehicleHardware::dump(const std::vector<std::string>& options) { DumpResult result; - // TODO(b/201830716): Implement this. + result.callerShouldDumpState = false; + if (options.size() == 0) { + // We only want caller to dump default state when there is no options. + result.callerShouldDumpState = true; + result.buffer = dumpAllProperties(); + return result; + } + std::string option = options[0]; + if (EqualsIgnoreCase(option, "--help")) { + result.buffer = dumpHelp(); + return result; + } else if (EqualsIgnoreCase(option, "--list")) { + result.buffer = dumpListProperties(); + } else if (EqualsIgnoreCase(option, "--get")) { + result.buffer = dumpSpecificProperty(options); + } else if (EqualsIgnoreCase(option, "--set")) { + result.buffer = dumpSetProperties(options); + } else { + result.buffer = StringPrintf("Invalid option: %s\n", option.c_str()); + } return result; } +std::string FakeVehicleHardware::dumpHelp() { + return "Usage: \n\n" + "[no args]: dumps (id and value) all supported properties \n" + "--help: shows this help\n" + "--list: lists the ids of all supported properties\n" + "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n" + "--set <PROP> [-i INT_VALUE [INT_VALUE ...]] [-i64 INT64_VALUE [INT64_VALUE ...]] " + "[-f FLOAT_VALUE [FLOAT_VALUE ...]] [-s STR_VALUE] " + "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. " + "Notice that the string, bytes and area value can be set just once, while the other can" + " have multiple values (so they're used in the respective array), " + "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n"; +} + +std::string FakeVehicleHardware::dumpAllProperties() { + auto configs = mServerSidePropStore->getAllConfigs(); + if (configs.size() == 0) { + return "no properties to dump\n"; + } + std::string msg = StringPrintf("dumping %zu properties\n", configs.size()); + int rowNumber = 1; + for (const VehiclePropConfig& config : configs) { + msg += dumpOnePropertyByConfig(rowNumber++, config); + } + return msg; +} + +std::string FakeVehicleHardware::dumpOnePropertyByConfig(int rowNumber, + const VehiclePropConfig& config) { + size_t numberAreas = config.areaConfigs.size(); + std::string msg = ""; + if (numberAreas == 0) { + msg += StringPrintf("%d: ", rowNumber); + msg += dumpOnePropertyById(config.prop, /* areaId= */ 0); + return msg; + } + for (size_t j = 0; j < numberAreas; ++j) { + if (numberAreas > 1) { + msg += StringPrintf("%d-%zu: ", rowNumber, j); + } else { + msg += StringPrintf("%d: ", rowNumber); + } + msg += dumpOnePropertyById(config.prop, config.areaConfigs[j].areaId); + } + return msg; +} + +std::string FakeVehicleHardware::dumpOnePropertyById(int32_t propId, int32_t areaId) { + VehiclePropValue value = { + .prop = propId, + .areaId = areaId, + }; + bool isSpecialValue = false; + auto result = maybeGetSpecialValue(value, &isSpecialValue); + if (!isSpecialValue) { + result = mServerSidePropStore->readValue(value); + } + if (!result.ok()) { + return StringPrintf("failed to read property value: %d, error: %s, code: %d\n", propId, + getErrorMsg(result).c_str(), getIntErrorCode(result)); + + } else { + return result.value()->toString() + "\n"; + } +} + +std::string FakeVehicleHardware::dumpListProperties() { + auto configs = mServerSidePropStore->getAllConfigs(); + if (configs.size() == 0) { + return "no properties to list\n"; + } + int rowNumber = 1; + std::string msg = StringPrintf("listing %zu properties\n", configs.size()); + for (const auto& config : configs) { + msg += StringPrintf("%d: %d\n", rowNumber++, config.prop); + } + return msg; +} + +Result<void> FakeVehicleHardware::checkArgumentsSize(const std::vector<std::string>& options, + size_t minSize) { + size_t size = options.size(); + if (size >= minSize) { + return {}; + } + return Error() << StringPrintf("Invalid number of arguments: required at least %zu, got %zu\n", + minSize, size); +} + +std::string FakeVehicleHardware::dumpSpecificProperty(const std::vector<std::string>& options) { + if (auto result = checkArgumentsSize(options, /*minSize=*/2); !result.ok()) { + return getErrorMsg(result); + } + + // options[0] is the command itself... + int rowNumber = 1; + size_t size = options.size(); + std::string msg = ""; + for (size_t i = 1; i < size; ++i) { + auto propResult = safelyParseInt<int32_t>(i, options[i]); + if (!propResult.ok()) { + msg += getErrorMsg(propResult); + continue; + } + int32_t prop = propResult.value(); + auto result = mServerSidePropStore->getConfig(prop); + if (!result.ok()) { + msg += StringPrintf("No property %d\n", prop); + continue; + } + msg += dumpOnePropertyByConfig(rowNumber++, *result.value()); + } + return msg; +} + +std::vector<std::string> FakeVehicleHardware::getOptionValues( + const std::vector<std::string>& options, size_t* index) { + std::vector<std::string> values; + while (*index < options.size()) { + std::string option = options[*index]; + if (SET_PROP_OPTIONS.find(option) != SET_PROP_OPTIONS.end()) { + return std::move(values); + } + values.push_back(option); + (*index)++; + } + return std::move(values); +} + +Result<VehiclePropValue> FakeVehicleHardware::parseSetPropOptions( + const std::vector<std::string>& options) { + // Options format: + // --set PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] [-b b1 b2...] [-a a] + size_t optionIndex = 1; + auto result = safelyParseInt<int32_t>(optionIndex, options[optionIndex]); + if (!result.ok()) { + return Error() << StringPrintf("Property value: \"%s\" is not a valid int: %s\n", + options[optionIndex].c_str(), getErrorMsg(result).c_str()); + } + VehiclePropValue prop = {}; + prop.prop = result.value(); + prop.status = VehiclePropertyStatus::AVAILABLE; + optionIndex++; + std::unordered_set<std::string> parsedOptions; + + while (optionIndex < options.size()) { + std::string type = options[optionIndex]; + optionIndex++; + size_t currentIndex = optionIndex; + std::vector<std::string> values = getOptionValues(options, &optionIndex); + if (parsedOptions.find(type) != parsedOptions.end()) { + return Error() << StringPrintf("Duplicate \"%s\" options\n", type.c_str()); + } + parsedOptions.insert(type); + if (EqualsIgnoreCase(type, "-i")) { + if (values.size() == 0) { + return Error() << "No values specified when using \"-i\"\n"; + } + prop.value.int32Values.resize(values.size()); + for (size_t i = 0; i < values.size(); i++) { + auto int32Result = safelyParseInt<int32_t>(currentIndex + i, values[i]); + if (!int32Result.ok()) { + return Error() + << StringPrintf("Value: \"%s\" is not a valid int: %s\n", + values[i].c_str(), getErrorMsg(int32Result).c_str()); + } + prop.value.int32Values[i] = int32Result.value(); + } + } else if (EqualsIgnoreCase(type, "-i64")) { + if (values.size() == 0) { + return Error() << "No values specified when using \"-i64\"\n"; + } + prop.value.int64Values.resize(values.size()); + for (size_t i = 0; i < values.size(); i++) { + auto int64Result = safelyParseInt<int64_t>(currentIndex + i, values[i]); + if (!int64Result.ok()) { + return Error() + << StringPrintf("Value: \"%s\" is not a valid int64: %s\n", + values[i].c_str(), getErrorMsg(int64Result).c_str()); + } + prop.value.int64Values[i] = int64Result.value(); + } + } else if (EqualsIgnoreCase(type, "-f")) { + if (values.size() == 0) { + return Error() << "No values specified when using \"-f\"\n"; + } + prop.value.floatValues.resize(values.size()); + for (size_t i = 0; i < values.size(); i++) { + auto floatResult = safelyParseFloat(currentIndex + i, values[i]); + if (!floatResult.ok()) { + return Error() + << StringPrintf("Value: \"%s\" is not a valid float: %s\n", + values[i].c_str(), getErrorMsg(floatResult).c_str()); + } + prop.value.floatValues[i] = floatResult.value(); + } + } else if (EqualsIgnoreCase(type, "-s")) { + if (values.size() != 1) { + return Error() << "Expect exact one value when using \"-s\"\n"; + } + prop.value.stringValue = values[0]; + } else if (EqualsIgnoreCase(type, "-b")) { + if (values.size() != 1) { + return Error() << "Expect exact one value when using \"-b\"\n"; + } + auto bytesResult = parseHexString(values[0]); + if (!bytesResult.ok()) { + return Error() << StringPrintf("value: \"%s\" is not a valid hex string: %s\n", + values[0].c_str(), getErrorMsg(bytesResult).c_str()); + } + prop.value.byteValues = std::move(bytesResult.value()); + } else if (EqualsIgnoreCase(type, "-a")) { + if (values.size() != 1) { + return Error() << "Expect exact one value when using \"-a\"\n"; + } + auto int32Result = safelyParseInt<int32_t>(currentIndex, values[0]); + if (!int32Result.ok()) { + return Error() << StringPrintf("Area ID: \"%s\" is not a valid int: %s\n", + values[0].c_str(), getErrorMsg(int32Result).c_str()); + } + prop.areaId = int32Result.value(); + } else { + return Error() << StringPrintf("Unknown option: %s\n", type.c_str()); + } + } + + return prop; +} + +std::string FakeVehicleHardware::dumpSetProperties(const std::vector<std::string>& options) { + if (auto result = checkArgumentsSize(options, 3); !result.ok()) { + return getErrorMsg(result); + } + + auto parseResult = parseSetPropOptions(options); + if (!parseResult.ok()) { + return getErrorMsg(parseResult); + } + VehiclePropValue prop = std::move(parseResult.value()); + ALOGD("Dump: Setting property: %s", prop.toString().c_str()); + + bool isSpecialValue = false; + auto setResult = maybeSetSpecialValue(prop, &isSpecialValue); + + if (!isSpecialValue) { + auto updatedValue = mValuePool->obtain(prop); + updatedValue->timestamp = elapsedRealtimeNano(); + setResult = mServerSidePropStore->writeValue(std::move(updatedValue)); + } + + if (setResult.ok()) { + return StringPrintf("Set property: %s\n", prop.toString().c_str()); + } + return StringPrintf("failed to set property: %s, error: %s\n", prop.toString().c_str(), + getErrorMsg(setResult).c_str()); +} + StatusCode FakeVehicleHardware::checkHealth() { - // TODO(b/201830716): Implement this. + // Always return OK for checkHealth. return StatusCode::OK; } @@ -544,6 +867,49 @@ void FakeVehicleHardware::overrideProperties(const char* overrideDir) { } } +Result<float> FakeVehicleHardware::safelyParseFloat(int index, const std::string& s) { + float out; + if (!ParseFloat(s, &out)) { + return Error() << StringPrintf("non-float argument at index %d: %s\n", index, s.c_str()); + } + return out; +} + +Result<std::vector<uint8_t>> FakeVehicleHardware::parseHexString(const std::string& s) { + std::vector<uint8_t> bytes; + if (s.size() % 2 != 0) { + return Error() << StringPrintf("invalid hex string: %s, should have even size\n", + s.c_str()); + } + if (!StartsWith(s, "0x")) { + return Error() << StringPrintf("hex string should start with \"0x\", got %s\n", s.c_str()); + } + std::string subs = s.substr(2); + std::transform(subs.begin(), subs.end(), subs.begin(), + [](unsigned char c) { return std::tolower(c); }); + + bool highDigit = true; + for (size_t i = 0; i < subs.size(); i++) { + char c = subs[i]; + uint8_t v; + if (c >= '0' && c <= '9') { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = c - 'a' + 10; + } else { + return Error() << StringPrintf("invalid character %c in hex string %s\n", c, + subs.c_str()); + } + if (highDigit) { + bytes.push_back(v * 16); + } else { + bytes[bytes.size() - 1] += v; + } + highDigit = !highDigit; + } + return bytes; +} + } // namespace fake } // namespace vehicle } // namespace automotive diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp index 970d044d43..0812c2a062 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp @@ -24,6 +24,7 @@ #include <android-base/expected.h> #include <android-base/file.h> +#include <android-base/stringprintf.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <utils/Log.h> @@ -52,13 +53,16 @@ using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::expected; +using ::android::base::StringPrintf; using ::android::base::unexpected; using ::testing::ContainerEq; +using ::testing::ContainsRegex; using ::testing::Eq; using ::testing::IsSubsetOf; using ::testing::WhenSortedBy; constexpr int INVALID_PROP_ID = 0; +constexpr char CAR_MAKE[] = "Default Car"; } // namespace @@ -1203,6 +1207,261 @@ TEST_F(FakeVehicleHardwareTest, testInitialUserInfo) { })); } +TEST_F(FakeVehicleHardwareTest, testDumpAllProperties) { + std::vector<std::string> options; + DumpResult result = getHardware()->dump(options); + ASSERT_TRUE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, ContainsRegex("dumping .+ properties")); +} + +TEST_F(FakeVehicleHardwareTest, testDumpHelp) { + std::vector<std::string> options; + options.push_back("--help"); + DumpResult result = getHardware()->dump(options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, ContainsRegex("Usage: ")); +} + +TEST_F(FakeVehicleHardwareTest, testDumpListProperties) { + std::vector<std::string> options; + options.push_back("--list"); + DumpResult result = getHardware()->dump(options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, ContainsRegex("listing .+ properties")); +} + +TEST_F(FakeVehicleHardwareTest, testDumpSpecificProperties) { + std::vector<std::string> options; + options.push_back("--get"); + std::string prop1 = std::to_string(toInt(VehicleProperty::INFO_FUEL_CAPACITY)); + std::string prop2 = std::to_string(toInt(VehicleProperty::TIRE_PRESSURE)); + options.push_back(prop1); + options.push_back(prop2); + DumpResult result = getHardware()->dump(options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, + ContainsRegex(StringPrintf("1:.*prop: %s.*\n2-0:.*prop: %s.*\n2-1:.*prop: %s.*\n", + prop1.c_str(), prop2.c_str(), prop2.c_str()))); +} + +TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesInvalidProp) { + std::vector<std::string> options; + options.push_back("--get"); + std::string prop1 = std::to_string(toInt(VehicleProperty::INFO_FUEL_CAPACITY)); + std::string prop2 = std::to_string(INVALID_PROP_ID); + options.push_back(prop1); + options.push_back(prop2); + DumpResult result = getHardware()->dump(options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, ContainsRegex(StringPrintf("1:.*prop: %s.*\nNo property %d\n", + prop1.c_str(), INVALID_PROP_ID))); +} + +TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesNoArg) { + std::vector<std::string> options; + options.push_back("--get"); + + // No arguments. + DumpResult result = getHardware()->dump(options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, ContainsRegex("Invalid number of arguments")); +} + +TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) { + std::vector<std::string> options; + options.push_back("--invalid"); + + DumpResult result = getHardware()->dump(options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid")); +} + +struct SetPropTestCase { + std::string test_name; + std::vector<std::string> options; + bool success; + std::string errorMsg = ""; +}; + +class FakeVehicleHardwareSetPropTest : public FakeVehicleHardwareTest, + public testing::WithParamInterface<SetPropTestCase> {}; + +TEST_P(FakeVehicleHardwareSetPropTest, cmdSetOneProperty) { + const SetPropTestCase& tc = GetParam(); + + DumpResult result = getHardware()->dump(tc.options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + if (tc.success) { + ASSERT_THAT(result.buffer, ContainsRegex("Set property:")); + } else { + ASSERT_THAT(result.buffer, ContainsRegex(tc.errorMsg)); + } +} + +std::vector<SetPropTestCase> GenSetPropParams() { + std::string infoMakeProperty = std::to_string(toInt(VehicleProperty::INFO_MAKE)); + return { + {"success_set_string", {"--set", infoMakeProperty, "-s", CAR_MAKE}, true}, + {"success_set_bytes", {"--set", infoMakeProperty, "-b", "0xdeadbeef"}, true}, + {"success_set_bytes_caps", {"--set", infoMakeProperty, "-b", "0xDEADBEEF"}, true}, + {"success_set_int", {"--set", infoMakeProperty, "-i", "2147483647"}, true}, + {"success_set_ints", + {"--set", infoMakeProperty, "-i", "2147483647", "0", "-2147483648"}, + true}, + {"success_set_int64", + {"--set", infoMakeProperty, "-i64", "-9223372036854775808"}, + true}, + {"success_set_int64s", + {"--set", infoMakeProperty, "-i64", "-9223372036854775808", "0", + "9223372036854775807"}, + true}, + {"success_set_float", {"--set", infoMakeProperty, "-f", "1.175494351E-38"}, true}, + {"success_set_floats", + {"--set", infoMakeProperty, "-f", "-3.402823466E+38", "0", "3.402823466E+38"}, + true}, + {"success_set_area", {"--set", infoMakeProperty, "-a", "2147483647"}, true}, + {"fail_no_options", {"--set", infoMakeProperty}, false, "Invalid number of arguments"}, + {"fail_less_than_4_options", + {"--set", infoMakeProperty, "-i"}, + false, + "No values specified"}, + {"fail_unknown_options", {"--set", infoMakeProperty, "-abcd"}, false, "Unknown option"}, + {"fail_invalid_property", + {"--set", "not valid", "-s", CAR_MAKE}, + false, + "not a valid int"}, + {"fail_duplicate_string", + {"--set", infoMakeProperty, "-s", CAR_MAKE, "-s", CAR_MAKE}, + false, + "Duplicate \"-s\" options"}, + {"fail_multiple_strings", + {"--set", infoMakeProperty, "-s", CAR_MAKE, CAR_MAKE}, + false, + "Expect exact one value"}, + {"fail_no_string_value", + {"--set", infoMakeProperty, "-s", "-a", "1234"}, + false, + "Expect exact one value"}, + {"fail_duplicate_bytes", + {"--set", infoMakeProperty, "-b", "0xdeadbeef", "-b", "0xdeadbeef"}, + false, + "Duplicate \"-b\" options"}, + {"fail_multiple_bytes", + {"--set", infoMakeProperty, "-b", "0xdeadbeef", "0xdeadbeef"}, + false, + "Expect exact one value"}, + {"fail_invalid_bytes", + {"--set", infoMakeProperty, "-b", "0xgood"}, + false, + "not a valid hex string"}, + {"fail_invalid_bytes_no_prefix", + {"--set", infoMakeProperty, "-b", "deadbeef"}, + false, + "not a valid hex string"}, + {"fail_invalid_int", + {"--set", infoMakeProperty, "-i", "abc"}, + false, + "not a valid int"}, + {"fail_int_out_of_range", + {"--set", infoMakeProperty, "-i", "2147483648"}, + false, + "not a valid int"}, + {"fail_no_int_value", + {"--set", infoMakeProperty, "-i", "-s", CAR_MAKE}, + false, + "No values specified"}, + {"fail_invalid_int64", + {"--set", infoMakeProperty, "-i64", "abc"}, + false, + "not a valid int64"}, + {"fail_int64_out_of_range", + {"--set", infoMakeProperty, "-i64", "-9223372036854775809"}, + false, + "not a valid int64"}, + {"fail_no_int64_value", + {"--set", infoMakeProperty, "-i64", "-s", CAR_MAKE}, + false, + "No values specified"}, + {"fail_invalid_float", + {"--set", infoMakeProperty, "-f", "abc"}, + false, + "not a valid float"}, + {"fail_float_out_of_range", + {"--set", infoMakeProperty, "-f", "-3.402823466E+39"}, + false, + "not a valid float"}, + {"fail_no_float_value", + {"--set", infoMakeProperty, "-f", "-s", CAR_MAKE}, + false, + "No values specified"}, + {"fail_multiple_areas", + {"--set", infoMakeProperty, "-a", "2147483648", "0"}, + false, + "Expect exact one value"}, + {"fail_invalid_area", + {"--set", infoMakeProperty, "-a", "abc"}, + false, + "not a valid int"}, + {"fail_area_out_of_range", + {"--set", infoMakeProperty, "-a", "2147483648"}, + false, + "not a valid int"}, + {"fail_no_area_value", + {"--set", infoMakeProperty, "-a", "-s", CAR_MAKE}, + false, + "Expect exact one value"}, + }; +} + +INSTANTIATE_TEST_SUITE_P( + FakeVehicleHardwareSetPropTests, FakeVehicleHardwareSetPropTest, + testing::ValuesIn(GenSetPropParams()), + [](const testing::TestParamInfo<FakeVehicleHardwareSetPropTest::ParamType>& info) { + return info.param.test_name; + }); + +TEST_F(FakeVehicleHardwareTest, SetComplexPropTest) { + std::string infoMakeProperty = std::to_string(toInt(VehicleProperty::INFO_MAKE)); + getHardware()->dump({"--set", infoMakeProperty, "-s", CAR_MAKE, + "-b", "0xdeadbeef", "-i", "2147483647", + "0", "-2147483648", "-i64", "-9223372036854775808", + "0", "9223372036854775807", "-f", "-3.402823466E+38", + "0", "3.402823466E+38", "-a", "123"}); + VehiclePropValue requestProp; + requestProp.prop = toInt(VehicleProperty::INFO_MAKE); + requestProp.areaId = 123; + auto result = getValue(requestProp); + ASSERT_TRUE(result.ok()); + VehiclePropValue value = result.value(); + ASSERT_EQ(value.prop, toInt(VehicleProperty::INFO_MAKE)); + ASSERT_EQ(value.areaId, 123); + ASSERT_STREQ(CAR_MAKE, value.value.stringValue.c_str()); + uint8_t bytes[] = {0xde, 0xad, 0xbe, 0xef}; + ASSERT_FALSE(memcmp(bytes, value.value.byteValues.data(), sizeof(bytes))); + ASSERT_EQ(3u, value.value.int32Values.size()); + ASSERT_EQ(2147483647, value.value.int32Values[0]); + ASSERT_EQ(0, value.value.int32Values[1]); + ASSERT_EQ(-2147483648, value.value.int32Values[2]); + ASSERT_EQ(3u, value.value.int64Values.size()); + // -9223372036854775808 is not a valid literal since '-' and '9223372036854775808' would be two + // tokens and the later does not fit in unsigned long long. + ASSERT_EQ(-9223372036854775807 - 1, value.value.int64Values[0]); + ASSERT_EQ(0, value.value.int64Values[1]); + ASSERT_EQ(9223372036854775807, value.value.int64Values[2]); + ASSERT_EQ(3u, value.value.floatValues.size()); + ASSERT_EQ(-3.402823466E+38f, value.value.floatValues[0]); + ASSERT_EQ(0.0f, value.value.floatValues[1]); + ASSERT_EQ(3.402823466E+38f, value.value.floatValues[2]); +} + } // namespace fake } // namespace vehicle } // namespace automotive diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h index 013d1773c8..a7fcdcf99d 100644 --- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h +++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h @@ -37,6 +37,7 @@ #include <aidl/android/hardware/automotive/vehicle/SetValueResult.h> #include <aidl/android/hardware/automotive/vehicle/SetValueResults.h> #include <aidl/android/hardware/automotive/vehicle/StatusCode.h> +#include <aidl/android/hardware/automotive/vehicle/SubscribeOptions.h> #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReport.h> #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReq.h> #include <aidl/android/hardware/automotive/vehicle/VehicleArea.h> diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h index 49b33d594a..0f0ccf11a2 100644 --- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h +++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h @@ -67,24 +67,30 @@ inline constexpr bool isSystemProp(int32_t prop) { } inline const ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig* getAreaConfig( - const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue, + int32_t propId, int32_t areaId, const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config) { if (config.areaConfigs.size() == 0) { return nullptr; } - if (isGlobalProp(propValue.prop)) { + if (isGlobalProp(propId)) { return &(config.areaConfigs[0]); } for (const auto& c : config.areaConfigs) { - if (c.areaId == propValue.areaId) { + if (c.areaId == areaId) { return &c; } } return nullptr; } +inline const ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig* getAreaConfig( + const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue, + const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config) { + return getAreaConfig(propValue.prop, propValue.areaId, config); +} + inline std::unique_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> createVehiclePropValueVec(::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type, size_t vecSize) { diff --git a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp index 1a79230143..c1fa89645c 100644 --- a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp +++ b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp @@ -21,9 +21,11 @@ #include <VehicleHalTypes.h> #include <VehicleUtils.h> -#include <android-base/format.h> +#include <android-base/stringprintf.h> #include <math/HashCombine.h> +#include <inttypes.h> + namespace android { namespace hardware { namespace automotive { @@ -36,13 +38,14 @@ using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::Error; using ::android::base::Result; +using ::android::base::StringPrintf; bool VehiclePropertyStore::RecordId::operator==(const VehiclePropertyStore::RecordId& other) const { return area == other.area && token == other.token; } std::string VehiclePropertyStore::RecordId::toString() const { - return ::fmt::format("RecordID{{.areaId={:d}, .token={:d}}}", area, token); + return StringPrintf("RecordID{{.areaId=% " PRId32 ", .token=%" PRId64 "}", area, token); } size_t VehiclePropertyStore::RecordIdHash::operator()(RecordId const& recordId) const { diff --git a/automotive/vehicle/aidl/impl/vhal/Android.bp b/automotive/vehicle/aidl/impl/vhal/Android.bp index a54ab4bbe9..0132e6f695 100644 --- a/automotive/vehicle/aidl/impl/vhal/Android.bp +++ b/automotive/vehicle/aidl/impl/vhal/Android.bp @@ -57,6 +57,8 @@ cc_library { "src/ConnectedClient.cpp", "src/DefaultVehicleHal.cpp", "src/PendingRequestPool.cpp", + "src/RecurrentTimer.cpp", + "src/SubscriptionManager.cpp", ], static_libs: [ "VehicleHalUtils", diff --git a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h index 43a96036cb..15a6278f38 100644 --- a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h +++ b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h @@ -17,6 +17,9 @@ #ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_ConnectedClient_H_ #define android_hardware_automotive_vehicle_aidl_impl_vhal_include_ConnectedClient_H_ +#include "PendingRequestPool.h" + +#include <IVehicleHardware.h> #include <VehicleHalTypes.h> #include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h> @@ -39,15 +42,33 @@ namespace vehicle { // This class is thread-safe. class ConnectedClient { public: - ConnectedClient( - std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> - callback); + using CallbackType = + std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>; + + ConnectedClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback); virtual ~ConnectedClient() = default; + // Gets the unique ID for this client. + const void* id(); + + // Adds client requests. The requests would be registered as pending requests until + // {@code tryFinishRequests} is called for them. + // Returns {@code INVALID_ARG} error if any of the requestIds are duplicate with one of the + // pending request IDs or {@code TRY_AGAIN} error if the pending request pool is full and could + // no longer add requests. + ::android::base::Result<void> addRequests(const std::unordered_set<int64_t>& requestIds); + + // Marks the requests as finished. Returns a list of request IDs that was pending and has been + // finished. It must be a set of the requested request IDs. + std::unordered_set<int64_t> tryFinishRequests(const std::unordered_set<int64_t>& requestIds); + protected: - const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> - mCallback; + // Gets the callback to be called when the request for this client has timeout. + virtual std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> getTimeoutCallback() = 0; + + const std::shared_ptr<PendingRequestPool> mRequestPool; + const CallbackType mCallback; }; // A class to represent a client that calls {@code IVehicle.setValues} or {@code @@ -55,12 +76,10 @@ class ConnectedClient { template <class ResultType, class ResultsType> class GetSetValuesClient final : public ConnectedClient { public: - GetSetValuesClient( - std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> - callback); + GetSetValuesClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback); // Sends the results to this client. - void sendResults(const std::vector<ResultType>& results); + void sendResults(std::vector<ResultType>&& results); // Sends each result separately to this client. Each result would be sent through one callback // invocation. @@ -69,11 +88,47 @@ class GetSetValuesClient final : public ConnectedClient { // Gets the callback to be called when the request for this client has finished. std::shared_ptr<const std::function<void(std::vector<ResultType>)>> getResultCallback(); + protected: + // Gets the callback to be called when the request for this client has timeout. + std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> getTimeoutCallback() override; + private: // The following members are only initialized during construction. + std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> mTimeoutCallback; std::shared_ptr<const std::function<void(std::vector<ResultType>)>> mResultCallback; }; +// A class to represent a client that calls {@code IVehicle.subscribe}. +class SubscriptionClient final : public ConnectedClient { + public: + SubscriptionClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback); + + // Gets the callback to be called when the request for this client has finished. + std::shared_ptr<const IVehicleHardware::GetValuesCallback> getResultCallback(); + + // Marshals the updated values into largeParcelable and sents it through {@code onPropertyEvent} + // callback. + static void sendUpdatedValues( + CallbackType callback, + std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&& + updatedValues); + + protected: + // Gets the callback to be called when the request for this client has timeout. + std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> getTimeoutCallback() override; + + private: + // The following members are only initialized during construction. + std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> mTimeoutCallback; + std::shared_ptr<const IVehicleHardware::GetValuesCallback> mResultCallback; + std::shared_ptr<const IVehicleHardware::PropertyChangeCallback> mPropertyChangeCallback; + + static void onGetValueResults( + const void* clientId, CallbackType callback, + std::shared_ptr<PendingRequestPool> requestPool, + std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult> results); +}; + } // namespace vehicle } // namespace automotive } // namespace hardware diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h index f6f7d80002..5e7adfca6d 100644 --- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h +++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h @@ -19,6 +19,8 @@ #include "ConnectedClient.h" #include "ParcelableUtils.h" +#include "PendingRequestPool.h" +#include "SubscriptionManager.h" #include <IVehicleHardware.h> #include <VehicleUtils.h> @@ -28,6 +30,7 @@ #include <android/binder_auto_utils.h> #include <memory> +#include <mutex> #include <unordered_map> #include <vector> @@ -36,13 +39,6 @@ namespace hardware { namespace automotive { namespace vehicle { -// private namespace -namespace defaultvehiclehal_impl { - -constexpr int INVALID_MEMORY_FD = -1; - -} // namespace defaultvehiclehal_impl - class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::vehicle::BnVehicle { public: using CallbackType = @@ -50,6 +46,8 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve explicit DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware); + ~DefaultVehicleHal(); + ::ndk::ScopedAStatus getAllPropConfigs( ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs* returnConfigs) override; @@ -74,6 +72,7 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve const std::vector<int32_t>& propIds) override; ::ndk::ScopedAStatus returnSharedMemory(const CallbackType& callback, int64_t sharedMemoryId) override; + binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; IVehicleHardware* getHardware(); @@ -88,11 +87,68 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve GetSetValuesClient<::aidl::android::hardware::automotive::vehicle::SetValueResult, ::aidl::android::hardware::automotive::vehicle::SetValueResults>; + // A thread safe class to maintain an increasing request ID for each subscribe client. This + // class is safe to pass to async callbacks. + class SubscribeIdByClient { + public: + int64_t getId(const CallbackType& callback); + + private: + std::mutex mLock; + std::unordered_map<const AIBinder*, int64_t> mIds GUARDED_BY(mLock); + }; + + // A thread safe class to store all subscribe clients. This class is safe to pass to async + // callbacks. + class SubscriptionClients { + public: + SubscriptionClients(std::shared_ptr<PendingRequestPool> pool) : mPendingRequestPool(pool) {} + + std::shared_ptr<SubscriptionClient> getClient(const CallbackType& callback); + + void removeClient(const AIBinder* clientId); + + size_t countClients(); + + private: + std::mutex mLock; + std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>> mClients + GUARDED_BY(mLock); + // PendingRequestPool is thread-safe. + std::shared_ptr<PendingRequestPool> mPendingRequestPool; + }; + + // A wrapper for linkToDeath to enable stubbing for test. + class ILinkToDeath { + public: + virtual ~ILinkToDeath() = default; + + virtual binder_status_t linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, + void* cookie) = 0; + }; + + // A real implementation for ILinkToDeath. + class AIBinderLinkToDeathImpl final : public ILinkToDeath { + public: + binder_status_t linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, + void* cookie) override; + }; + + // OnBinderDiedContext is a type used as a cookie passed deathRecipient. The deathRecipient's + // onBinderDied function takes only a cookie as input and we have to store all the contexts + // as the cookie. + struct OnBinderDiedContext { + DefaultVehicleHal* vhal; + const AIBinder* clientId; + }; + // The default timeout of get or set value requests is 30s. // TODO(b/214605968): define TIMEOUT_IN_NANO in IVehicle and allow getValues/setValues/subscribe // to specify custom timeouts. static constexpr int64_t TIMEOUT_IN_NANO = 30'000'000'000; - const std::unique_ptr<IVehicleHardware> mVehicleHardware; + // heart beat event interval: 3s + static constexpr int64_t HEART_BEAT_INTERVAL_IN_NANO = 3'000'000'000; + const std::shared_ptr<IVehicleHardware> mVehicleHardware; // mConfigsByPropId and mConfigFile are only modified during initialization, so no need to // lock guard them. @@ -100,20 +156,90 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve mConfigsByPropId; // Only modified in constructor, so thread-safe. std::unique_ptr<::ndk::ScopedFileDescriptor> mConfigFile; + // PendingRequestPool is thread-safe. + std::shared_ptr<PendingRequestPool> mPendingRequestPool; + // SubscriptionManager is thread-safe. + std::shared_ptr<SubscriptionManager> mSubscriptionManager; std::mutex mLock; - std::unordered_map<CallbackType, std::shared_ptr<GetValuesClient>> mGetValuesClients + std::unordered_map<const AIBinder*, std::unique_ptr<OnBinderDiedContext>> mOnBinderDiedContexts + GUARDED_BY(mLock); + std::unordered_map<const AIBinder*, std::shared_ptr<GetValuesClient>> mGetValuesClients GUARDED_BY(mLock); - std::unordered_map<CallbackType, std::shared_ptr<SetValuesClient>> mSetValuesClients + std::unordered_map<const AIBinder*, std::shared_ptr<SetValuesClient>> mSetValuesClients GUARDED_BY(mLock); + // SubscriptionClients is thread-safe. + std::shared_ptr<SubscriptionClients> mSubscriptionClients; + // mLinkToDeathImpl is only going to be changed in test. + std::unique_ptr<ILinkToDeath> mLinkToDeathImpl; - template <class T> - std::shared_ptr<T> getOrCreateClient( - std::unordered_map<CallbackType, std::shared_ptr<T>>* clients, - const CallbackType& callback) REQUIRES(mLock); + // RecurrentTimer is thread-safe. + RecurrentTimer mRecurrentTimer; + + ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient; ::android::base::Result<void> checkProperty( const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue); + + ::android::base::Result<std::vector<int64_t>> checkDuplicateRequests( + const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>& + requests); + + ::android::base::Result<std::vector<int64_t>> checkDuplicateRequests( + const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>& + requests); + + ::android::base::Result<void> checkSubscribeOptions( + const std::vector<::aidl::android::hardware::automotive::vehicle::SubscribeOptions>& + options); + + ::android::base::Result<void> checkReadPermission( + const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const; + + ::android::base::Result<void> checkWritePermission( + const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const; + + ::android::base::Result< + const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig*> + getConfig(int32_t propId) const; + + void onBinderDiedWithContext(const AIBinder* clientId); + + void onBinderUnlinkedWithContext(const AIBinder* clientId); + + void monitorBinderLifeCycle(const CallbackType& callback); + + bool checkDumpPermission(); + + template <class T> + static std::shared_ptr<T> getOrCreateClient( + std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients, + const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); + + static void getValueFromHardwareCallCallback( + std::weak_ptr<IVehicleHardware> vehicleHardware, + std::shared_ptr<SubscribeIdByClient> subscribeIdByClient, + std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback, + const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value); + + static void onPropertyChangeEvent( + std::weak_ptr<SubscriptionManager> subscriptionManager, + const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>& + updatedValues); + + static void checkHealth(std::weak_ptr<IVehicleHardware> hardware, + std::weak_ptr<SubscriptionManager> subscriptionManager); + + static void onBinderDied(void* cookie); + + static void onBinderUnlinked(void* cookie); + + // Test-only + // Set the default timeout for pending requests. + void setTimeout(int64_t timeoutInNano); + + // Test-only + void setLinkToDeathImpl(std::unique_ptr<ILinkToDeath> impl); }; } // namespace vehicle diff --git a/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h b/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h index 4b7c2f3221..7b2111b96b 100644 --- a/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h +++ b/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h @@ -29,6 +29,9 @@ namespace hardware { namespace automotive { namespace vehicle { +// Turns the values into a stable large parcelable that could be sent via binder. +// If values is small enough, it would be put into output.payloads, otherwise a shared memory file +// would be created and output.sharedMemoryFd would be filled in. template <class T1, class T2> ::ndk::ScopedAStatus vectorToStableLargeParcelable(std::vector<T1>&& values, T2* output) { output->payloads = std::move(values); @@ -44,6 +47,9 @@ template <class T1, class T2> // 'sharedMemoryFd' field. output->payloads.clear(); output->sharedMemoryFd = std::move(*fd); + } else { + output->sharedMemoryFd = ::ndk::ScopedFileDescriptor(); + // Do not modify payloads. } return ::ndk::ScopedAStatus::ok(); } diff --git a/automotive/vehicle/aidl/impl/vhal/include/PendingRequestPool.h b/automotive/vehicle/aidl/impl/vhal/include/PendingRequestPool.h index 6dcfaff5fa..efb33151bd 100644 --- a/automotive/vehicle/aidl/impl/vhal/include/PendingRequestPool.h +++ b/automotive/vehicle/aidl/impl/vhal/include/PendingRequestPool.h @@ -51,7 +51,7 @@ class PendingRequestPool final { // seconds. android::base::Result<void> addRequests(const void* clientId, const std::unordered_set<int64_t>& requestIds, - std::shared_ptr<TimeoutCallbackFunc> callback); + std::shared_ptr<const TimeoutCallbackFunc> callback); // Checks whether the request is currently pending. bool isRequestPending(const void* clientId, int64_t requestId) const; @@ -66,6 +66,8 @@ class PendingRequestPool final { // Returns how many pending requests in the pool, for testing purpose. size_t countPendingRequests(const void* clientId) const; + size_t countPendingRequests() const; + private: // The maximum number of pending requests allowed per client. If exceeds this number, adding // more requests would fail. This is to prevent spamming from client. @@ -74,7 +76,7 @@ class PendingRequestPool final { struct PendingRequest { std::unordered_set<int64_t> requestIds; int64_t timeoutTimestamp; - std::shared_ptr<TimeoutCallbackFunc> callback; + std::shared_ptr<const TimeoutCallbackFunc> callback; }; int64_t mTimeoutInNano; diff --git a/automotive/vehicle/aidl/impl/vhal/include/RecurrentTimer.h b/automotive/vehicle/aidl/impl/vhal/include/RecurrentTimer.h new file mode 100644 index 0000000000..5f0f7161c2 --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/include/RecurrentTimer.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_RecurrentTimer_H_ +#define android_hardware_automotive_vehicle_aidl_impl_vhal_include_RecurrentTimer_H_ + +#include <android-base/thread_annotations.h> + +#include <memory> +#include <mutex> +#include <queue> +#include <thread> +#include <unordered_map> +#include <vector> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +// A thread-safe recurrent timer. +class RecurrentTimer final { + public: + // The class for the function that would be called recurrently. + using Callback = std::function<void()>; + + RecurrentTimer(); + + ~RecurrentTimer(); + + // Registers a recurrent callback for a given interval. + // Registering the same callback twice will override the interval provided before. + void registerTimerCallback(int64_t intervalInNano, std::shared_ptr<Callback> callback); + + // Unregisters a previously registered recurrent callback. + void unregisterTimerCallback(std::shared_ptr<Callback> callback); + + private: + // friend class for unit testing. + friend class RecurrentTimerTest; + + struct CallbackInfo { + std::shared_ptr<Callback> callback; + int64_t interval; + int64_t nextTime; + // A flag to indicate whether this CallbackInfo is already outdated and should be ignored. + // The reason we need this flag is because we cannot easily remove an element from a heap. + bool outdated = false; + + static bool cmp(const std::unique_ptr<CallbackInfo>& lhs, + const std::unique_ptr<CallbackInfo>& rhs); + }; + + std::mutex mLock; + std::thread mThread; + std::condition_variable mCond; + bool mStopRequested GUARDED_BY(mLock) = false; + // A map to map each callback to its current active CallbackInfo in the mCallbackQueue. + std::unordered_map<std::shared_ptr<Callback>, CallbackInfo*> mCallbacks GUARDED_BY(mLock); + // A min-heap sorted by nextTime. Note that because we cannot remove arbitrary element from the + // heap, a single Callback can have multiple entries in this queue, all but one should be valid. + // The rest should be mark as outdated. The valid one is one stored in mCallbacks. + std::vector<std::unique_ptr<CallbackInfo>> mCallbackQueue GUARDED_BY(mLock); + + void loop(); + + // Mark the callbackInfo as outdated and should be ignored when popped from the heap. + void markOutdatedLocked(CallbackInfo* callback) REQUIRES(mLock); + // Remove all outdated callbackInfos from the top of the heap. This function must be called + // each time we might introduce outdated elements to the top. We must make sure the heap is + // always valid from the top. + void removeInvalidCallbackLocked() REQUIRES(mLock); + // Pops the next closest callback (must be valid) from the heap. + std::unique_ptr<CallbackInfo> popNextCallbackLocked() REQUIRES(mLock); +}; + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_aidl_impl_vhal_include_RecurrentTimer_H_ diff --git a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h new file mode 100644 index 0000000000..e739c8c3f2 --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_SubscriptionManager_H_ +#define android_hardware_automotive_vehicle_aidl_impl_vhal_include_SubscriptionManager_H_ + +#include "RecurrentTimer.h" + +#include <VehicleHalTypes.h> + +#include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h> +#include <android-base/result.h> +#include <android-base/thread_annotations.h> + +#include <mutex> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +// A thread-safe subscription manager that manages all VHAL subscriptions. +class SubscriptionManager final { + public: + using ClientIdType = const AIBinder*; + using CallbackType = + std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>; + using GetValueFunc = std::function<void( + const CallbackType& callback, + const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)>; + + explicit SubscriptionManager(GetValueFunc&& action); + ~SubscriptionManager(); + + // Subscribes to properties according to {@code SubscribeOptions}. Note that all option must + // contain non-empty areaIds field, which contains all area IDs to subscribe. As a result, + // the options here is different from the options passed from VHAL client. + // Returns error if any of the subscribe options is not valid. If error is returned, no + // properties would be subscribed. + // Returns ok if all the options are parsed correctly and all the properties are subscribed. + ::android::base::Result<void> subscribe( + const CallbackType& callback, + const std::vector<::aidl::android::hardware::automotive::vehicle::SubscribeOptions>& + options, + bool isContinuousProperty); + + // Unsubscribes from the properties for the client. + // Returns error if the client was not subscribed before or one of the given property was not + // subscribed. If error is returned, no property would be unsubscribed. + // Returns ok if all the requested properties for the client are unsubscribed. + ::android::base::Result<void> unsubscribe(ClientIdType client, + const std::vector<int32_t>& propIds); + + // Unsubscribes from all the properties for the client. + // Returns error if the client was not subscribed before. If error is returned, no property + // would be unsubscribed. + // Returns ok if all the properties for the client are unsubscribed. + ::android::base::Result<void> unsubscribe(ClientIdType client); + + // For a list of updated properties, returns a map that maps clients subscribing to + // the updated properties to a list of updated values. This would only return on-change property + // clients that should be informed for the given updated values. + std::unordered_map< + CallbackType, + std::vector<const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue*>> + getSubscribedClients( + const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>& + updatedValues); + + // Checks whether the sample rate is valid. + static bool checkSampleRate(float sampleRate); + + private: + // Friend class for testing. + friend class DefaultVehicleHalTest; + + struct PropIdAreaId { + int32_t propId; + int32_t areaId; + + bool operator==(const PropIdAreaId& other) const; + }; + + struct PropIdAreaIdHash { + size_t operator()(const PropIdAreaId& propIdAreaId) const; + }; + + // A class to represent a registered subscription. + class Subscription { + public: + Subscription() = default; + + Subscription(const Subscription&) = delete; + + virtual ~Subscription() = default; + + virtual bool isOnChange(); + }; + + // A subscription for OnContinuous property. The registered action would be called recurrently + // until this class is destructed. + class RecurrentSubscription final : public Subscription { + public: + explicit RecurrentSubscription(std::shared_ptr<RecurrentTimer> timer, + std::function<void()>&& action, int64_t interval); + ~RecurrentSubscription(); + + bool isOnChange() override; + + private: + std::shared_ptr<std::function<void()>> mAction; + std::shared_ptr<RecurrentTimer> mTimer; + }; + + // A subscription for OnChange property. + class OnChangeSubscription final : public Subscription { + public: + bool isOnChange() override; + }; + + mutable std::mutex mLock; + std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>, + PropIdAreaIdHash> + mClientsByPropIdArea GUARDED_BY(mLock); + std::unordered_map<ClientIdType, std::unordered_map<PropIdAreaId, std::unique_ptr<Subscription>, + PropIdAreaIdHash>> + mSubscriptionsByClient GUARDED_BY(mLock); + // RecurrentTimer is thread-safe. + std::shared_ptr<RecurrentTimer> mTimer; + const GetValueFunc mGetValue; + + static ::android::base::Result<int64_t> getInterval(float sampleRate); + + // Checks whether the manager is empty. For testing purpose. + bool isEmpty(); +}; + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_aidl_impl_vhal_include_SubscriptionManager_H_ diff --git a/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp index 656dfaf579..098bfee1bb 100644 --- a/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp +++ b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp @@ -84,9 +84,9 @@ void sendGetOrSetValueResultsSeparately(std::shared_ptr<IVehicleCallback> callba // Send all the GetValue/SetValue results through callback in a single callback invocation. template <class ResultType, class ResultsType> void sendGetOrSetValueResults(std::shared_ptr<IVehicleCallback> callback, - const std::vector<ResultType>& results) { + std::vector<ResultType>&& results) { ResultsType parcelableResults; - ScopedAStatus status = vectorToStableLargeParcelable(results, &parcelableResults); + ScopedAStatus status = vectorToStableLargeParcelable(std::move(results), &parcelableResults); if (status.isOk()) { if (ScopedAStatus callbackStatus = callCallback(callback, parcelableResults); !callbackStatus.isOk()) { @@ -99,7 +99,55 @@ void sendGetOrSetValueResults(std::shared_ptr<IVehicleCallback> callback, ALOGE("failed to marshal result into large parcelable, error: " "%s, code: %d", status.getMessage(), statusCode); - sendGetOrSetValueResultsSeparately<ResultType, ResultsType>(callback, results); + sendGetOrSetValueResultsSeparately<ResultType, ResultsType>(callback, + parcelableResults.payloads); +} + +// The timeout callback for GetValues/SetValues. +template <class ResultType, class ResultsType> +void onTimeout( + std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback, + const std::unordered_set<int64_t>& timeoutIds) { + std::vector<ResultType> timeoutResults; + for (int64_t requestId : timeoutIds) { + ALOGD("hardware request timeout, request ID: %" PRId64, requestId); + timeoutResults.push_back({ + .requestId = requestId, + .status = StatusCode::TRY_AGAIN, + }); + } + sendGetOrSetValueResults<ResultType, ResultsType>(callback, std::move(timeoutResults)); +} + +// The on-results callback for GetValues/SetValues. +template <class ResultType, class ResultsType> +void getOrSetValuesCallback( + const void* clientId, + std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback, + std::vector<ResultType>&& results, std::shared_ptr<PendingRequestPool> requestPool) { + std::unordered_set<int64_t> requestIds; + for (const auto& result : results) { + requestIds.insert(result.requestId); + } + + auto finishedRequests = requestPool->tryFinishRequests(clientId, requestIds); + + auto it = results.begin(); + while (it != results.end()) { + int64_t requestId = it->requestId; + if (finishedRequests.find(requestId) == finishedRequests.end()) { + ALOGD("no pending request for the result from hardware, " + "possibly already time-out, ID: %" PRId64, + requestId); + it = results.erase(it); + } else { + it++; + } + } + + if (!results.empty()) { + sendGetOrSetValueResults<ResultType, ResultsType>(callback, std::move(results)); + } } // Specify the functions for GetValues and SetValues types. @@ -109,27 +157,64 @@ template void sendGetOrSetValueResult<SetValueResult, SetValueResults>( std::shared_ptr<IVehicleCallback> callback, const SetValueResult& result); template void sendGetOrSetValueResults<GetValueResult, GetValueResults>( - std::shared_ptr<IVehicleCallback> callback, const std::vector<GetValueResult>& results); + std::shared_ptr<IVehicleCallback> callback, std::vector<GetValueResult>&& results); template void sendGetOrSetValueResults<SetValueResult, SetValueResults>( - std::shared_ptr<IVehicleCallback> callback, const std::vector<SetValueResult>& results); + std::shared_ptr<IVehicleCallback> callback, std::vector<SetValueResult>&& results); template void sendGetOrSetValueResultsSeparately<GetValueResult, GetValueResults>( std::shared_ptr<IVehicleCallback> callback, const std::vector<GetValueResult>& results); template void sendGetOrSetValueResultsSeparately<SetValueResult, SetValueResults>( std::shared_ptr<IVehicleCallback> callback, const std::vector<SetValueResult>& results); +template void onTimeout<GetValueResult, GetValueResults>( + std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback, + const std::unordered_set<int64_t>& timeoutIds); +template void onTimeout<SetValueResult, SetValueResults>( + std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback, + const std::unordered_set<int64_t>& timeoutIds); + +template void getOrSetValuesCallback<GetValueResult, GetValueResults>( + const void* clientId, + std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback, + std::vector<GetValueResult>&& results, std::shared_ptr<PendingRequestPool> requestPool); +template void getOrSetValuesCallback<SetValueResult, SetValueResults>( + const void* clientId, + std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback, + std::vector<SetValueResult>&& results, std::shared_ptr<PendingRequestPool> requestPool); + } // namespace -ConnectedClient::ConnectedClient(std::shared_ptr<IVehicleCallback> callback) - : mCallback(callback) {} +ConnectedClient::ConnectedClient(std::shared_ptr<PendingRequestPool> requestPool, + std::shared_ptr<IVehicleCallback> callback) + : mRequestPool(requestPool), mCallback(callback) {} + +const void* ConnectedClient::id() { + return reinterpret_cast<const void*>(this); +} + +Result<void> ConnectedClient::addRequests(const std::unordered_set<int64_t>& requestIds) { + return mRequestPool->addRequests(id(), requestIds, getTimeoutCallback()); +} + +std::unordered_set<int64_t> ConnectedClient::tryFinishRequests( + const std::unordered_set<int64_t>& requestIds) { + return mRequestPool->tryFinishRequests(id(), requestIds); +} template <class ResultType, class ResultsType> GetSetValuesClient<ResultType, ResultsType>::GetSetValuesClient( - std::shared_ptr<IVehicleCallback> callback) - : ConnectedClient(callback) { + std::shared_ptr<PendingRequestPool> requestPool, std::shared_ptr<IVehicleCallback> callback) + : ConnectedClient(requestPool, callback) { + mTimeoutCallback = std::make_shared<const PendingRequestPool::TimeoutCallbackFunc>( + [callback](const std::unordered_set<int64_t>& timeoutIds) { + return onTimeout<ResultType, ResultsType>(callback, timeoutIds); + }); + auto requestPoolCopy = mRequestPool; + const void* clientId = id(); mResultCallback = std::make_shared<const std::function<void(std::vector<ResultType>)>>( - [callback](std::vector<ResultType> results) { - return sendGetOrSetValueResults<ResultType, ResultsType>(callback, results); + [clientId, callback, requestPoolCopy](std::vector<ResultType> results) { + return getOrSetValuesCallback<ResultType, ResultsType>( + clientId, callback, std::move(results), requestPoolCopy); }); } @@ -140,9 +225,14 @@ GetSetValuesClient<ResultType, ResultsType>::getResultCallback() { } template <class ResultType, class ResultsType> -void GetSetValuesClient<ResultType, ResultsType>::sendResults( - const std::vector<ResultType>& results) { - return sendGetOrSetValueResults<ResultType, ResultsType>(mCallback, results); +std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> +GetSetValuesClient<ResultType, ResultsType>::getTimeoutCallback() { + return mTimeoutCallback; +} + +template <class ResultType, class ResultsType> +void GetSetValuesClient<ResultType, ResultsType>::sendResults(std::vector<ResultType>&& results) { + return sendGetOrSetValueResults<ResultType, ResultsType>(mCallback, std::move(results)); } template <class ResultType, class ResultsType> @@ -154,6 +244,100 @@ void GetSetValuesClient<ResultType, ResultsType>::sendResultsSeparately( template class GetSetValuesClient<GetValueResult, GetValueResults>; template class GetSetValuesClient<SetValueResult, SetValueResults>; +SubscriptionClient::SubscriptionClient(std::shared_ptr<PendingRequestPool> requestPool, + std::shared_ptr<IVehicleCallback> callback) + : ConnectedClient(requestPool, callback) { + mTimeoutCallback = std::make_shared<const PendingRequestPool::TimeoutCallbackFunc>( + [](std::unordered_set<int64_t> timeoutIds) { + for (int64_t id : timeoutIds) { + ALOGW("subscribe: requests with IDs: %" PRId64 + " has timed-out, not client informed, " + "possibly one of recurrent requests for this subscription failed", + id); + } + }); + auto requestPoolCopy = mRequestPool; + const void* clientId = reinterpret_cast<const void*>(this); + mResultCallback = std::make_shared<const IVehicleHardware::GetValuesCallback>( + [clientId, callback, requestPoolCopy](std::vector<GetValueResult> results) { + onGetValueResults(clientId, callback, requestPoolCopy, results); + }); +} + +std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> +SubscriptionClient::getResultCallback() { + return mResultCallback; +} + +std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> +SubscriptionClient::getTimeoutCallback() { + return mTimeoutCallback; +} + +void SubscriptionClient::sendUpdatedValues(std::shared_ptr<IVehicleCallback> callback, + std::vector<VehiclePropValue>&& updatedValues) { + if (updatedValues.empty()) { + return; + } + + // TODO(b/205189110): Use memory pool here and fill in sharedMemoryId. + VehiclePropValues vehiclePropValues; + int32_t sharedMemoryFileCount = 0; + ScopedAStatus status = + vectorToStableLargeParcelable(std::move(updatedValues), &vehiclePropValues); + if (!status.isOk()) { + int statusCode = status.getServiceSpecificError(); + ALOGE("subscribe: failed to marshal result into large parcelable, error: " + "%s, code: %d", + status.getMessage(), statusCode); + return; + } + + if (ScopedAStatus callbackStatus = + callback->onPropertyEvent(vehiclePropValues, sharedMemoryFileCount); + !callbackStatus.isOk()) { + ALOGE("subscribe: failed to call callback, error: %s, code: %d", status.getMessage(), + status.getServiceSpecificError()); + } +} + +void SubscriptionClient::onGetValueResults(const void* clientId, + std::shared_ptr<IVehicleCallback> callback, + std::shared_ptr<PendingRequestPool> requestPool, + std::vector<GetValueResult> results) { + std::unordered_set<int64_t> requestIds; + for (const auto& result : results) { + requestIds.insert(result.requestId); + } + + auto finishedRequests = requestPool->tryFinishRequests(clientId, requestIds); + std::vector<VehiclePropValue> propValues; + for (auto& result : results) { + int64_t requestId = result.requestId; + if (finishedRequests.find(requestId) == finishedRequests.end()) { + ALOGE("subscribe[%" PRId64 + "]: no pending request for the result from hardware, " + "possibly already time-out", + requestId); + continue; + } + if (result.status != StatusCode::OK) { + ALOGE("subscribe[%" PRId64 + "]: hardware returns non-ok status for getValues, status: " + "%d", + requestId, toInt(result.status)); + continue; + } + if (!result.prop.has_value()) { + ALOGE("subscribe[%" PRId64 "]: no prop value in getValues result", requestId); + continue; + } + propValues.push_back(std::move(result.prop.value())); + } + + sendUpdatedValues(callback, std::move(propValues)); +} + } // namespace vehicle } // namespace automotive } // namespace hardware diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp index 7f09a59fbb..c0a66daf75 100644 --- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp +++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp @@ -23,18 +23,27 @@ #include <VehicleUtils.h> #include <android-base/result.h> +#include <android-base/stringprintf.h> +#include <android/binder_ibinder.h> +#include <private/android_filesystem_config.h> #include <utils/Log.h> +#include <utils/SystemClock.h> + +#include <inttypes.h> +#include <set> +#include <unordered_set> namespace android { namespace hardware { namespace automotive { namespace vehicle { +namespace { + using ::aidl::android::hardware::automotive::vehicle::GetValueRequest; using ::aidl::android::hardware::automotive::vehicle::GetValueRequests; using ::aidl::android::hardware::automotive::vehicle::GetValueResult; using ::aidl::android::hardware::automotive::vehicle::GetValueResults; -using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback; using ::aidl::android::hardware::automotive::vehicle::SetValueRequest; using ::aidl::android::hardware::automotive::vehicle::SetValueRequests; using ::aidl::android::hardware::automotive::vehicle::SetValueResult; @@ -44,15 +53,59 @@ using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs; +using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::automotive::car_binder_lib::LargeParcelableBase; using ::android::base::Error; using ::android::base::expected; using ::android::base::Result; +using ::android::base::StringPrintf; + +using ::ndk::ScopedAIBinder_DeathRecipient; using ::ndk::ScopedAStatus; +std::string toString(const std::unordered_set<int64_t>& values) { + std::string str = ""; + for (auto it = values.begin(); it != values.end(); it++) { + str += std::to_string(*it); + if (std::next(it, 1) != values.end()) { + str += ", "; + } + } + return str; +} + +} // namespace + +std::shared_ptr<SubscriptionClient> DefaultVehicleHal::SubscriptionClients::getClient( + const CallbackType& callback) { + std::scoped_lock<std::mutex> lockGuard(mLock); + return getOrCreateClient(&mClients, callback, mPendingRequestPool); +} + +int64_t DefaultVehicleHal::SubscribeIdByClient::getId(const CallbackType& callback) { + std::scoped_lock<std::mutex> lockGuard(mLock); + // This would be initialized to 0 if callback does not exist in the map. + int64_t subscribeId = (mIds[callback->asBinder().get()])++; + return subscribeId; +} + +void DefaultVehicleHal::SubscriptionClients::removeClient(const AIBinder* clientId) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mClients.erase(clientId); +} + +size_t DefaultVehicleHal::SubscriptionClients::countClients() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mClients.size(); +} + DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware) - : mVehicleHardware(std::move(hardware)) { + : mVehicleHardware(std::move(hardware)), + mPendingRequestPool(std::make_shared<PendingRequestPool>(TIMEOUT_IN_NANO)) { auto configs = mVehicleHardware->getAllPropertyConfigs(); for (auto& config : configs) { mConfigsByPropId[config.prop] = config; @@ -69,6 +122,168 @@ DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware) if (result.value() != nullptr) { mConfigFile = std::move(result.value()); } + + mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool); + + auto subscribeIdByClient = std::make_shared<SubscribeIdByClient>(); + // Make a weak copy of IVehicleHardware because subscriptionManager uses IVehicleHardware and + // IVehicleHardware uses subscriptionManager. We want to avoid cyclic reference. + std::weak_ptr<IVehicleHardware> hardwareCopy = mVehicleHardware; + SubscriptionManager::GetValueFunc getValueFunc = std::bind( + &DefaultVehicleHal::getValueFromHardwareCallCallback, hardwareCopy, subscribeIdByClient, + mSubscriptionClients, std::placeholders::_1, std::placeholders::_2); + mSubscriptionManager = std::make_shared<SubscriptionManager>(std::move(getValueFunc)); + + std::weak_ptr<SubscriptionManager> subscriptionManagerCopy = mSubscriptionManager; + mVehicleHardware->registerOnPropertyChangeEvent( + std::make_unique<IVehicleHardware::PropertyChangeCallback>( + [subscriptionManagerCopy](std::vector<VehiclePropValue> updatedValues) { + onPropertyChangeEvent(subscriptionManagerCopy, updatedValues); + })); + + // Register heartbeat event. + mRecurrentTimer.registerTimerCallback( + HEART_BEAT_INTERVAL_IN_NANO, + std::make_shared<std::function<void()>>([hardwareCopy, subscriptionManagerCopy]() { + checkHealth(hardwareCopy, subscriptionManagerCopy); + })); + + mLinkToDeathImpl = std::make_unique<AIBinderLinkToDeathImpl>(); + mDeathRecipient = ScopedAIBinder_DeathRecipient( + AIBinder_DeathRecipient_new(&DefaultVehicleHal::onBinderDied)); + AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), + &DefaultVehicleHal::onBinderUnlinked); +} + +DefaultVehicleHal::~DefaultVehicleHal() { + // Delete the deathRecipient so that onBinderDied would not be called to reference 'this'. + mDeathRecipient = ScopedAIBinder_DeathRecipient(); +} + +void DefaultVehicleHal::onPropertyChangeEvent( + std::weak_ptr<SubscriptionManager> subscriptionManager, + const std::vector<VehiclePropValue>& updatedValues) { + auto manager = subscriptionManager.lock(); + if (manager == nullptr) { + ALOGW("the SubscriptionManager is destroyed, DefaultVehicleHal is ending"); + return; + } + auto updatedValuesByClients = manager->getSubscribedClients(updatedValues); + for (const auto& [callback, valuePtrs] : updatedValuesByClients) { + std::vector<VehiclePropValue> values; + for (const VehiclePropValue* valuePtr : valuePtrs) { + values.push_back(*valuePtr); + } + SubscriptionClient::sendUpdatedValues(callback, std::move(values)); + } +} + +template <class T> +std::shared_ptr<T> DefaultVehicleHal::getOrCreateClient( + std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients, + const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool) { + const AIBinder* clientId = callback->asBinder().get(); + if (clients->find(clientId) == clients->end()) { + (*clients)[clientId] = std::make_shared<T>(pendingRequestPool, callback); + } + return (*clients)[clientId]; +} + +void DefaultVehicleHal::monitorBinderLifeCycle(const CallbackType& callback) { + AIBinder* clientId = callback->asBinder().get(); + { + std::scoped_lock<std::mutex> lockGuard(mLock); + if (mOnBinderDiedContexts.find(clientId) != mOnBinderDiedContexts.end()) { + // Already registered. + return; + } + } + + std::unique_ptr<OnBinderDiedContext> context = std::make_unique<OnBinderDiedContext>( + OnBinderDiedContext{.vhal = this, .clientId = clientId}); + binder_status_t status = mLinkToDeathImpl->linkToDeath(clientId, mDeathRecipient.get(), + static_cast<void*>(context.get())); + if (status == STATUS_OK) { + std::scoped_lock<std::mutex> lockGuard(mLock); + // Insert into a map to keep the context object alive. + mOnBinderDiedContexts[clientId] = std::move(context); + } else { + ALOGE("failed to call linkToDeath on client binder, status: %d", static_cast<int>(status)); + } +} + +void DefaultVehicleHal::onBinderDied(void* cookie) { + OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie); + context->vhal->onBinderDiedWithContext(context->clientId); +} + +void DefaultVehicleHal::onBinderDiedWithContext(const AIBinder* clientId) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mSetValuesClients.erase(clientId); + mGetValuesClients.erase(clientId); + mSubscriptionClients->removeClient(clientId); + mSubscriptionManager->unsubscribe(clientId); +} + +void DefaultVehicleHal::onBinderUnlinked(void* cookie) { + // Delete the context associated with this cookie. + OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie); + context->vhal->onBinderUnlinkedWithContext(context->clientId); +} + +void DefaultVehicleHal::onBinderUnlinkedWithContext(const AIBinder* clientId) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mOnBinderDiedContexts.erase(clientId); +} + +template std::shared_ptr<DefaultVehicleHal::GetValuesClient> +DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::GetValuesClient>( + std::unordered_map<const AIBinder*, std::shared_ptr<GetValuesClient>>* clients, + const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); +template std::shared_ptr<DefaultVehicleHal::SetValuesClient> +DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::SetValuesClient>( + std::unordered_map<const AIBinder*, std::shared_ptr<SetValuesClient>>* clients, + const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); +template std::shared_ptr<SubscriptionClient> +DefaultVehicleHal::getOrCreateClient<SubscriptionClient>( + std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>>* clients, + const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); + +void DefaultVehicleHal::getValueFromHardwareCallCallback( + std::weak_ptr<IVehicleHardware> vehicleHardware, + std::shared_ptr<SubscribeIdByClient> subscribeIdByClient, + std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback, + const VehiclePropValue& value) { + int64_t subscribeId = subscribeIdByClient->getId(callback); + auto client = subscriptionClients->getClient(callback); + if (auto addRequestResult = client->addRequests({subscribeId}); !addRequestResult.ok()) { + ALOGE("subscribe[%" PRId64 "]: too many pending requests, ignore the getValue request", + subscribeId); + return; + } + + std::vector<GetValueRequest> hardwareRequests = {{ + .requestId = subscribeId, + .prop = value, + }}; + + std::shared_ptr<IVehicleHardware> hardware = vehicleHardware.lock(); + if (hardware == nullptr) { + ALOGW("the IVehicleHardware is destroyed, DefaultVehicleHal is ending"); + return; + } + if (StatusCode status = hardware->getValues(client->getResultCallback(), hardwareRequests); + status != StatusCode::OK) { + // If the hardware returns error, finish all the pending requests for this request because + // we never expect hardware to call callback for these requests. + client->tryFinishRequests({subscribeId}); + ALOGE("subscribe[%" PRId64 "]: failed to get value from VehicleHardware, code: %d", + subscribeId, toInt(status)); + } +} + +void DefaultVehicleHal::setTimeout(int64_t timeoutInNano) { + mPendingRequestPool = std::make_unique<PendingRequestPool>(timeoutInNano); } ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs* output) { @@ -84,55 +299,42 @@ ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs* output) { return ScopedAStatus::ok(); } -template <class T> -std::shared_ptr<T> DefaultVehicleHal::getOrCreateClient( - std::unordered_map<CallbackType, std::shared_ptr<T>>* clients, - const CallbackType& callback) { - if (clients->find(callback) == clients->end()) { - // TODO(b/204943359): Remove client from clients when linkToDeath is implemented. - (*clients)[callback] = std::make_shared<T>(callback); +Result<const VehiclePropConfig*> DefaultVehicleHal::getConfig(int32_t propId) const { + auto it = mConfigsByPropId.find(propId); + if (it == mConfigsByPropId.end()) { + return Error() << "no config for property, ID: " << propId; } - return (*clients)[callback]; + return &(it->second); } -template std::shared_ptr<DefaultVehicleHal::GetValuesClient> -DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::GetValuesClient>( - std::unordered_map<CallbackType, std::shared_ptr<GetValuesClient>>* clients, - const CallbackType& callback); - -template std::shared_ptr<DefaultVehicleHal::SetValuesClient> -DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::SetValuesClient>( - std::unordered_map<CallbackType, std::shared_ptr<SetValuesClient>>* clients, - const CallbackType& callback); - Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue) { int32_t propId = propValue.prop; - auto it = mConfigsByPropId.find(propId); - if (it == mConfigsByPropId.end()) { - return Error() << "no config for property, ID: " << propId; + auto result = getConfig(propId); + if (!result.ok()) { + return result.error(); } - const VehiclePropConfig& config = it->second; - const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, config); + const VehiclePropConfig* config = result.value(); + const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, *config); if (!isGlobalProp(propId) && areaConfig == nullptr) { // Ignore areaId for global property. For non global property, check whether areaId is // allowed. areaId must appear in areaConfig. return Error() << "invalid area ID: " << propValue.areaId << " for prop ID: " << propId << ", not listed in config"; } - if (auto result = checkPropValue(propValue, &config); !result.ok()) { + if (auto result = checkPropValue(propValue, config); !result.ok()) { return Error() << "invalid property value: " << propValue.toString() - << ", error: " << result.error().message(); + << ", error: " << getErrorMsg(result); } if (auto result = checkValueRange(propValue, areaConfig); !result.ok()) { return Error() << "property value out of range: " << propValue.toString() - << ", error: " << result.error().message(); + << ", error: " << getErrorMsg(result); } return {}; } ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback, const GetValueRequests& requests) { - // TODO(b/203713317): check for duplicate properties and duplicate request IDs. + monitorBinderLifeCycle(callback); expected<LargeParcelableBase::BorrowedOwnedObject<GetValueRequests>, ScopedAStatus> deserializedResults = fromStableLargeParcelable(requests); @@ -143,25 +345,74 @@ ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback, const std::vector<GetValueRequest>& getValueRequests = deserializedResults.value().getObject()->payloads; + auto maybeRequestIds = checkDuplicateRequests(getValueRequests); + if (!maybeRequestIds.ok()) { + ALOGE("getValues: duplicate request ID"); + return toScopedAStatus(maybeRequestIds, StatusCode::INVALID_ARG); + } + + // A list of failed result we already know before sending to hardware. + std::vector<GetValueResult> failedResults; + // The list of requests that we would send to hardware. + std::vector<GetValueRequest> hardwareRequests; + + for (const auto& request : getValueRequests) { + if (auto result = checkReadPermission(request.prop); !result.ok()) { + ALOGW("property does not support reading: %s", getErrorMsg(result).c_str()); + failedResults.push_back(GetValueResult{ + .requestId = request.requestId, + .status = getErrorCode(result), + .prop = {}, + }); + } else { + hardwareRequests.push_back(request); + } + } + + // The set of request Ids that we would send to hardware. + std::unordered_set<int64_t> hardwareRequestIds; + for (const auto& request : hardwareRequests) { + hardwareRequestIds.insert(request.requestId); + } + std::shared_ptr<GetValuesClient> client; { std::scoped_lock<std::mutex> lockGuard(mLock); - client = getOrCreateClient(&mGetValuesClients, callback); + client = getOrCreateClient(&mGetValuesClients, callback, mPendingRequestPool); + } + // Register the pending hardware requests and also check for duplicate request Ids. + if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) { + ALOGE("getValues[%s]: failed to add pending requests, error: %s", + toString(hardwareRequestIds).c_str(), getErrorMsg(addRequestResult).c_str()); + return toScopedAStatus(addRequestResult); + } + + if (!failedResults.empty()) { + // First send the failed results we already know back to the client. + client->sendResults(std::move(failedResults)); + } + + if (hardwareRequests.empty()) { + return ScopedAStatus::ok(); } if (StatusCode status = - mVehicleHardware->getValues(client->getResultCallback(), getValueRequests); + mVehicleHardware->getValues(client->getResultCallback(), hardwareRequests); status != StatusCode::OK) { + // If the hardware returns error, finish all the pending requests for this request because + // we never expect hardware to call callback for these requests. + client->tryFinishRequests(hardwareRequestIds); + ALOGE("getValues[%s]: failed to get value from VehicleHardware, status: %d", + toString(hardwareRequestIds).c_str(), toInt(status)); return ScopedAStatus::fromServiceSpecificErrorWithMessage( toInt(status), "failed to get value from VehicleHardware"); } - return ScopedAStatus::ok(); } ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback, const SetValueRequests& requests) { - // TODO(b/203713317): check for duplicate properties and duplicate request IDs. + monitorBinderLifeCycle(callback); expected<LargeParcelableBase::BorrowedOwnedObject<SetValueRequests>, ScopedAStatus> deserializedResults = fromStableLargeParcelable(requests); @@ -177,32 +428,71 @@ ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback, // The list of requests that we would send to hardware. std::vector<SetValueRequest> hardwareRequests; + auto maybeRequestIds = checkDuplicateRequests(setValueRequests); + if (!maybeRequestIds.ok()) { + ALOGE("setValues: duplicate request ID"); + return toScopedAStatus(maybeRequestIds, StatusCode::INVALID_ARG); + } + for (auto& request : setValueRequests) { int64_t requestId = request.requestId; + if (auto result = checkWritePermission(request.value); !result.ok()) { + ALOGW("property does not support writing: %s", getErrorMsg(result).c_str()); + failedResults.push_back(SetValueResult{ + .requestId = requestId, + .status = getErrorCode(result), + }); + continue; + } if (auto result = checkProperty(request.value); !result.ok()) { - ALOGW("property not valid: %s", result.error().message().c_str()); + ALOGW("setValues[%" PRId64 "]: property is not valid: %s", requestId, + getErrorMsg(result).c_str()); failedResults.push_back(SetValueResult{ .requestId = requestId, .status = StatusCode::INVALID_ARG, }); continue; } + hardwareRequests.push_back(request); } + // The set of request Ids that we would send to hardware. + std::unordered_set<int64_t> hardwareRequestIds; + for (const auto& request : hardwareRequests) { + hardwareRequestIds.insert(request.requestId); + } + std::shared_ptr<SetValuesClient> client; { std::scoped_lock<std::mutex> lockGuard(mLock); - client = getOrCreateClient(&mSetValuesClients, callback); + client = getOrCreateClient(&mSetValuesClients, callback, mPendingRequestPool); + } + + // Register the pending hardware requests and also check for duplicate request Ids. + if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) { + ALOGE("setValues[%s], failed to add pending requests, error: %s", + toString(hardwareRequestIds).c_str(), getErrorMsg(addRequestResult).c_str()); + return toScopedAStatus(addRequestResult, StatusCode::INVALID_ARG); } if (!failedResults.empty()) { - client->sendResults(failedResults); + // First send the failed results we already know back to the client. + client->sendResults(std::move(failedResults)); + } + + if (hardwareRequests.empty()) { + return ScopedAStatus::ok(); } if (StatusCode status = mVehicleHardware->setValues(client->getResultCallback(), hardwareRequests); status != StatusCode::OK) { + // If the hardware returns error, finish all the pending requests for this request because + // we never expect hardware to call callback for these requests. + client->tryFinishRequests(hardwareRequestIds); + ALOGE("setValues[%s], failed to set value to VehicleHardware, status: %d", + toString(hardwareRequestIds).c_str(), toInt(status)); return ScopedAStatus::fromServiceSpecificErrorWithMessage( toInt(status), "failed to set value to VehicleHardware"); } @@ -210,6 +500,34 @@ ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback, return ScopedAStatus::ok(); } +#define CHECK_DUPLICATE_REQUESTS(PROP_NAME) \ + do { \ + std::vector<int64_t> requestIds; \ + std::set<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> requestProps; \ + for (const auto& request : requests) { \ + const auto& prop = request.PROP_NAME; \ + if (requestProps.count(prop) != 0) { \ + return ::android::base::Error() \ + << "duplicate request for property: " << prop.toString(); \ + } \ + requestProps.insert(prop); \ + requestIds.push_back(request.requestId); \ + } \ + return requestIds; \ + } while (0); + +::android::base::Result<std::vector<int64_t>> DefaultVehicleHal::checkDuplicateRequests( + const std::vector<GetValueRequest>& requests) { + CHECK_DUPLICATE_REQUESTS(prop); +} + +::android::base::Result<std::vector<int64_t>> DefaultVehicleHal::checkDuplicateRequests( + const std::vector<SetValueRequest>& requests) { + CHECK_DUPLICATE_REQUESTS(value); +} + +#undef CHECK_DUPLICATE_REQUESTS + ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>& props, VehiclePropConfigs* output) { std::vector<VehiclePropConfig> configs; @@ -221,17 +539,114 @@ ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>& prop return vectorToStableLargeParcelable(std::move(configs), output); } -ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType&, - const std::vector<SubscribeOptions>&, int32_t) { - // TODO(b/200737967): implement this. - return ScopedAStatus::ok(); +Result<void> DefaultVehicleHal::checkSubscribeOptions( + const std::vector<SubscribeOptions>& options) { + for (const auto& option : options) { + int32_t propId = option.propId; + if (mConfigsByPropId.find(propId) == mConfigsByPropId.end()) { + return Error(toInt(StatusCode::INVALID_ARG)) + << StringPrintf("no config for property, ID: %" PRId32, propId); + } + const VehiclePropConfig& config = mConfigsByPropId[propId]; + + if (config.changeMode != VehiclePropertyChangeMode::ON_CHANGE && + config.changeMode != VehiclePropertyChangeMode::CONTINUOUS) { + return Error(toInt(StatusCode::INVALID_ARG)) + << "only support subscribing to ON_CHANGE or CONTINUOUS property"; + } + + if (config.access != VehiclePropertyAccess::READ && + config.access != VehiclePropertyAccess::READ_WRITE) { + return Error(toInt(StatusCode::ACCESS_DENIED)) + << StringPrintf("Property %" PRId32 " has no read access", propId); + } + + if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) { + float sampleRate = option.sampleRate; + float minSampleRate = config.minSampleRate; + float maxSampleRate = config.maxSampleRate; + if (sampleRate < minSampleRate || sampleRate > maxSampleRate) { + return Error(toInt(StatusCode::INVALID_ARG)) + << StringPrintf("sample rate: %f out of range, must be within %f and %f", + sampleRate, minSampleRate, maxSampleRate); + } + if (!SubscriptionManager::checkSampleRate(sampleRate)) { + return Error(toInt(StatusCode::INVALID_ARG)) + << "invalid sample rate: " << sampleRate; + } + } + + if (isGlobalProp(propId)) { + continue; + } + + // Non-global property. + for (int32_t areaId : option.areaIds) { + if (auto areaConfig = getAreaConfig(propId, areaId, config); areaConfig == nullptr) { + return Error(toInt(StatusCode::INVALID_ARG)) + << StringPrintf("invalid area ID: %" PRId32 " for prop ID: %" PRId32 + ", not listed in config", + areaId, propId); + } + } + } + return {}; } -ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType&, const std::vector<int32_t>&) { - // TODO(b/200737967): implement this. +ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback, + const std::vector<SubscribeOptions>& options, + [[maybe_unused]] int32_t maxSharedMemoryFileCount) { + monitorBinderLifeCycle(callback); + + // TODO(b/205189110): Use shared memory file count. + if (auto result = checkSubscribeOptions(options); !result.ok()) { + ALOGE("subscribe: invalid subscribe options: %s", getErrorMsg(result).c_str()); + return toScopedAStatus(result); + } + + std::vector<SubscribeOptions> onChangeSubscriptions; + std::vector<SubscribeOptions> continuousSubscriptions; + for (const auto& option : options) { + int32_t propId = option.propId; + // We have already validate config exists. + const VehiclePropConfig& config = mConfigsByPropId[propId]; + + SubscribeOptions optionCopy = option; + // If areaIds is empty, subscribe to all areas. + if (optionCopy.areaIds.empty() && !isGlobalProp(propId)) { + for (const auto& areaConfig : config.areaConfigs) { + optionCopy.areaIds.push_back(areaConfig.areaId); + } + } + + if (isGlobalProp(propId)) { + optionCopy.areaIds = {0}; + } + + if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) { + continuousSubscriptions.push_back(std::move(optionCopy)); + } else { + onChangeSubscriptions.push_back(std::move(optionCopy)); + } + } + // Since we have already check the sample rates, the following functions must succeed. + if (!onChangeSubscriptions.empty()) { + mSubscriptionManager->subscribe(callback, onChangeSubscriptions, + /*isContinuousProperty=*/false); + } + if (!continuousSubscriptions.empty()) { + mSubscriptionManager->subscribe(callback, continuousSubscriptions, + /*isContinuousProperty=*/true); + } return ScopedAStatus::ok(); } +ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType& callback, + const std::vector<int32_t>& propIds) { + return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds), + StatusCode::INVALID_ARG); +} + ScopedAStatus DefaultVehicleHal::returnSharedMemory(const CallbackType&, int64_t) { // TODO(b/200737967): implement this. return ScopedAStatus::ok(); @@ -241,6 +656,103 @@ IVehicleHardware* DefaultVehicleHal::getHardware() { return mVehicleHardware.get(); } +Result<void> DefaultVehicleHal::checkWritePermission(const VehiclePropValue& value) const { + int32_t propId = value.prop; + auto result = getConfig(propId); + if (!result.ok()) { + return Error(toInt(StatusCode::INVALID_ARG)) << getErrorMsg(result); + } + const VehiclePropConfig* config = result.value(); + + if (config->access != VehiclePropertyAccess::WRITE && + config->access != VehiclePropertyAccess::READ_WRITE) { + return Error(toInt(StatusCode::ACCESS_DENIED)) + << StringPrintf("Property %" PRId32 " has no write access", propId); + } + return {}; +} + +Result<void> DefaultVehicleHal::checkReadPermission(const VehiclePropValue& value) const { + int32_t propId = value.prop; + auto result = getConfig(propId); + if (!result.ok()) { + return Error(toInt(StatusCode::INVALID_ARG)) << getErrorMsg(result); + } + const VehiclePropConfig* config = result.value(); + + if (config->access != VehiclePropertyAccess::READ && + config->access != VehiclePropertyAccess::READ_WRITE) { + return Error(toInt(StatusCode::ACCESS_DENIED)) + << StringPrintf("Property %" PRId32 " has no read access", propId); + } + return {}; +} + +void DefaultVehicleHal::checkHealth(std::weak_ptr<IVehicleHardware> hardware, + std::weak_ptr<SubscriptionManager> subscriptionManager) { + auto hardwarePtr = hardware.lock(); + if (hardwarePtr == nullptr) { + ALOGW("the VehicleHardware is destroyed, DefaultVehicleHal is ending"); + return; + } + + StatusCode status = hardwarePtr->checkHealth(); + if (status != StatusCode::OK) { + ALOGE("VHAL check health returns non-okay status"); + return; + } + std::vector<VehiclePropValue> values = {{ + .prop = toInt(VehicleProperty::VHAL_HEARTBEAT), + .areaId = 0, + .status = VehiclePropertyStatus::AVAILABLE, + .value.int64Values = {uptimeMillis()}, + }}; + onPropertyChangeEvent(subscriptionManager, values); + return; +} + +binder_status_t DefaultVehicleHal::AIBinderLinkToDeathImpl::linkToDeath( + AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { + return AIBinder_linkToDeath(binder, recipient, cookie); +} + +void DefaultVehicleHal::setLinkToDeathImpl(std::unique_ptr<ILinkToDeath> impl) { + mLinkToDeathImpl = std::move(impl); +} + +bool DefaultVehicleHal::checkDumpPermission() { + uid_t uid = AIBinder_getCallingUid(); + return uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM; +} + +binder_status_t DefaultVehicleHal::dump(int fd, const char** args, uint32_t numArgs) { + if (!checkDumpPermission()) { + dprintf(fd, "Caller must be root, system or shell"); + return STATUS_PERMISSION_DENIED; + } + + std::vector<std::string> options; + for (uint32_t i = 0; i < numArgs; i++) { + options.push_back(args[i]); + } + DumpResult result = mVehicleHardware->dump(options); + dprintf(fd, "%s", (result.buffer + "\n").c_str()); + if (!result.callerShouldDumpState) { + dprintf(fd, "Skip dumping Vehicle HAL State.\n"); + return STATUS_OK; + } + dprintf(fd, "Vehicle HAL State: \n"); + { + std::scoped_lock<std::mutex> lockGuard(mLock); + dprintf(fd, "Containing %zu property configs\n", mConfigsByPropId.size()); + dprintf(fd, "Currently have %zu getValues clients\n", mGetValuesClients.size()); + dprintf(fd, "Currently have %zu setValues clients\n", mSetValuesClients.size()); + dprintf(fd, "Currently have %zu subscription clients\n", + mSubscriptionClients->countClients()); + } + return STATUS_OK; +} + } // namespace vehicle } // namespace automotive } // namespace hardware diff --git a/automotive/vehicle/aidl/impl/vhal/src/PendingRequestPool.cpp b/automotive/vehicle/aidl/impl/vhal/src/PendingRequestPool.cpp index c2d6f89c2d..23a540393a 100644 --- a/automotive/vehicle/aidl/impl/vhal/src/PendingRequestPool.cpp +++ b/automotive/vehicle/aidl/impl/vhal/src/PendingRequestPool.cpp @@ -74,7 +74,7 @@ PendingRequestPool::~PendingRequestPool() { Result<void> PendingRequestPool::addRequests(const void* clientId, const std::unordered_set<int64_t>& requestIds, - std::shared_ptr<TimeoutCallbackFunc> callback) { + std::shared_ptr<const TimeoutCallbackFunc> callback) { std::scoped_lock<std::mutex> lockGuard(mLock); std::list<PendingRequest>* pendingRequests; size_t pendingRequestCount = 0; @@ -117,6 +117,18 @@ bool PendingRequestPool::isRequestPending(const void* clientId, int64_t requestI return isRequestPendingLocked(clientId, requestId); } +size_t PendingRequestPool::countPendingRequests() const { + std::scoped_lock<std::mutex> lockGuard(mLock); + + size_t count = 0; + for (const auto& [clientId, requests] : mPendingRequestsByClient) { + for (const auto& request : requests) { + count += request.requestIds.size(); + } + } + return count; +} + size_t PendingRequestPool::countPendingRequests(const void* clientId) const { std::scoped_lock<std::mutex> lockGuard(mLock); diff --git a/automotive/vehicle/aidl/impl/vhal/src/RecurrentTimer.cpp b/automotive/vehicle/aidl/impl/vhal/src/RecurrentTimer.cpp new file mode 100644 index 0000000000..8521c4db7c --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/src/RecurrentTimer.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2021 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 "RecurrentTimer.h" + +#include <utils/Log.h> +#include <utils/SystemClock.h> + +#include <inttypes.h> +#include <math.h> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +using ::android::base::ScopedLockAssertion; + +RecurrentTimer::RecurrentTimer() : mThread(&RecurrentTimer::loop, this) {} + +RecurrentTimer::~RecurrentTimer() { + { + std::scoped_lock<std::mutex> lockGuard(mLock); + mStopRequested = true; + } + mCond.notify_one(); + if (mThread.joinable()) { + mThread.join(); + } +} + +void RecurrentTimer::registerTimerCallback(int64_t intervalInNano, + std::shared_ptr<RecurrentTimer::Callback> callback) { + { + std::scoped_lock<std::mutex> lockGuard(mLock); + + // Aligns the nextTime to multiply of interval. + int64_t nextTime = ceil(elapsedRealtimeNano() / intervalInNano) * intervalInNano; + + std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>(); + info->callback = callback; + info->interval = intervalInNano; + info->nextTime = nextTime; + + auto it = mCallbacks.find(callback); + if (it != mCallbacks.end()) { + ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64 + " ns, new: %" PRId64 " ns", + it->second->interval, intervalInNano); + markOutdatedLocked(it->second); + } + mCallbacks[callback] = info.get(); + mCallbackQueue.push_back(std::move(info)); + // Insert the last element into the heap. + std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp); + } + mCond.notify_one(); +} + +void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) { + { + std::scoped_lock<std::mutex> lockGuard(mLock); + + auto it = mCallbacks.find(callback); + if (it == mCallbacks.end()) { + ALOGE("No event found to unregister"); + return; + } + + markOutdatedLocked(it->second); + mCallbacks.erase(it); + } + + mCond.notify_one(); +} + +void RecurrentTimer::markOutdatedLocked(RecurrentTimer::CallbackInfo* info) { + info->outdated = true; + info->callback = nullptr; + // Make sure the first element is always valid. + removeInvalidCallbackLocked(); +} + +void RecurrentTimer::removeInvalidCallbackLocked() { + while (mCallbackQueue.size() != 0 && mCallbackQueue[0]->outdated) { + std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp); + mCallbackQueue.pop_back(); + } +} + +std::unique_ptr<RecurrentTimer::CallbackInfo> RecurrentTimer::popNextCallbackLocked() { + std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp); + std::unique_ptr<CallbackInfo> info = std::move(mCallbackQueue[mCallbackQueue.size() - 1]); + mCallbackQueue.pop_back(); + // Make sure the first element is always valid. + removeInvalidCallbackLocked(); + return info; +} + +void RecurrentTimer::loop() { + std::unique_lock<std::mutex> uniqueLock(mLock); + + while (true) { + // Wait until the timer exits or we have at least one recurrent callback. + mCond.wait(uniqueLock, [this] { + ScopedLockAssertion lockAssertion(mLock); + return mStopRequested || mCallbackQueue.size() != 0; + }); + + int64_t interval; + { + ScopedLockAssertion lockAssertion(mLock); + if (mStopRequested) { + return; + } + // The first element is the nearest next event. + int64_t nextTime = mCallbackQueue[0]->nextTime; + int64_t now = elapsedRealtimeNano(); + if (nextTime > now) { + interval = nextTime - now; + } else { + interval = 0; + } + } + + // Wait for the next event or the timer exits. + if (mCond.wait_for(uniqueLock, std::chrono::nanoseconds(interval), [this] { + ScopedLockAssertion lockAssertion(mLock); + return mStopRequested; + })) { + return; + } + + { + ScopedLockAssertion lockAssertion(mLock); + int64_t now = elapsedRealtimeNano(); + while (mCallbackQueue.size() > 0) { + int64_t nextTime = mCallbackQueue[0]->nextTime; + if (nextTime > now) { + break; + } + + std::unique_ptr<CallbackInfo> info = popNextCallbackLocked(); + info->nextTime += info->interval; + + auto callback = info->callback; + mCallbackQueue.push_back(std::move(info)); + std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp); + + (*callback)(); + } + } + } +} + +bool RecurrentTimer::CallbackInfo::cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo>& lhs, + const std::unique_ptr<RecurrentTimer::CallbackInfo>& rhs) { + return lhs->nextTime > rhs->nextTime; +} + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp new file mode 100644 index 0000000000..21bfba6e3a --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2021 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 "SubscriptionManager.h" + +#include <math/HashCombine.h> +#include <utils/Log.h> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +namespace { + +constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.; + +} // namespace + +using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback; +using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; +using ::android::base::Error; +using ::android::base::Result; +using ::ndk::ScopedAStatus; + +bool SubscriptionManager::PropIdAreaId::operator==(const PropIdAreaId& other) const { + return areaId == other.areaId && propId == other.propId; +} + +size_t SubscriptionManager::PropIdAreaIdHash::operator()(PropIdAreaId const& propIdAreaId) const { + size_t res = 0; + hashCombine(res, propIdAreaId.propId); + hashCombine(res, propIdAreaId.areaId); + return res; +} + +SubscriptionManager::SubscriptionManager(GetValueFunc&& action) + : mTimer(std::make_shared<RecurrentTimer>()), mGetValue(std::move(action)) {} + +SubscriptionManager::~SubscriptionManager() { + std::scoped_lock<std::mutex> lockGuard(mLock); + + mClientsByPropIdArea.clear(); + // RecurrentSubscription has reference to mGetValue, so it must be destroyed before mGetValue is + // destroyed. + mSubscriptionsByClient.clear(); +} + +bool SubscriptionManager::checkSampleRate(float sampleRate) { + return getInterval(sampleRate).ok(); +} + +Result<int64_t> SubscriptionManager::getInterval(float sampleRate) { + int64_t interval = 0; + if (sampleRate <= 0) { + return Error() << "invalid sample rate, must be a positive number"; + } + if (sampleRate <= (ONE_SECOND_IN_NANO / static_cast<float>(INT64_MAX))) { + return Error() << "invalid sample rate: " << sampleRate << ", too small"; + } + interval = static_cast<int64_t>(ONE_SECOND_IN_NANO / sampleRate); + return interval; +} + +Result<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallback>& callback, + const std::vector<SubscribeOptions>& options, + bool isContinuousProperty) { + std::scoped_lock<std::mutex> lockGuard(mLock); + + std::vector<int64_t> intervals; + for (const auto& option : options) { + float sampleRate = option.sampleRate; + + if (isContinuousProperty) { + auto intervalResult = getInterval(sampleRate); + if (!intervalResult.ok()) { + return intervalResult.error(); + } + intervals.push_back(intervalResult.value()); + } + + if (option.areaIds.empty()) { + ALOGE("area IDs to subscribe must not be empty"); + return Error() << "area IDs to subscribe must not be empty"; + } + } + + size_t intervalIndex = 0; + ClientIdType clientId = callback->asBinder().get(); + for (const auto& option : options) { + int32_t propId = option.propId; + const std::vector<int32_t>& areaIds = option.areaIds; + int64_t interval = 0; + if (isContinuousProperty) { + interval = intervals[intervalIndex]; + intervalIndex++; + } + for (int32_t areaId : areaIds) { + PropIdAreaId propIdAreaId = { + .propId = propId, + .areaId = areaId, + }; + if (isContinuousProperty) { + VehiclePropValue propValueRequest{ + .prop = propId, + .areaId = areaId, + }; + mSubscriptionsByClient[clientId][propIdAreaId] = + std::make_unique<RecurrentSubscription>( + mTimer, + [this, callback, propValueRequest] { + mGetValue(callback, propValueRequest); + }, + interval); + } else { + mSubscriptionsByClient[clientId][propIdAreaId] = + std::make_unique<OnChangeSubscription>(); + } + mClientsByPropIdArea[propIdAreaId][clientId] = callback; + } + } + return {}; +} + +Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId, + const std::vector<int32_t>& propIds) { + std::scoped_lock<std::mutex> lockGuard(mLock); + + if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) { + return Error() << "No property was subscribed for the callback"; + } + std::unordered_set<int32_t> subscribedPropIds; + for (auto const& [propIdAreaId, _] : mSubscriptionsByClient[clientId]) { + subscribedPropIds.insert(propIdAreaId.propId); + } + + for (int32_t propId : propIds) { + if (subscribedPropIds.find(propId) == subscribedPropIds.end()) { + return Error() << "property ID: " << propId << " is not subscribed"; + } + } + + auto& subscriptions = mSubscriptionsByClient[clientId]; + auto it = subscriptions.begin(); + while (it != subscriptions.end()) { + int32_t propId = it->first.propId; + if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) { + auto& clients = mClientsByPropIdArea[it->first]; + clients.erase(clientId); + if (clients.empty()) { + mClientsByPropIdArea.erase(it->first); + } + it = subscriptions.erase(it); + } else { + it++; + } + } + if (subscriptions.empty()) { + mSubscriptionsByClient.erase(clientId); + } + return {}; +} + +Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) { + std::scoped_lock<std::mutex> lockGuard(mLock); + + if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) { + return Error() << "No property was subscribed for this client"; + } + + auto& subscriptions = mSubscriptionsByClient[clientId]; + for (auto const& [propIdAreaId, _] : subscriptions) { + auto& clients = mClientsByPropIdArea[propIdAreaId]; + clients.erase(clientId); + if (clients.empty()) { + mClientsByPropIdArea.erase(propIdAreaId); + } + } + mSubscriptionsByClient.erase(clientId); + return {}; +} + +std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<const VehiclePropValue*>> +SubscriptionManager::getSubscribedClients(const std::vector<VehiclePropValue>& updatedValues) { + std::scoped_lock<std::mutex> lockGuard(mLock); + std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<const VehiclePropValue*>> + clients; + + for (const auto& value : updatedValues) { + PropIdAreaId propIdAreaId{ + .propId = value.prop, + .areaId = value.areaId, + }; + if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) { + continue; + } + for (const auto& [clientId, client] : mClientsByPropIdArea[propIdAreaId]) { + if (!mSubscriptionsByClient[clientId][propIdAreaId]->isOnChange()) { + continue; + } + clients[client].push_back(&value); + } + } + return clients; +} + +bool SubscriptionManager::isEmpty() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mSubscriptionsByClient.empty() && mClientsByPropIdArea.empty(); +} + +SubscriptionManager::RecurrentSubscription::RecurrentSubscription( + std::shared_ptr<RecurrentTimer> timer, std::function<void()>&& action, int64_t interval) + : mAction(std::make_shared<std::function<void()>>(action)), mTimer(timer) { + mTimer->registerTimerCallback(interval, mAction); +} + +SubscriptionManager::RecurrentSubscription::~RecurrentSubscription() { + mTimer->unregisterTimerCallback(mAction); +} + +bool SubscriptionManager::RecurrentSubscription::isOnChange() { + return false; +} + +bool SubscriptionManager::OnChangeSubscription::isOnChange() { + return true; +} + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp index ddd0c65ec5..bdb0d31cdb 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp +++ b/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp @@ -39,12 +39,17 @@ class ConnectedClientTest : public ::testing::Test { void SetUp() override { mCallback = ndk::SharedRefBase::make<MockVehicleCallback>(); mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder()); + // timeout: 1s. + int64_t timeout = 1000000000; + mPool = std::make_shared<PendingRequestPool>(timeout); } std::shared_ptr<IVehicleCallback> getCallbackClient() { return mCallbackClient; } MockVehicleCallback* getCallback() { return mCallback.get(); } + std::shared_ptr<PendingRequestPool> getPool() { return mPool; } + protected: using GetValuesClient = GetSetValuesClient<GetValueResult, GetValueResults>; using SetValuesClient = GetSetValuesClient<SetValueResult, SetValueResults>; @@ -52,6 +57,7 @@ class ConnectedClientTest : public ::testing::Test { private: std::shared_ptr<MockVehicleCallback> mCallback; std::shared_ptr<IVehicleCallback> mCallbackClient; + std::shared_ptr<PendingRequestPool> mPool; }; TEST_F(ConnectedClientTest, testSendGetValueResults) { @@ -72,9 +78,10 @@ TEST_F(ConnectedClientTest, testSendGetValueResults) { }, }}; - GetValuesClient client(getCallbackClient()); + GetValuesClient client(getPool(), getCallbackClient()); - client.sendResults(results); + auto resultsCopy = results; + client.sendResults(std::move(resultsCopy)); auto maybeGetValueResults = getCallback()->nextGetValueResults(); ASSERT_TRUE(maybeGetValueResults.has_value()); @@ -99,7 +106,7 @@ TEST_F(ConnectedClientTest, testSendGetValueResultsSeparately) { }, }}; - GetValuesClient client(getCallbackClient()); + GetValuesClient client(getPool(), getCallbackClient()); client.sendResultsSeparately(results); @@ -131,7 +138,9 @@ TEST_F(ConnectedClientTest, testGetValuesGnResultCallback) { }, }}; - GetValuesClient client(getCallbackClient()); + GetValuesClient client(getPool(), getCallbackClient()); + + client.addRequests({0, 1}); (*(client.getResultCallback()))(results); @@ -150,9 +159,10 @@ TEST_F(ConnectedClientTest, testSendSetValueResults) { .status = StatusCode::OK, }}; - SetValuesClient client(getCallbackClient()); + SetValuesClient client(getPool(), getCallbackClient()); - client.sendResults(results); + auto resultsCopy = results; + client.sendResults(std::move(resultsCopy)); auto maybeSetValueResults = getCallback()->nextSetValueResults(); ASSERT_TRUE(maybeSetValueResults.has_value()); @@ -169,7 +179,7 @@ TEST_F(ConnectedClientTest, testSendSetValueResultsSeparately) { .status = StatusCode::OK, }}; - SetValuesClient client(getCallbackClient()); + SetValuesClient client(getPool(), getCallbackClient()); client.sendResultsSeparately(results); @@ -193,7 +203,9 @@ TEST_F(ConnectedClientTest, testSetValuesGetResultCallback) { .status = StatusCode::OK, }}; - SetValuesClient client(getCallbackClient()); + SetValuesClient client(getPool(), getCallbackClient()); + + client.addRequests({0, 1}); (*(client.getResultCallback()))(results); diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp index ffc08a71b8..7443d5bc6d 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp +++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ +#include "ConnectedClient.h" #include "DefaultVehicleHal.h" #include "MockVehicleCallback.h" +#include "MockVehicleHardware.h" #include <IVehicleHardware.h> #include <LargeParcelableBase.h> @@ -25,8 +27,11 @@ #include <android-base/thread_annotations.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <sys/mman.h> #include <utils/Log.h> +#include <utils/SystemClock.h> +#include <chrono> #include <list> #include <memory> #include <mutex> @@ -53,10 +58,14 @@ using ::aidl::android::hardware::automotive::vehicle::SetValueRequests; using ::aidl::android::hardware::automotive::vehicle::SetValueResult; using ::aidl::android::hardware::automotive::vehicle::SetValueResults; using ::aidl::android::hardware::automotive::vehicle::StatusCode; +using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs; using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors; +using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues; @@ -65,207 +74,35 @@ using ::android::base::Result; using ::ndk::ScopedAStatus; using ::ndk::ScopedFileDescriptor; +using ::ndk::SpAIBinder; +using ::testing::ContainsRegex; using ::testing::Eq; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; using ::testing::WhenSortedBy; constexpr int32_t INVALID_PROP_ID = 0; // VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32 constexpr int32_t INT32_WINDOW_PROP = 10001 + 0x10000000 + 0x03000000 + 0x00400000; - -template <class T> -std::optional<T> pop(std::list<T>& items) { - if (items.size() > 0) { - auto item = std::move(items.front()); - items.pop_front(); - return item; - } - return std::nullopt; -} +// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32 +constexpr int32_t GLOBAL_ON_CHANGE_PROP = 10002 + 0x10000000 + 0x01000000 + 0x00400000; +// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32 +constexpr int32_t GLOBAL_CONTINUOUS_PROP = 10003 + 0x10000000 + 0x01000000 + 0x00400000; +// VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32 +constexpr int32_t AREA_ON_CHANGE_PROP = 10004 + 0x10000000 + 0x03000000 + 0x00400000; +// VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32 +constexpr int32_t AREA_CONTINUOUS_PROP = 10005 + 0x10000000 + 0x03000000 + 0x00400000; +// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32 +constexpr int32_t READ_ONLY_PROP = 10006 + 0x10000000 + 0x01000000 + 0x00400000; +// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32 +constexpr int32_t WRITE_ONLY_PROP = 10007 + 0x10000000 + 0x01000000 + 0x00400000; int32_t testInt32VecProp(size_t i) { // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC return static_cast<int32_t>(i) + 0x10000000 + 0x01000000 + 0x00410000; } -class MockVehicleHardware final : public IVehicleHardware { - public: - ~MockVehicleHardware() { - std::scoped_lock<std::mutex> lockGuard(mLock); - for (auto& thread : mThreads) { - thread.join(); - } - } - - std::vector<VehiclePropConfig> getAllPropertyConfigs() const override { - std::scoped_lock<std::mutex> lockGuard(mLock); - return mPropertyConfigs; - } - - StatusCode setValues(std::shared_ptr<const SetValuesCallback> callback, - const std::vector<SetValueRequest>& requests) override { - std::scoped_lock<std::mutex> lockGuard(mLock); - return handleRequests(__func__, callback, requests, &mSetValueRequests, - &mSetValueResponses); - } - - StatusCode getValues(std::shared_ptr<const GetValuesCallback> callback, - const std::vector<GetValueRequest>& requests) const override { - std::scoped_lock<std::mutex> lockGuard(mLock); - return handleRequests(__func__, callback, requests, &mGetValueRequests, - &mGetValueResponses); - } - - DumpResult dump(const std::vector<std::string>&) override { - // TODO(b/200737967): mock this. - return DumpResult{}; - } - - StatusCode checkHealth() override { - // TODO(b/200737967): mock this. - return StatusCode::OK; - } - - void registerOnPropertyChangeEvent(std::unique_ptr<const PropertyChangeCallback>) override { - // TODO(b/200737967): mock this. - } - - void registerOnPropertySetErrorEvent(std::unique_ptr<const PropertySetErrorCallback>) override { - // TODO(b/200737967): mock this. - } - - // Test functions. - void setPropertyConfigs(const std::vector<VehiclePropConfig>& configs) { - std::scoped_lock<std::mutex> lockGuard(mLock); - mPropertyConfigs = configs; - } - - void addGetValueResponses(const std::vector<GetValueResult>& responses) { - std::scoped_lock<std::mutex> lockGuard(mLock); - mGetValueResponses.push_back(responses); - } - - void addSetValueResponses(const std::vector<SetValueResult>& responses) { - std::scoped_lock<std::mutex> lockGuard(mLock); - mSetValueResponses.push_back(responses); - } - - std::vector<GetValueRequest> nextGetValueRequests() { - std::scoped_lock<std::mutex> lockGuard(mLock); - std::optional<std::vector<GetValueRequest>> request = pop(mGetValueRequests); - if (!request.has_value()) { - return std::vector<GetValueRequest>(); - } - return std::move(request.value()); - } - - std::vector<SetValueRequest> nextSetValueRequests() { - std::scoped_lock<std::mutex> lockGuard(mLock); - std::optional<std::vector<SetValueRequest>> request = pop(mSetValueRequests); - if (!request.has_value()) { - return std::vector<SetValueRequest>(); - } - return std::move(request.value()); - } - - void setStatus(const char* functionName, StatusCode status) { - std::scoped_lock<std::mutex> lockGuard(mLock); - mStatusByFunctions[functionName] = status; - } - - void setSleepTime(int64_t timeInNano) { - std::scoped_lock<std::mutex> lockGuard(mLock); - mSleepTime = timeInNano; - } - - private: - mutable std::mutex mLock; - std::vector<VehiclePropConfig> mPropertyConfigs GUARDED_BY(mLock); - mutable std::list<std::vector<GetValueRequest>> mGetValueRequests GUARDED_BY(mLock); - mutable std::list<std::vector<GetValueResult>> mGetValueResponses GUARDED_BY(mLock); - mutable std::list<std::vector<SetValueRequest>> mSetValueRequests GUARDED_BY(mLock); - mutable std::list<std::vector<SetValueResult>> mSetValueResponses GUARDED_BY(mLock); - std::unordered_map<const char*, StatusCode> mStatusByFunctions GUARDED_BY(mLock); - int64_t mSleepTime GUARDED_BY(mLock) = 0; - mutable std::vector<std::thread> mThreads GUARDED_BY(mLock); - - template <class ResultType> - StatusCode returnResponse( - std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback, - std::list<std::vector<ResultType>>* storedResponses) const; - - template <class RequestType, class ResultType> - StatusCode handleRequests( - const char* functionName, - std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback, - const std::vector<RequestType>& requests, - std::list<std::vector<RequestType>>* storedRequests, - std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock); -}; - -template <class ResultType> -StatusCode MockVehicleHardware::returnResponse( - std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback, - std::list<std::vector<ResultType>>* storedResponses) const { - if (storedResponses->size() > 0) { - (*callback)(std::move(storedResponses->front())); - storedResponses->pop_front(); - return StatusCode::OK; - } else { - ALOGE("no more response"); - return StatusCode::INTERNAL_ERROR; - } -} - -template StatusCode MockVehicleHardware::returnResponse<GetValueResult>( - std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback, - std::list<std::vector<GetValueResult>>* storedResponses) const; - -template StatusCode MockVehicleHardware::returnResponse<SetValueResult>( - std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback, - std::list<std::vector<SetValueResult>>* storedResponses) const; - -template <class RequestType, class ResultType> -StatusCode MockVehicleHardware::handleRequests( - const char* functionName, - std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback, - const std::vector<RequestType>& requests, - std::list<std::vector<RequestType>>* storedRequests, - std::list<std::vector<ResultType>>* storedResponses) const { - storedRequests->push_back(requests); - if (auto it = mStatusByFunctions.find(functionName); it != mStatusByFunctions.end()) { - if (StatusCode status = it->second; status != StatusCode::OK) { - return status; - } - } - - if (mSleepTime != 0) { - int64_t sleepTime = mSleepTime; - mThreads.emplace_back([this, callback, sleepTime, storedResponses]() { - std::this_thread::sleep_for(std::chrono::nanoseconds(sleepTime)); - returnResponse(callback, storedResponses); - }); - return StatusCode::OK; - - } else { - return returnResponse(callback, storedResponses); - } -} - -template StatusCode MockVehicleHardware::handleRequests<GetValueRequest, GetValueResult>( - const char* functionName, - std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback, - const std::vector<GetValueRequest>& requests, - std::list<std::vector<GetValueRequest>>* storedRequests, - std::list<std::vector<GetValueResult>>* storedResponses) const; - -template StatusCode MockVehicleHardware::handleRequests<SetValueRequest, SetValueResult>( - const char* functionName, - std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback, - const std::vector<SetValueRequest>& requests, - std::list<std::vector<SetValueRequest>>* storedRequests, - std::list<std::vector<SetValueResult>>* storedResponses) const; - struct PropConfigCmp { bool operator()(const VehiclePropConfig& a, const VehiclePropConfig& b) const { return (a.prop < b.prop); @@ -318,6 +155,62 @@ std::vector<SetValuesInvalidRequestTestCase> getSetValuesInvalidRequestTestCases .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT), }, .expectedStatus = StatusCode::INVALID_ARG, + }, + { + .name = "no_write_permission", + .request = + { + .prop = READ_ONLY_PROP, + .value.int32Values = {0}, + }, + .expectedStatus = StatusCode::ACCESS_DENIED, + }}; +} + +struct SubscribeInvalidOptionsTestCase { + std::string name; + SubscribeOptions option; +}; + +std::vector<SubscribeInvalidOptionsTestCase> getSubscribeInvalidOptionsTestCases() { + return {{ + .name = "invalid_prop", + .option = + { + .propId = INVALID_PROP_ID, + }, + }, + { + .name = "invalid_area_ID", + .option = + { + .propId = AREA_ON_CHANGE_PROP, + .areaIds = {0}, + }, + }, + { + .name = "invalid_sample_rate", + .option = + { + .propId = GLOBAL_CONTINUOUS_PROP, + .sampleRate = 0.0, + }, + }, + { + .name = "sample_rate_out_of_range", + .option = + { + .propId = GLOBAL_CONTINUOUS_PROP, + .sampleRate = 1000.0, + }, + }, + { + .name = "static_property", + .option = + { + // Default change mode is static. + .propId = testInt32VecProp(0), + }, }}; } @@ -331,6 +224,7 @@ class DefaultVehicleHalTest : public ::testing::Test { for (size_t i = 0; i < 10000; i++) { testConfigs.push_back(VehiclePropConfig{ .prop = testInt32VecProp(i), + .access = VehiclePropertyAccess::READ_WRITE, .areaConfigs = { { @@ -341,19 +235,109 @@ class DefaultVehicleHalTest : public ::testing::Test { }, }); } + // A property with area config. testConfigs.push_back( VehiclePropConfig{.prop = INT32_WINDOW_PROP, + .access = VehiclePropertyAccess::READ_WRITE, .areaConfigs = {{ .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT), .minInt32Value = 0, .maxInt32Value = 100, }}}); + // A global on-change property. + testConfigs.push_back(VehiclePropConfig{ + .prop = GLOBAL_ON_CHANGE_PROP, + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + }); + // A global continuous property. + testConfigs.push_back(VehiclePropConfig{ + .prop = GLOBAL_CONTINUOUS_PROP, + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, + .minSampleRate = 0.0, + .maxSampleRate = 100.0, + }); + // A per-area on-change property. + testConfigs.push_back(VehiclePropConfig{ + .prop = AREA_ON_CHANGE_PROP, + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .areaConfigs = + { + { + + .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT), + .minInt32Value = 0, + .maxInt32Value = 100, + }, + { + .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT), + .minInt32Value = 0, + .maxInt32Value = 100, + }, + }, + }); + // A per-area continuous property. + testConfigs.push_back(VehiclePropConfig{ + .prop = AREA_CONTINUOUS_PROP, + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, + .minSampleRate = 0.0, + .maxSampleRate = 1000.0, + .areaConfigs = + { + { + + .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT), + .minInt32Value = 0, + .maxInt32Value = 100, + }, + { + .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT), + .minInt32Value = 0, + .maxInt32Value = 100, + }, + }, + }); + // A read-only property. + testConfigs.push_back(VehiclePropConfig{ + .prop = READ_ONLY_PROP, + .access = VehiclePropertyAccess::READ, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, + .minSampleRate = 0.0, + .maxSampleRate = 1000.0, + }); + // A write-only property. + testConfigs.push_back(VehiclePropConfig{ + .prop = WRITE_ONLY_PROP, + .access = VehiclePropertyAccess::WRITE, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, + .minSampleRate = 0.0, + .maxSampleRate = 1000.0, + }); + // Register the heartbeat event property. + testConfigs.push_back(VehiclePropConfig{ + .prop = toInt(VehicleProperty::VHAL_HEARTBEAT), + .access = VehiclePropertyAccess::READ, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + }); hardware->setPropertyConfigs(testConfigs); mHardwarePtr = hardware.get(); mVhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware)); mVhalClient = IVehicle::fromBinder(mVhal->asBinder()); mCallback = ndk::SharedRefBase::make<MockVehicleCallback>(); - mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder()); + // Keep the local binder alive. + mBinder = mCallback->asBinder(); + mCallbackClient = IVehicleCallback::fromBinder(mBinder); + + // Set the linkToDeath to a fake implementation that always returns OK. + setTestLinkToDeathImpl(); + } + + void TearDown() override { + ASSERT_EQ(countPendingRequests(), static_cast<size_t>(0)) + << "must have no pending requests when test finishes"; } MockVehicleHardware* getHardware() { return mHardwarePtr; } @@ -364,6 +348,38 @@ class DefaultVehicleHalTest : public ::testing::Test { MockVehicleCallback* getCallback() { return mCallback.get(); } + void setTimeout(int64_t timeoutInNano) { mVhal->setTimeout(timeoutInNano); } + + void setTestLinkToDeathImpl() { + mVhal->setLinkToDeathImpl(std::make_unique<TestLinkToDeathImpl>()); + } + + size_t countPendingRequests() { return mVhal->mPendingRequestPool->countPendingRequests(); } + + size_t countClients() { + std::scoped_lock<std::mutex> lockGuard(mVhal->mLock); + return mVhal->mGetValuesClients.size() + mVhal->mSetValuesClients.size() + + mVhal->mSubscriptionClients->countClients(); + } + + std::shared_ptr<PendingRequestPool> getPool() { return mVhal->mPendingRequestPool; } + + void onBinderDied(void* cookie) { return mVhal->onBinderDied(cookie); } + + void onBinderUnlinked(void* cookie) { return mVhal->onBinderUnlinked(cookie); } + + void* getOnBinderDiedContexts(AIBinder* clientId) { + std::scoped_lock<std::mutex> lockGuard(mVhal->mLock); + return mVhal->mOnBinderDiedContexts[clientId].get(); + } + + size_t countOnBinderDiedContexts() { + std::scoped_lock<std::mutex> lockGuard(mVhal->mLock); + return mVhal->mOnBinderDiedContexts.size(); + } + + bool hasNoSubscriptions() { return mVhal->mSubscriptionManager->isEmpty(); } + static Result<void> getValuesTestCases(size_t size, GetValueRequests& requests, std::vector<GetValueResult>& expectedResults, std::vector<GetValueRequest>& expectedHardwareRequests) { @@ -430,21 +446,25 @@ class DefaultVehicleHalTest : public ::testing::Test { if (result.value() != nullptr) { requests.payloads.clear(); requests.sharedMemoryFd = std::move(*result.value()); + requests.payloads.clear(); } return {}; } - size_t countClients() { - std::scoped_lock<std::mutex> lockGuard(mVhal->mLock); - return mVhal->mGetValuesClients.size() + mVhal->mSetValuesClients.size(); - } - private: std::shared_ptr<DefaultVehicleHal> mVhal; std::shared_ptr<IVehicle> mVhalClient; MockVehicleHardware* mHardwarePtr; std::shared_ptr<MockVehicleCallback> mCallback; std::shared_ptr<IVehicleCallback> mCallbackClient; + SpAIBinder mBinder; + + class TestLinkToDeathImpl final : public DefaultVehicleHal::ILinkToDeath { + public: + binder_status_t linkToDeath(AIBinder*, AIBinder_DeathRecipient*, void*) override { + return STATUS_OK; + } + }; }; TEST_F(DefaultVehicleHalTest, testGetAllPropConfigsSmall) { @@ -570,6 +590,178 @@ TEST_F(DefaultVehicleHalTest, testGetValuesInvalidLargeParcelableInput) { ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG)); } +TEST_F(DefaultVehicleHalTest, testGetValuesNoReadPermission) { + GetValueRequests requests = { + .sharedMemoryFd = {}, + .payloads = + { + { + .requestId = 0, + .prop = + { + .prop = WRITE_ONLY_PROP, + }, + }, + }, + }; + + auto status = getClient()->getValues(getCallbackClient(), requests); + + ASSERT_TRUE(status.isOk()) << "getValue with no read permission should return okay with error " + "returned from callback" + << ", error: " << status.getMessage(); + EXPECT_TRUE(getHardware()->nextGetValueRequests().empty()) << "expect no request to hardware"; + + auto maybeResult = getCallback()->nextGetValueResults(); + ASSERT_TRUE(maybeResult.has_value()) << "no results in callback"; + EXPECT_EQ(maybeResult.value().payloads, std::vector<GetValueResult>({ + { + .requestId = 0, + .status = StatusCode::ACCESS_DENIED, + }, + })) + << "expect to get ACCESS_DENIED status if no read permission"; +} + +TEST_F(DefaultVehicleHalTest, testGetValuesFinishBeforeTimeout) { + // timeout: 0.1s + int64_t timeout = 100000000; + setTimeout(timeout); + + GetValueRequests requests; + std::vector<GetValueResult> expectedResults; + std::vector<GetValueRequest> expectedHardwareRequests; + + ASSERT_TRUE(getValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok()); + + // The response would be returned after 0.05s. + getHardware()->setSleepTime(timeout / 2); + getHardware()->addGetValueResponses(expectedResults); + + auto status = getClient()->getValues(getCallbackClient(), requests); + + ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage(); + + // Wait for the response. + std::this_thread::sleep_for(std::chrono::nanoseconds(timeout)); + + auto maybeGetValueResults = getCallback()->nextGetValueResults(); + ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback"; + EXPECT_EQ(maybeGetValueResults.value().payloads, expectedResults) << "results mismatch"; + ASSERT_FALSE(getCallback()->nextGetValueResults().has_value()) << "more results than expected"; +} + +TEST_F(DefaultVehicleHalTest, testGetValuesFinishAfterTimeout) { + // timeout: 0.1s + int64_t timeout = 100000000; + setTimeout(timeout); + + GetValueRequests requests; + std::vector<GetValueResult> expectedResults; + std::vector<GetValueRequest> expectedHardwareRequests; + + ASSERT_TRUE(getValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok()); + + // The response would be returned after 0.2s. + getHardware()->setSleepTime(timeout * 2); + getHardware()->addGetValueResponses(expectedResults); + + auto status = getClient()->getValues(getCallbackClient(), requests); + + ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage(); + + // Wait for the response. + std::this_thread::sleep_for(std::chrono::nanoseconds(timeout * 5)); + + for (size_t i = 0; i < expectedResults.size(); i++) { + expectedResults[i] = { + .requestId = expectedResults[i].requestId, + .status = StatusCode::TRY_AGAIN, + .prop = std::nullopt, + }; + } + + auto maybeGetValueResults = getCallback()->nextGetValueResults(); + ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback"; + ASSERT_THAT(maybeGetValueResults.value().payloads, UnorderedElementsAreArray(expectedResults)) + << "results mismatch, expect TRY_AGAIN error."; + ASSERT_FALSE(getCallback()->nextGetValueResults().has_value()) << "more results than expected"; +} + +TEST_F(DefaultVehicleHalTest, testGetValuesDuplicateRequestIdsInTwoRequests) { + // timeout: 0.1s + int64_t timeout = 100000000; + setTimeout(timeout); + + GetValueRequests requests; + std::vector<GetValueResult> expectedResults; + std::vector<GetValueRequest> expectedHardwareRequests; + + ASSERT_TRUE(getValuesTestCases(1, requests, expectedResults, expectedHardwareRequests).ok()); + + getHardware()->setSleepTime(timeout * 2); + getHardware()->addGetValueResponses(expectedResults); + + auto status = getClient()->getValues(getCallbackClient(), requests); + + ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage(); + + // Use the same request ID again. + status = getClient()->getValues(getCallbackClient(), requests); + + ASSERT_FALSE(status.isOk()) + << "Use the same request ID before the previous request finishes must fail"; + + // Wait for the request to finish. + std::this_thread::sleep_for(std::chrono::nanoseconds(timeout * 5)); +} + +TEST_F(DefaultVehicleHalTest, testGetValuesDuplicateRequestIdsInOneRequest) { + GetValueRequests requests = {.payloads = { + { + .requestId = 0, + .prop = + VehiclePropValue{ + .prop = testInt32VecProp(0), + }, + }, + { + .requestId = 0, + .prop = + VehiclePropValue{ + .prop = testInt32VecProp(1), + }, + }, + }}; + + auto status = getClient()->getValues(getCallbackClient(), requests); + + ASSERT_FALSE(status.isOk()) << "duplicate Ids in one request must fail"; +} + +TEST_F(DefaultVehicleHalTest, testGetValuesDuplicateRequestProps) { + GetValueRequests requests = {.payloads = { + { + .requestId = 0, + .prop = + VehiclePropValue{ + .prop = testInt32VecProp(0), + }, + }, + { + .requestId = 1, + .prop = + VehiclePropValue{ + .prop = testInt32VecProp(0), + }, + }, + }}; + + auto status = getClient()->getValues(getCallbackClient(), requests); + + ASSERT_FALSE(status.isOk()) << "duplicate request properties in one request must fail"; +} + TEST_F(DefaultVehicleHalTest, testSetValuesSmall) { SetValueRequests requests; std::vector<SetValueResult> expectedResults; @@ -675,6 +867,725 @@ TEST_P(SetValuesInvalidRequestTest, testSetValuesInvalidRequest) { << "results from hardware mismatch"; } +TEST_F(DefaultVehicleHalTest, testSetValuesFinishBeforeTimeout) { + // timeout: 0.1s + int64_t timeout = 100000000; + setTimeout(timeout); + + SetValueRequests requests; + std::vector<SetValueResult> expectedResults; + std::vector<SetValueRequest> expectedHardwareRequests; + + ASSERT_TRUE(setValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok()); + + // The response would be returned after 0.05s. + getHardware()->setSleepTime(timeout / 2); + getHardware()->addSetValueResponses(expectedResults); + + auto status = getClient()->setValues(getCallbackClient(), requests); + + ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage(); + + // Wait for the response. + std::this_thread::sleep_for(std::chrono::nanoseconds(timeout)); + + auto maybeSetValueResults = getCallback()->nextSetValueResults(); + ASSERT_TRUE(maybeSetValueResults.has_value()) << "no results in callback"; + EXPECT_EQ(maybeSetValueResults.value().payloads, expectedResults) << "results mismatch"; + ASSERT_FALSE(getCallback()->nextSetValueResults().has_value()) << "more results than expected"; +} + +TEST_F(DefaultVehicleHalTest, testSetValuesFinishAfterTimeout) { + // timeout: 0.1s + int64_t timeout = 100000000; + setTimeout(timeout); + + SetValueRequests requests; + std::vector<SetValueResult> expectedResults; + std::vector<SetValueRequest> expectedHardwareRequests; + + ASSERT_TRUE(setValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok()); + + // The response would be returned after 0.2s. + getHardware()->setSleepTime(timeout * 2); + getHardware()->addSetValueResponses(expectedResults); + + auto status = getClient()->setValues(getCallbackClient(), requests); + + ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage(); + + // Wait for the response. + std::this_thread::sleep_for(std::chrono::nanoseconds(timeout * 5)); + + for (size_t i = 0; i < expectedResults.size(); i++) { + expectedResults[i] = { + .requestId = expectedResults[i].requestId, + .status = StatusCode::TRY_AGAIN, + }; + } + + auto maybeSetValueResults = getCallback()->nextSetValueResults(); + ASSERT_TRUE(maybeSetValueResults.has_value()) << "no results in callback"; + ASSERT_THAT(maybeSetValueResults.value().payloads, UnorderedElementsAreArray(expectedResults)) + << "results mismatch, expect TRY_AGAIN error."; + ASSERT_FALSE(getCallback()->nextSetValueResults().has_value()) << "more results than expected"; +} + +TEST_F(DefaultVehicleHalTest, testSetValuesDuplicateRequestIdsInTwoRequests) { + // timeout: 0.1s + int64_t timeout = 100000000; + setTimeout(timeout); + + SetValueRequests requests; + std::vector<SetValueResult> expectedResults; + std::vector<SetValueRequest> expectedHardwareRequests; + + ASSERT_TRUE(setValuesTestCases(1, requests, expectedResults, expectedHardwareRequests).ok()); + + getHardware()->setSleepTime(timeout * 2); + getHardware()->addSetValueResponses(expectedResults); + + auto status = getClient()->setValues(getCallbackClient(), requests); + + ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage(); + + // Use the same request ID again. + status = getClient()->setValues(getCallbackClient(), requests); + + ASSERT_FALSE(status.isOk()) + << "Use the same request ID before the previous request finishes must fail"; + + // Wait for the request to finish. + std::this_thread::sleep_for(std::chrono::nanoseconds(timeout * 5)); +} + +TEST_F(DefaultVehicleHalTest, testSetValuesDuplicateRequestIdsInOneRequest) { + SetValueRequests requests = {.payloads = { + { + .requestId = 0, + .value = + VehiclePropValue{ + .prop = testInt32VecProp(0), + .value.int32Values = {0}, + }, + }, + { + .requestId = 0, + .value = + VehiclePropValue{ + .prop = testInt32VecProp(1), + .value.int32Values = {0}, + }, + }, + }}; + + auto status = getClient()->setValues(getCallbackClient(), requests); + + ASSERT_FALSE(status.isOk()) << "duplicate Ids in one request must fail"; +} + +TEST_F(DefaultVehicleHalTest, testSetValuesDuplicateRequestProps) { + SetValueRequests requests = {.payloads = { + { + .requestId = 0, + .value = + VehiclePropValue{ + .prop = testInt32VecProp(0), + .value.int32Values = {0}, + }, + }, + { + .requestId = 1, + .value = + VehiclePropValue{ + .prop = testInt32VecProp(0), + .value.int32Values = {0}, + }, + }, + }}; + + auto status = getClient()->setValues(getCallbackClient(), requests); + + ASSERT_FALSE(status.isOk()) << "duplicate request properties in one request must fail"; +} + +TEST_F(DefaultVehicleHalTest, testSubscribeUnsubscribe) { + std::vector<SubscribeOptions> options = { + { + .propId = GLOBAL_ON_CHANGE_PROP, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + status = getClient()->unsubscribe(getCallbackClient(), + std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP})); + + ASSERT_TRUE(status.isOk()) << "unsubscribe failed: " << status.getMessage(); +} + +TEST_F(DefaultVehicleHalTest, testSubscribeGlobalOnChangeNormal) { + std::vector<SubscribeOptions> options = { + { + .propId = GLOBAL_ON_CHANGE_PROP, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + VehiclePropValue testValue{ + .prop = GLOBAL_ON_CHANGE_PROP, + .value.int32Values = {0}, + }; + SetValueRequests setValueRequests = { + .payloads = + { + SetValueRequest{ + .requestId = 0, + .value = testValue, + }, + }, + }; + std::vector<SetValueResult> setValueResults = {{ + .requestId = 0, + .status = StatusCode::OK, + }}; + + // Set the value to trigger a property change event. + getHardware()->addSetValueResponses(setValueResults); + status = getClient()->setValues(getCallbackClient(), setValueRequests); + + ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage(); + + auto maybeResults = getCallback()->nextOnPropertyEventResults(); + ASSERT_TRUE(maybeResults.has_value()) << "no results in callback"; + ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue)) + << "results mismatch, expect on change event for the updated value"; + ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value()) + << "more results than expected"; + EXPECT_EQ(countClients(), static_cast<size_t>(1)); +} + +TEST_F(DefaultVehicleHalTest, testSubscribeGlobalOnchangeUnrelatedEventIgnored) { + std::vector<SubscribeOptions> options = { + { + .propId = GLOBAL_ON_CHANGE_PROP, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + VehiclePropValue testValue{ + .prop = GLOBAL_CONTINUOUS_PROP, + .value.int32Values = {0}, + }; + + // Set the value to trigger a property change event. This event should be ignored because we + // have not subscribed to it. + getHardware()->addSetValueResponses({{ + .requestId = 0, + .status = StatusCode::OK, + }}); + status = getClient()->setValues(getCallbackClient(), + { + .payloads = + { + SetValueRequest{ + .requestId = 0, + .value = testValue, + }, + }, + }); + + ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage(); + + ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value()) + << "must receive no property update event if the property is not subscribed"; +} + +TEST_F(DefaultVehicleHalTest, testSubscribeAreaOnChange) { + int testAreaId = toInt(VehicleAreaWindow::ROW_1_LEFT); + std::vector<SubscribeOptions> options = { + { + .propId = AREA_ON_CHANGE_PROP, + .areaIds = {testAreaId}, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + VehiclePropValue testValue{ + .prop = AREA_ON_CHANGE_PROP, + .areaId = testAreaId, + .value.int32Values = {0}, + }; + + // Set the value to trigger a property change event. + getHardware()->addSetValueResponses({{ + .requestId = 0, + .status = StatusCode::OK, + }}); + status = getClient()->setValues(getCallbackClient(), + { + .payloads = + { + SetValueRequest{ + .requestId = 0, + .value = testValue, + }, + }, + }); + + ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage(); + + auto maybeResults = getCallback()->nextOnPropertyEventResults(); + ASSERT_TRUE(maybeResults.has_value()) << "no results in callback"; + ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue)) + << "results mismatch, expect on change event for the updated value"; + ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value()) + << "more results than expected"; +} + +TEST_F(DefaultVehicleHalTest, testSubscribeAreaOnChangeAllAreas) { + std::vector<SubscribeOptions> options = { + { + .propId = AREA_ON_CHANGE_PROP, + // No areaIds means subscribing to all area IDs. + .areaIds = {}, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + VehiclePropValue testValue1{ + .prop = AREA_ON_CHANGE_PROP, + .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT), + .value.int32Values = {0}, + }; + VehiclePropValue testValue2{ + .prop = AREA_ON_CHANGE_PROP, + .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT), + .value.int32Values = {0}, + }; + + // Set the values to trigger property change events for two areas. + getHardware()->addSetValueResponses({{ + .requestId = 0, + .status = StatusCode::OK, + }, + { + .requestId = 1, + .status = StatusCode::OK, + }}); + status = getClient()->setValues(getCallbackClient(), + { + .payloads = + { + SetValueRequest{ + .requestId = 0, + .value = testValue1, + }, + SetValueRequest{ + .requestId = 1, + .value = testValue2, + }, + }, + }); + + ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage(); + + auto maybeResults = getCallback()->nextOnPropertyEventResults(); + ASSERT_TRUE(maybeResults.has_value()) << "no results in callback"; + ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue1, testValue2)) + << "results mismatch, expect two on-change events for all updated areas"; + ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value()) + << "more results than expected"; +} + +TEST_F(DefaultVehicleHalTest, testSubscribeGlobalContinuous) { + VehiclePropValue testValue{ + .prop = GLOBAL_CONTINUOUS_PROP, + .value.int32Values = {0}, + }; + // Set responses for all the hardware getValues requests. + getHardware()->setGetValueResponder( + [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, + const std::vector<GetValueRequest>& requests) { + std::vector<GetValueResult> results; + for (auto& request : requests) { + VehiclePropValue prop = request.prop; + prop.value.int32Values = {0}; + results.push_back({ + .requestId = request.requestId, + .status = StatusCode::OK, + .prop = prop, + }); + } + (*callback)(results); + return StatusCode::OK; + }); + + std::vector<SubscribeOptions> options = { + { + .propId = GLOBAL_CONTINUOUS_PROP, + .sampleRate = 20.0, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + // Sleep for 1s, which should generate ~20 events. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Should trigger about 20 times, check for at least 15 events to be safe. + for (size_t i = 0; i < 15; i++) { + auto maybeResults = getCallback()->nextOnPropertyEventResults(); + ASSERT_TRUE(maybeResults.has_value()) << "no results in callback"; + ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue)) + << "results mismatch, expect to get the updated value"; + } + EXPECT_EQ(countClients(), static_cast<size_t>(1)); +} + +TEST_F(DefaultVehicleHalTest, testSubscribeAreaContinuous) { + // Set responses for all the hardware getValues requests. + getHardware()->setGetValueResponder( + [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, + const std::vector<GetValueRequest>& requests) { + std::vector<GetValueResult> results; + for (auto& request : requests) { + VehiclePropValue prop = request.prop; + prop.value.int32Values = {0}; + results.push_back({ + .requestId = request.requestId, + .status = StatusCode::OK, + .prop = prop, + }); + } + (*callback)(results); + return StatusCode::OK; + }); + + std::vector<SubscribeOptions> options = { + { + .propId = AREA_CONTINUOUS_PROP, + .sampleRate = 20.0, + .areaIds = {toInt(VehicleAreaWindow::ROW_1_LEFT)}, + }, + { + .propId = AREA_CONTINUOUS_PROP, + .sampleRate = 10.0, + .areaIds = {toInt(VehicleAreaWindow::ROW_1_RIGHT)}, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + // Sleep for 1s, which should generate ~20 events. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + std::vector<VehiclePropValue> events; + while (true) { + auto maybeResults = getCallback()->nextOnPropertyEventResults(); + if (!maybeResults.has_value()) { + break; + } + for (const auto& value : maybeResults.value().payloads) { + events.push_back(value); + } + } + + size_t leftCount = 0; + size_t rightCount = 0; + + for (const auto& event : events) { + ASSERT_EQ(event.prop, AREA_CONTINUOUS_PROP); + if (event.areaId == toInt(VehicleAreaWindow::ROW_1_LEFT)) { + leftCount++; + continue; + } + rightCount++; + } + + // Should trigger about 20 times, check for at least 15 events to be safe. + ASSERT_GE(leftCount, static_cast<size_t>(15)); + // Should trigger about 10 times, check for at least 5 events to be safe. + ASSERT_GE(rightCount, static_cast<size_t>(5)); +} + +TEST_F(DefaultVehicleHalTest, testUnsubscribeOnChange) { + std::vector<SubscribeOptions> options = { + { + .propId = GLOBAL_ON_CHANGE_PROP, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + status = getClient()->unsubscribe(getCallbackClient(), + std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP})); + + ASSERT_TRUE(status.isOk()) << "unsubscribe failed: " << status.getMessage(); + + VehiclePropValue testValue{ + .prop = GLOBAL_ON_CHANGE_PROP, + .value.int32Values = {0}, + }; + + // Set the value to trigger a property change event. + getHardware()->addSetValueResponses({{ + .requestId = 0, + .status = StatusCode::OK, + }}); + status = getClient()->setValues(getCallbackClient(), + { + .payloads = + { + SetValueRequest{ + .requestId = 0, + .value = testValue, + }, + }, + }); + + ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage(); + + ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value()) + << "No property event should be generated after unsubscription"; +} + +TEST_F(DefaultVehicleHalTest, testUnsubscribeContinuous) { + VehiclePropValue testValue{ + .prop = GLOBAL_CONTINUOUS_PROP, + .value.int32Values = {0}, + }; + // Set responses for all the hardware getValues requests. + getHardware()->setGetValueResponder( + [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, + const std::vector<GetValueRequest>& requests) { + std::vector<GetValueResult> results; + for (auto& request : requests) { + VehiclePropValue prop = request.prop; + prop.value.int32Values = {0}; + results.push_back({ + .requestId = request.requestId, + .status = StatusCode::OK, + .prop = prop, + }); + } + (*callback)(results); + return StatusCode::OK; + }); + + std::vector<SubscribeOptions> options = { + { + .propId = GLOBAL_CONTINUOUS_PROP, + .sampleRate = 20.0, + }, + }; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + + status = getClient()->unsubscribe(getCallbackClient(), + std::vector<int32_t>({GLOBAL_CONTINUOUS_PROP})); + + ASSERT_TRUE(status.isOk()) << "unsubscribe failed: " << status.getMessage(); + + // Clear existing events. + while (getCallback()->nextOnPropertyEventResults().has_value()) { + // Do nothing. + } + + // Wait for a while, make sure no new events are generated. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value()) + << "No property event should be generated after unsubscription"; +} + +class SubscribeInvalidOptionsTest + : public DefaultVehicleHalTest, + public testing::WithParamInterface<SubscribeInvalidOptionsTestCase> {}; + +INSTANTIATE_TEST_SUITE_P( + SubscribeInvalidOptionsTests, SubscribeInvalidOptionsTest, + ::testing::ValuesIn(getSubscribeInvalidOptionsTestCases()), + [](const testing::TestParamInfo<SubscribeInvalidOptionsTest::ParamType>& info) { + return info.param.name; + }); + +TEST_P(SubscribeInvalidOptionsTest, testSubscribeInvalidOptions) { + std::vector<SubscribeOptions> options = {GetParam().option}; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_FALSE(status.isOk()) << "invalid subscribe options must fail"; + ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG)); +} + +TEST_F(DefaultVehicleHalTest, testSubscribeNoReadPermission) { + std::vector<SubscribeOptions> options = {{ + .propId = WRITE_ONLY_PROP, + }}; + + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_FALSE(status.isOk()) << "subscribe to a write-only property must fail"; + ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::ACCESS_DENIED)); +} + +TEST_F(DefaultVehicleHalTest, testUnsubscribeFailure) { + auto status = getClient()->unsubscribe(getCallbackClient(), + std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP})); + + ASSERT_FALSE(status.isOk()) << "unsubscribe to a not-subscribed property must fail"; + ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG)); +} + +TEST_F(DefaultVehicleHalTest, testHeartbeatEvent) { + std::vector<SubscribeOptions> options = {{ + .propId = toInt(VehicleProperty::VHAL_HEARTBEAT), + }}; + int64_t currentTime = uptimeMillis(); + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + + ASSERT_TRUE(status.isOk()) << "unable to subscribe to heartbeat event: " << status.getMessage(); + + // We send out a heartbeat event every 3s, so sleep for 3s. + std::this_thread::sleep_for(std::chrono::seconds(3)); + + auto maybeResults = getCallback()->nextOnPropertyEventResults(); + ASSERT_TRUE(maybeResults.has_value()) << "no results in callback"; + ASSERT_EQ(maybeResults.value().payloads.size(), static_cast<size_t>(1)); + VehiclePropValue gotValue = maybeResults.value().payloads[0]; + ASSERT_EQ(gotValue.prop, toInt(VehicleProperty::VHAL_HEARTBEAT)); + ASSERT_EQ(gotValue.value.int64Values.size(), static_cast<size_t>(1)); + ASSERT_GE(gotValue.value.int64Values[0], currentTime) + << "expect to get the latest timestamp with the heartbeat event"; +} + +TEST_F(DefaultVehicleHalTest, testOnBinderDiedUnlinked) { + // First subscribe to a continuous property so that we register a death recipient for our + // client. + VehiclePropValue testValue{ + .prop = GLOBAL_CONTINUOUS_PROP, + .value.int32Values = {0}, + }; + // Set responses for all the hardware getValues requests. + getHardware()->setGetValueResponder( + [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, + const std::vector<GetValueRequest>& requests) { + std::vector<GetValueResult> results; + for (auto& request : requests) { + VehiclePropValue prop = request.prop; + prop.value.int32Values = {0}; + results.push_back({ + .requestId = request.requestId, + .status = StatusCode::OK, + .prop = prop, + }); + } + (*callback)(results); + return StatusCode::OK; + }); + std::vector<SubscribeOptions> options = { + { + .propId = GLOBAL_CONTINUOUS_PROP, + .sampleRate = 20.0, + }, + }; + auto status = getClient()->subscribe(getCallbackClient(), options, 0); + ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage(); + // Sleep for 100ms so that the subscriptionClient gets created because we would at least try to + // get value once. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + // Issue another getValue request on the same client. + GetValueRequests requests; + std::vector<GetValueResult> expectedResults; + std::vector<GetValueRequest> expectedHardwareRequests; + ASSERT_TRUE(getValuesTestCases(1, requests, expectedResults, expectedHardwareRequests).ok()); + getHardware()->addGetValueResponses(expectedResults); + status = getClient()->getValues(getCallbackClient(), requests); + ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage(); + + ASSERT_EQ(countOnBinderDiedContexts(), static_cast<size_t>(1)) + << "expect one OnBinderDied context when one client is registered"; + + // Get the death recipient cookie for our callback that would be used in onBinderDied and + // onBinderUnlinked. + AIBinder* clientId = getCallbackClient()->asBinder().get(); + void* context = getOnBinderDiedContexts(clientId); + + onBinderDied(context); + + ASSERT_EQ(countClients(), static_cast<size_t>(0)) + << "expect all clients to be removed when binder died"; + ASSERT_TRUE(hasNoSubscriptions()) << "expect no subscriptions when binder died"; + + onBinderUnlinked(context); + + ASSERT_EQ(countOnBinderDiedContexts(), static_cast<size_t>(0)) + << "expect OnBinderDied context to be deleted when binder is unlinked"; +} + +TEST_F(DefaultVehicleHalTest, testDumpCallerShouldDump) { + std::string buffer = "Dump from hardware"; + getHardware()->setDumpResult({ + .callerShouldDumpState = true, + .buffer = buffer, + }); + int fd = memfd_create("memfile", 0); + getClient()->dump(fd, nullptr, 0); + + lseek(fd, 0, SEEK_SET); + char buf[10240] = {}; + read(fd, buf, sizeof(buf)); + close(fd); + + std::string msg(buf); + + ASSERT_THAT(msg, ContainsRegex(buffer + "\nVehicle HAL State: \n")); +} + +TEST_F(DefaultVehicleHalTest, testDumpCallerShouldNotDump) { + std::string buffer = "Dump from hardware"; + getHardware()->setDumpResult({ + .callerShouldDumpState = false, + .buffer = buffer, + }); + int fd = memfd_create("memfile", 0); + getClient()->dump(fd, nullptr, 0); + + lseek(fd, 0, SEEK_SET); + char buf[10240] = {}; + read(fd, buf, sizeof(buf)); + close(fd); + + std::string msg(buf); + + ASSERT_THAT(msg, ContainsRegex(buffer)); + ASSERT_EQ(msg.find("Vehicle HAL State: "), std::string::npos); +} + } // namespace vehicle } // namespace automotive } // namespace hardware diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp index ca366cd746..5e3e03c64a 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp +++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp @@ -31,16 +31,6 @@ using ::ndk::ScopedAStatus; using ::ndk::ScopedFileDescriptor; template <class T> -std::optional<T> pop(std::list<T>& items) { - if (items.size() > 0) { - auto item = std::move(items.front()); - items.pop_front(); - return item; - } - return std::nullopt; -} - -template <class T> static ScopedAStatus storeResults(const T& results, std::list<T>* storedResults) { T resultsCopy{ .payloads = results.payloads, @@ -65,8 +55,12 @@ ScopedAStatus MockVehicleCallback::onSetValues(const SetValueResults& results) { return storeResults(results, &mSetValueResults); } -ScopedAStatus MockVehicleCallback::onPropertyEvent(const VehiclePropValues&, int32_t) { - return ScopedAStatus::ok(); +ScopedAStatus MockVehicleCallback::onPropertyEvent(const VehiclePropValues& results, + int32_t sharedMemoryFileCount) { + std::scoped_lock<std::mutex> lockGuard(mLock); + + mSharedMemoryFileCount = sharedMemoryFileCount; + return storeResults(results, &mOnPropertyEventResults); } ScopedAStatus MockVehicleCallback::onPropertySetError(const VehiclePropErrors&) { @@ -83,6 +77,11 @@ std::optional<SetValueResults> MockVehicleCallback::nextSetValueResults() { return pop(mSetValueResults); } +std::optional<VehiclePropValues> MockVehicleCallback::nextOnPropertyEventResults() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return pop(mOnPropertyEventResults); +} + } // namespace vehicle } // namespace automotive } // namespace hardware diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h index 916575abdc..c83164ff79 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h +++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h @@ -31,6 +31,16 @@ namespace hardware { namespace automotive { namespace vehicle { +template <class T> +std::optional<T> pop(std::list<T>& items) { + if (items.size() > 0) { + auto item = std::move(items.front()); + items.pop_front(); + return item; + } + return std::nullopt; +} + // MockVehicleCallback is a mock VehicleCallback implementation that simply stores the results. class MockVehicleCallback final : public ::aidl::android::hardware::automotive::vehicle::BnVehicleCallback { @@ -52,6 +62,8 @@ class MockVehicleCallback final nextGetValueResults(); std::optional<::aidl::android::hardware::automotive::vehicle::SetValueResults> nextSetValueResults(); + std::optional<::aidl::android::hardware::automotive::vehicle::VehiclePropValues> + nextOnPropertyEventResults(); private: std::mutex mLock; @@ -59,6 +71,9 @@ class MockVehicleCallback final GUARDED_BY(mLock); std::list<::aidl::android::hardware::automotive::vehicle::SetValueResults> mSetValueResults GUARDED_BY(mLock); + std::list<::aidl::android::hardware::automotive::vehicle::VehiclePropValues> + mOnPropertyEventResults GUARDED_BY(mLock); + int32_t mSharedMemoryFileCount GUARDED_BY(mLock); }; } // namespace vehicle diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp new file mode 100644 index 0000000000..66aef7c2da --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2021 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 "MockVehicleHardware.h" +#include "MockVehicleCallback.h" + +#include <utils/Log.h> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +using ::aidl::android::hardware::automotive::vehicle::GetValueRequest; +using ::aidl::android::hardware::automotive::vehicle::GetValueResult; +using ::aidl::android::hardware::automotive::vehicle::SetValueRequest; +using ::aidl::android::hardware::automotive::vehicle::SetValueResult; +using ::aidl::android::hardware::automotive::vehicle::StatusCode; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; + +MockVehicleHardware::~MockVehicleHardware() { + std::unique_lock<std::mutex> lk(mLock); + mCv.wait(lk, [this] { return mThreadCount == 0; }); +} + +std::vector<VehiclePropConfig> MockVehicleHardware::getAllPropertyConfigs() const { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mPropertyConfigs; +} + +StatusCode MockVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback, + const std::vector<SetValueRequest>& requests) { + std::scoped_lock<std::mutex> lockGuard(mLock); + if (StatusCode status = handleRequestsLocked(__func__, callback, requests, &mSetValueRequests, + &mSetValueResponses); + status != StatusCode::OK) { + return status; + } + if (mPropertyChangeCallback == nullptr) { + return StatusCode::OK; + } + std::vector<VehiclePropValue> values; + for (auto& request : requests) { + values.push_back(request.value); + } + (*mPropertyChangeCallback)(values); + return StatusCode::OK; +} + +StatusCode MockVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback, + const std::vector<GetValueRequest>& requests) const { + std::scoped_lock<std::mutex> lockGuard(mLock); + if (mGetValueResponder != nullptr) { + return mGetValueResponder(callback, requests); + } + return handleRequestsLocked(__func__, callback, requests, &mGetValueRequests, + &mGetValueResponses); +} + +void MockVehicleHardware::setDumpResult(DumpResult result) { + mDumpResult = result; +} + +DumpResult MockVehicleHardware::dump(const std::vector<std::string>&) { + return mDumpResult; +} + +StatusCode MockVehicleHardware::checkHealth() { + return StatusCode::OK; +} + +void MockVehicleHardware::registerOnPropertyChangeEvent( + std::unique_ptr<const PropertyChangeCallback> callback) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mPropertyChangeCallback = std::move(callback); +} + +void MockVehicleHardware::registerOnPropertySetErrorEvent( + std::unique_ptr<const PropertySetErrorCallback>) { + // TODO(b/200737967): mock this. +} + +void MockVehicleHardware::setPropertyConfigs(const std::vector<VehiclePropConfig>& configs) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mPropertyConfigs = configs; +} + +void MockVehicleHardware::addGetValueResponses(const std::vector<GetValueResult>& responses) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mGetValueResponses.push_back(responses); +} + +void MockVehicleHardware::addSetValueResponses(const std::vector<SetValueResult>& responses) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mSetValueResponses.push_back(responses); +} + +void MockVehicleHardware::setGetValueResponder( + std::function<StatusCode(std::shared_ptr<const GetValuesCallback>, + const std::vector<GetValueRequest>&)>&& responder) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mGetValueResponder = responder; +} + +std::vector<GetValueRequest> MockVehicleHardware::nextGetValueRequests() { + std::scoped_lock<std::mutex> lockGuard(mLock); + std::optional<std::vector<GetValueRequest>> request = pop(mGetValueRequests); + if (!request.has_value()) { + return std::vector<GetValueRequest>(); + } + return std::move(request.value()); +} + +std::vector<SetValueRequest> MockVehicleHardware::nextSetValueRequests() { + std::scoped_lock<std::mutex> lockGuard(mLock); + std::optional<std::vector<SetValueRequest>> request = pop(mSetValueRequests); + if (!request.has_value()) { + return std::vector<SetValueRequest>(); + } + return std::move(request.value()); +} + +void MockVehicleHardware::setStatus(const char* functionName, StatusCode status) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mStatusByFunctions[functionName] = status; +} + +void MockVehicleHardware::setSleepTime(int64_t timeInNano) { + std::scoped_lock<std::mutex> lockGuard(mLock); + mSleepTime = timeInNano; +} + +template <class ResultType> +StatusCode MockVehicleHardware::returnResponse( + std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback, + std::list<std::vector<ResultType>>* storedResponses) const { + if (storedResponses->size() > 0) { + (*callback)(std::move(storedResponses->front())); + storedResponses->pop_front(); + return StatusCode::OK; + } else { + ALOGE("no more response"); + return StatusCode::INTERNAL_ERROR; + } +} + +template StatusCode MockVehicleHardware::returnResponse<GetValueResult>( + std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback, + std::list<std::vector<GetValueResult>>* storedResponses) const; + +template StatusCode MockVehicleHardware::returnResponse<SetValueResult>( + std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback, + std::list<std::vector<SetValueResult>>* storedResponses) const; + +template <class RequestType, class ResultType> +StatusCode MockVehicleHardware::handleRequestsLocked( + const char* functionName, + std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback, + const std::vector<RequestType>& requests, + std::list<std::vector<RequestType>>* storedRequests, + std::list<std::vector<ResultType>>* storedResponses) const { + storedRequests->push_back(requests); + if (auto it = mStatusByFunctions.find(functionName); it != mStatusByFunctions.end()) { + if (StatusCode status = it->second; status != StatusCode::OK) { + return status; + } + } + + if (mSleepTime != 0) { + int64_t sleepTime = mSleepTime; + mThreadCount++; + std::thread t([this, callback, sleepTime, storedResponses]() { + std::this_thread::sleep_for(std::chrono::nanoseconds(sleepTime)); + returnResponse(callback, storedResponses); + mThreadCount--; + mCv.notify_one(); + }); + // Detach the thread here so we do not have to maintain the thread object. mThreadCount + // and mCv make sure we wait for all threads to end before we exit. + t.detach(); + return StatusCode::OK; + + } else { + return returnResponse(callback, storedResponses); + } +} + +template StatusCode MockVehicleHardware::handleRequestsLocked<GetValueRequest, GetValueResult>( + const char* functionName, + std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback, + const std::vector<GetValueRequest>& requests, + std::list<std::vector<GetValueRequest>>* storedRequests, + std::list<std::vector<GetValueResult>>* storedResponses) const; + +template StatusCode MockVehicleHardware::handleRequestsLocked<SetValueRequest, SetValueResult>( + const char* functionName, + std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback, + const std::vector<SetValueRequest>& requests, + std::list<std::vector<SetValueRequest>>* storedRequests, + std::list<std::vector<SetValueResult>>* storedResponses) const; + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h new file mode 100644 index 0000000000..74d4fae337 --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleHardware_H_ +#define android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleHardware_H_ + +#include <IVehicleHardware.h> +#include <VehicleHalTypes.h> + +#include <android-base/thread_annotations.h> + +#include <atomic> +#include <condition_variable> +#include <list> +#include <memory> +#include <mutex> +#include <thread> +#include <unordered_map> +#include <vector> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +class MockVehicleHardware final : public IVehicleHardware { + public: + ~MockVehicleHardware(); + + std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropConfig> + getAllPropertyConfigs() const override; + ::aidl::android::hardware::automotive::vehicle::StatusCode setValues( + std::shared_ptr<const SetValuesCallback> callback, + const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>& + requests) override; + ::aidl::android::hardware::automotive::vehicle::StatusCode getValues( + std::shared_ptr<const GetValuesCallback> callback, + const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>& + requests) const override; + DumpResult dump(const std::vector<std::string>&) override; + ::aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() override; + void registerOnPropertyChangeEvent( + std::unique_ptr<const PropertyChangeCallback> callback) override; + void registerOnPropertySetErrorEvent(std::unique_ptr<const PropertySetErrorCallback>) override; + + // Test functions. + void setPropertyConfigs( + const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropConfig>& + configs); + void addGetValueResponses( + const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult>& + responses); + void addSetValueResponses( + const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueResult>& + responses); + void setGetValueResponder( + std::function<::aidl::android::hardware::automotive::vehicle::StatusCode( + std::shared_ptr<const GetValuesCallback>, + const std::vector< + ::aidl::android::hardware::automotive::vehicle::GetValueRequest>&)>&& + responder); + std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest> + nextGetValueRequests(); + std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest> + nextSetValueRequests(); + void setStatus(const char* functionName, + ::aidl::android::hardware::automotive::vehicle::StatusCode status); + void setSleepTime(int64_t timeInNano); + void setDumpResult(DumpResult result); + + private: + mutable std::mutex mLock; + mutable std::condition_variable mCv; + mutable std::atomic<int> mThreadCount; + std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropConfig> mPropertyConfigs + GUARDED_BY(mLock); + mutable std::list<std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>> + mGetValueRequests GUARDED_BY(mLock); + mutable std::list<std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult>> + mGetValueResponses GUARDED_BY(mLock); + mutable std::list<std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>> + mSetValueRequests GUARDED_BY(mLock); + mutable std::list<std::vector<::aidl::android::hardware::automotive::vehicle::SetValueResult>> + mSetValueResponses GUARDED_BY(mLock); + std::unordered_map<const char*, ::aidl::android::hardware::automotive::vehicle::StatusCode> + mStatusByFunctions GUARDED_BY(mLock); + int64_t mSleepTime GUARDED_BY(mLock) = 0; + std::unique_ptr<const PropertyChangeCallback> mPropertyChangeCallback GUARDED_BY(mLock); + std::function<::aidl::android::hardware::automotive::vehicle::StatusCode( + std::shared_ptr<const GetValuesCallback>, + const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>&)> + mGetValueResponder GUARDED_BY(mLock); + + template <class ResultType> + ::aidl::android::hardware::automotive::vehicle::StatusCode returnResponse( + std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback, + std::list<std::vector<ResultType>>* storedResponses) const; + template <class RequestType, class ResultType> + ::aidl::android::hardware::automotive::vehicle::StatusCode handleRequestsLocked( + const char* functionName, + std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback, + const std::vector<RequestType>& requests, + std::list<std::vector<RequestType>>* storedRequests, + std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock); + + DumpResult mDumpResult; +}; + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleHardware_H_ diff --git a/automotive/vehicle/aidl/impl/vhal/test/PendingRequestPoolTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/PendingRequestPoolTest.cpp index 03d795beaa..9c9e4b90aa 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/PendingRequestPoolTest.cpp +++ b/automotive/vehicle/aidl/impl/vhal/test/PendingRequestPoolTest.cpp @@ -53,7 +53,7 @@ class PendingRequestPoolTest : public ::testing::Test { int64_t getTimeout() { return TEST_TIMEOUT; } - void* getTestClientId() { return reinterpret_cast<void*>(0); } + const void* getTestClientId() { return reinterpret_cast<const void*>(0); } private: // Test timeout is 0.1s. @@ -239,12 +239,12 @@ TEST_F(PendingRequestPoolTest, testSameRequestIdForDifferentClient) { auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>( [](std::unordered_set<int64_t>) {}); - ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<void*>(0), {0}, callback)); - ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<void*>(1), {1, 2, 0}, callback)); + ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<const void*>(0), {0}, callback)); + ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<const void*>(1), {1, 2, 0}, callback)); - ASSERT_THAT(getPool()->tryFinishRequests(reinterpret_cast<void*>(0), {0}), + ASSERT_THAT(getPool()->tryFinishRequests(reinterpret_cast<const void*>(0), {0}), UnorderedElementsAre(0)); - ASSERT_THAT(getPool()->tryFinishRequests(reinterpret_cast<void*>(1), {1, 2, 0}), + ASSERT_THAT(getPool()->tryFinishRequests(reinterpret_cast<const void*>(1), {1, 2, 0}), UnorderedElementsAre(0, 1, 2)); } @@ -258,14 +258,14 @@ TEST_F(PendingRequestPoolTest, testPendingRequestCountLimit) { for (size_t i = 0; i < 10000; i++) { requests.insert(static_cast<int64_t>(i)); } - ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<void*>(0), requests, callback)); + ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<const void*>(0), requests, callback)); - auto result = getPool()->addRequests(reinterpret_cast<void*>(0), {static_cast<int64_t>(10000)}, - callback); + auto result = getPool()->addRequests(reinterpret_cast<const void*>(0), + {static_cast<int64_t>(10000)}, callback); ASSERT_FALSE(result.ok()) << "adding more pending requests than limit must fail"; ASSERT_EQ(result.error().code(), toInt(StatusCode::TRY_AGAIN)); - getPool()->tryFinishRequests(reinterpret_cast<void*>(0), requests); + getPool()->tryFinishRequests(reinterpret_cast<const void*>(0), requests); } } // namespace vehicle diff --git a/automotive/vehicle/aidl/impl/vhal/test/RecurrentTimerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/RecurrentTimerTest.cpp new file mode 100644 index 0000000000..d343cea5b0 --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/test/RecurrentTimerTest.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2021 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 "RecurrentTimer.h" + +#include <android-base/thread_annotations.h> +#include <gtest/gtest.h> + +#include <chrono> +#include <memory> +#include <mutex> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +class RecurrentTimerTest : public ::testing::Test { + public: + std::shared_ptr<RecurrentTimer::Callback> getCallback(size_t token) { + return std::make_shared<RecurrentTimer::Callback>([this, token] { + std::scoped_lock<std::mutex> lockGuard(mLock); + + mCallbacks.push_back(token); + }); + } + + std::vector<size_t> getCalledCallbacks() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mCallbacks; + } + + void clearCalledCallbacks() { + std::scoped_lock<std::mutex> lockGuard(mLock); + mCallbacks.clear(); + } + + size_t countTimerCallbackQueue(RecurrentTimer* timer) { + std::scoped_lock<std::mutex> lockGuard(timer->mLock); + return timer->mCallbackQueue.size(); + } + + private: + std::mutex mLock; + std::vector<size_t> mCallbacks GUARDED_BY(mLock); +}; + +TEST_F(RecurrentTimerTest, testRegisterCallback) { + RecurrentTimer timer; + // 0.1s + int64_t interval = 100000000; + + auto action = getCallback(0); + timer.registerTimerCallback(interval, action); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + timer.unregisterTimerCallback(action); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9)); +} + +TEST_F(RecurrentTimerTest, testRegisterUnregisterRegister) { + RecurrentTimer timer; + // 0.1s + int64_t interval = 100000000; + + auto action = getCallback(0); + timer.registerTimerCallback(interval, action); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + timer.unregisterTimerCallback(action); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + clearCalledCallbacks(); + + timer.registerTimerCallback(interval, action); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9)); +} + +TEST_F(RecurrentTimerTest, testDestroyTimerWithCallback) { + std::unique_ptr<RecurrentTimer> timer = std::make_unique<RecurrentTimer>(); + // 0.1s + int64_t interval = 100000000; + + auto action = getCallback(0); + timer->registerTimerCallback(interval, action); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + timer.reset(); + + clearCalledCallbacks(); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + ASSERT_TRUE(getCalledCallbacks().empty()); +} + +TEST_F(RecurrentTimerTest, testRegisterMultipleCallbacks) { + RecurrentTimer timer; + // 0.1s + int64_t interval1 = 100000000; + auto action1 = getCallback(1); + timer.registerTimerCallback(interval1, action1); + // 0.05s + int64_t interval2 = 50000000; + auto action2 = getCallback(2); + timer.registerTimerCallback(interval2, action2); + // 0.03s + int64_t interval3 = 30000000; + auto action3 = getCallback(3); + timer.registerTimerCallback(interval3, action3); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + timer.unregisterTimerCallback(action1); + timer.unregisterTimerCallback(action2); + timer.unregisterTimerCallback(action3); + + size_t action1Count = 0; + size_t action2Count = 0; + size_t action3Count = 0; + for (size_t token : getCalledCallbacks()) { + if (token == 1) { + action1Count++; + } + if (token == 2) { + action2Count++; + } + if (token == 3) { + action3Count++; + } + } + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + ASSERT_GE(action1Count, static_cast<size_t>(9)); + // Theoretically trigger 20 times, but check for at least 15 times to be stable. + ASSERT_GE(action2Count, static_cast<size_t>(15)); + // Theoretically trigger 33 times, but check for at least 25 times to be stable. + ASSERT_GE(action3Count, static_cast<size_t>(25)); +} + +TEST_F(RecurrentTimerTest, testRegisterSameCallbackMultipleTimes) { + RecurrentTimer timer; + // 0.02s + int64_t interval1 = 20000000; + // 0.01s + int64_t interval2 = 10000000; + + auto action = getCallback(0); + for (int i = 0; i < 10; i++) { + timer.registerTimerCallback(interval1, action); + timer.registerTimerCallback(interval2, action); + } + + clearCalledCallbacks(); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9)); + + timer.unregisterTimerCallback(action); + + // Make sure there is no item in the callback queue. + ASSERT_EQ(countTimerCallbackQueue(&timer), static_cast<size_t>(0)); +} + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp new file mode 100644 index 0000000000..f81b1a2998 --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2021 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 "SubscriptionManager.h" + +#include <VehicleHalTypes.h> + +#include <aidl/android/hardware/automotive/vehicle/BnVehicleCallback.h> +#include <android-base/thread_annotations.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <float.h> +#include <chrono> +#include <list> +#include <memory> +#include <mutex> +#include <thread> +#include <vector> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +using ::aidl::android::hardware::automotive::vehicle::BnVehicleCallback; +using ::aidl::android::hardware::automotive::vehicle::GetValueResults; +using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback; +using ::aidl::android::hardware::automotive::vehicle::SetValueResults; +using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues; +using ::ndk::ScopedAStatus; +using ::ndk::SpAIBinder; +using ::testing::ElementsAre; +using ::testing::WhenSorted; + +class PropertyCallback final : public BnVehicleCallback { + public: + ScopedAStatus onGetValues(const GetValueResults&) override { return ScopedAStatus::ok(); } + + ScopedAStatus onSetValues(const SetValueResults&) override { return ScopedAStatus::ok(); } + + ScopedAStatus onPropertyEvent(const VehiclePropValues& values, int32_t) override { + std::scoped_lock<std::mutex> lockGuard(mLock); + for (const auto& value : values.payloads) { + mEvents.push_back(value); + } + return ScopedAStatus::ok(); + } + + ScopedAStatus onPropertySetError(const VehiclePropErrors&) override { + return ScopedAStatus::ok(); + } + + // Test functions. + std::list<VehiclePropValue> getEvents() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mEvents; + } + + void clearEvents() { + std::scoped_lock<std::mutex> lockGuard(mLock); + mEvents.clear(); + } + + private: + std::mutex mLock; + std::list<VehiclePropValue> mEvents GUARDED_BY(mLock); +}; + +class SubscriptionManagerTest : public ::testing::Test { + public: + void SetUp() override { + mManager = std::make_unique<SubscriptionManager>( + [](const std::shared_ptr<IVehicleCallback>& callback, + const VehiclePropValue& value) { + callback->onPropertyEvent( + VehiclePropValues{ + .payloads = {value}, + }, + 0); + }); + mCallback = ::ndk::SharedRefBase::make<PropertyCallback>(); + // Keep the local binder alive. + mBinder = mCallback->asBinder(); + mCallbackClient = IVehicleCallback::fromBinder(mBinder); + } + + SubscriptionManager* getManager() { return mManager.get(); } + + std::shared_ptr<IVehicleCallback> getCallbackClient() { return mCallbackClient; } + + PropertyCallback* getCallback() { return mCallback.get(); } + + std::list<VehiclePropValue> getEvents() { return getCallback()->getEvents(); } + + void clearEvents() { return getCallback()->clearEvents(); } + + private: + std::unique_ptr<SubscriptionManager> mManager; + std::shared_ptr<PropertyCallback> mCallback; + std::shared_ptr<IVehicleCallback> mCallbackClient; + SpAIBinder mBinder; +}; + +TEST_F(SubscriptionManagerTest, testSubscribeGlobalContinuous) { + std::vector<SubscribeOptions> options = {{ + .propId = 0, + .areaIds = {0}, + .sampleRate = 10.0, + }}; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + ASSERT_GE(getEvents().size(), static_cast<size_t>(9)); + EXPECT_EQ(getEvents().back().prop, 0); + EXPECT_EQ(getEvents().back().areaId, 0); +} + +TEST_F(SubscriptionManagerTest, testSubscribeMultiplePropsGlobalContinuous) { + std::vector<SubscribeOptions> options = {{ + .propId = 0, + .areaIds = {0}, + .sampleRate = 10.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 20.0, + }}; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + size_t event0Count = 0; + size_t event1Count = 0; + + for (const auto& event : getEvents()) { + if (event.prop == 0) { + event0Count++; + } else { + event1Count++; + } + } + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(event0Count, static_cast<size_t>(9)); + // Theoretically trigger 20 times, but check for at least 15 times to be stable. + EXPECT_GE(event1Count, static_cast<size_t>(15)); +} + +TEST_F(SubscriptionManagerTest, testOverrideSubscriptionContinuous) { + std::vector<SubscribeOptions> options = {{ + .propId = 0, + .areaIds = {0}, + .sampleRate = 20.0, + }}; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + // Override sample rate to be 10.0. + options[0].sampleRate = 10.0; + result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(getEvents().size(), static_cast<size_t>(9)); + EXPECT_LE(getEvents().size(), static_cast<size_t>(15)); +} + +TEST_F(SubscriptionManagerTest, testSubscribeMultipleAreasContinuous) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + size_t area0Count = 0; + size_t area1Count = 0; + + for (const auto& event : getEvents()) { + if (event.areaId == 0) { + area0Count++; + } else { + area1Count++; + } + } + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(area0Count, static_cast<size_t>(9)); + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(area1Count, static_cast<size_t>(9)); +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeGlobalContinuous) { + std::vector<SubscribeOptions> options = {{ + .propId = 0, + .areaIds = {0}, + .sampleRate = 10.0, + }}; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get()); + ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); + + clearEvents(); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + ASSERT_TRUE(getEvents().empty()); +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeMultipleAreas) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1, 2, 3, 4}, + .sampleRate = 10.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), + std::vector<int32_t>({0})); + ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); + + clearEvents(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(getEvents().size(), static_cast<size_t>(9)); + + for (const auto& event : getEvents()) { + EXPECT_EQ(event.prop, 1); + } +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeByCallback) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1, 2, 3, 4}, + .sampleRate = 10.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get()); + ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); + + clearEvents(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + EXPECT_TRUE(getEvents().empty()); +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeFailure) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1, 2, 3, 4}, + }, + { + .propId = 1, + .areaIds = {0}, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, false); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + // Property ID: 2 was not subscribed. + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), + std::vector<int32_t>({0, 1, 2})); + ASSERT_FALSE(result.ok()) << "unsubscribe an unsubscribed property must fail"; + + // Since property 0 and property 1 was not unsubscribed successfully, we should be able to + // unsubscribe them again. + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), + std::vector<int32_t>({0, 1})); + ASSERT_TRUE(result.ok()) << "a failed unsubscription must not unsubscribe any properties" + << result.error().message(); +} + +TEST_F(SubscriptionManagerTest, testSubscribeOnchange) { + std::vector<SubscribeOptions> options1 = { + { + .propId = 0, + .areaIds = {0, 1}, + }, + { + .propId = 1, + .areaIds = {0}, + }, + }; + std::vector<SubscribeOptions> options2 = { + { + .propId = 0, + .areaIds = {0}, + }, + }; + + SpAIBinder binder1 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder(); + std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1); + SpAIBinder binder2 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder(); + std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2); + auto result = getManager()->subscribe(client1, options1, false); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + result = getManager()->subscribe(client2, options2, false); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::vector<VehiclePropValue> updatedValues = { + { + .prop = 0, + .areaId = 0, + }, + { + .prop = 0, + .areaId = 1, + }, + { + .prop = 1, + .areaId = 0, + }, + { + .prop = 1, + .areaId = 1, + }, + }; + auto clients = getManager()->getSubscribedClients(updatedValues); + + ASSERT_THAT(clients[client1], + WhenSorted(ElementsAre(&updatedValues[0], &updatedValues[1], &updatedValues[2]))); + ASSERT_THAT(clients[client2], ElementsAre(&updatedValues[0])); +} + +TEST_F(SubscriptionManagerTest, testSubscribeInvalidOption) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1, 2, 3, 4}, + // invalid sample rate. + .sampleRate = 0.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_FALSE(result.ok()) << "subscribe with invalid sample rate must fail"; + ASSERT_TRUE(getManager() + ->getSubscribedClients({{ + .prop = 0, + .areaId = 0, + }, + { + .prop = 1, + .areaId = 0, + }}) + .empty()) + << "no property should be subscribed if error is returned"; +} + +TEST_F(SubscriptionManagerTest, testSubscribeNoAreaIds) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {}, + .sampleRate = 1.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_FALSE(result.ok()) << "subscribe with invalid sample rate must fail"; + ASSERT_TRUE(getManager() + ->getSubscribedClients({{ + .prop = 1, + .areaId = 0, + }}) + .empty()) + << "no property should be subscribed if error is returned"; +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeOnchange) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1}, + }, + { + .propId = 1, + .areaIds = {0}, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, false); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), + std::vector<int32_t>({0})); + ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); + + std::vector<VehiclePropValue> updatedValues = { + { + .prop = 0, + .areaId = 0, + }, + { + .prop = 1, + .areaId = 0, + }, + }; + auto clients = getManager()->getSubscribedClients(updatedValues); + + ASSERT_THAT(clients[getCallbackClient()], ElementsAre(&updatedValues[1])); +} + +TEST_F(SubscriptionManagerTest, testCheckSampleRateValid) { + ASSERT_TRUE(SubscriptionManager::checkSampleRate(1.0)); +} + +TEST_F(SubscriptionManagerTest, testCheckSampleRateInvalidTooSmall) { + ASSERT_FALSE(SubscriptionManager::checkSampleRate(FLT_MIN)); +} + +TEST_F(SubscriptionManagerTest, testCheckSampleRateInvalidZero) { + ASSERT_FALSE(SubscriptionManager::checkSampleRate(0)); +} + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp b/automotive/vehicle/proto/Android.bp index 3307bd62a3..683f1281c4 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp +++ b/automotive/vehicle/proto/Android.bp @@ -25,8 +25,8 @@ package { cc_library_static { name: "android.hardware.automotive.vehicle@2.0-libproto-native", visibility: [ - "//hardware/interfaces/automotive/vehicle/2.0/default:__subpackages__", - "//device/generic/car/emulator/vhal_v2_0:__subpackages__", + "//hardware/interfaces/automotive/vehicle:__subpackages__", + "//device/generic/car/emulator:__subpackages__", ], vendor: true, host_supported: true, diff --git a/automotive/vehicle/proto/VehicleHalProto.proto b/automotive/vehicle/proto/VehicleHalProto.proto new file mode 100644 index 0000000000..0dafe8c4a6 --- /dev/null +++ b/automotive/vehicle/proto/VehicleHalProto.proto @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 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. + */ + +syntax = "proto2"; + +package vhal_proto; + +// CMD messages are from workstation --> VHAL +// RESP messages are from VHAL --> workstation +enum MsgType { + GET_CONFIG_CMD = 0; + GET_CONFIG_RESP = 1; + GET_CONFIG_ALL_CMD = 2; + GET_CONFIG_ALL_RESP = 3; + GET_PROPERTY_CMD = 4; + GET_PROPERTY_RESP = 5; + GET_PROPERTY_ALL_CMD = 6; + GET_PROPERTY_ALL_RESP = 7; + SET_PROPERTY_CMD = 8; + SET_PROPERTY_RESP = 9; + SET_PROPERTY_ASYNC = 10; + DEBUG_CMD = 11; + DEBUG_RESP = 12; +} +enum Status { + RESULT_OK = 0; + ERROR_UNKNOWN = 1; + ERROR_UNIMPLEMENTED_CMD = 2; + ERROR_INVALID_PROPERTY = 3; + ERROR_INVALID_AREA_ID = 4; + ERROR_PROPERTY_UNINITIALIZED = 5; + ERROR_WRITE_ONLY_PROPERTY = 6; + ERROR_MEMORY_ALLOC_FAILED = 7; + ERROR_INVALID_OPERATION = 8; +} + +enum VehiclePropStatus { + AVAILABLE = 0; + UNAVAILABLE = 1; + ERROR = 2; +} + +message VehicleAreaConfig { + required int32 area_id = 1; + optional sint32 min_int32_value = 2; + optional sint32 max_int32_value = 3; + optional sint64 min_int64_value = 4; + optional sint64 max_int64_value = 5; + optional float min_float_value = 6; + optional float max_float_value = 7; +} + +message VehiclePropConfig { + required int32 prop = 1; + optional int32 access = 2; + optional int32 change_mode = 3; + optional int32 value_type = 4; + optional int32 supported_areas = 5; // Deprecated - DO NOT USE + repeated VehicleAreaConfig area_configs = 6; + optional int32 config_flags = 7; + repeated int32 config_array = 8; + optional string config_string = 9; + optional float min_sample_rate = 10; + optional float max_sample_rate = 11; +}; + +message VehiclePropValue { + // common data + required int32 prop = 1; + optional int32 value_type = 2; + optional int64 timestamp = 3; // required for valid data from HAL, skipped for set + optional VehiclePropStatus status = 10; // required for valid data from HAL, skipped for set + + // values + optional int32 area_id = 4; + repeated sint32 int32_values = 5; // this also covers boolean value. + repeated sint64 int64_values = 6; + repeated float float_values = 7; + optional string string_value = 8; + optional bytes bytes_value = 9; +}; + +// This structure is used to notify what values to get from the Vehicle HAL +message VehiclePropGet { + required int32 prop = 1; + optional int32 area_id = 2; +}; + +message EmulatorMessage { + required MsgType msg_type = 1; + optional Status status = 2; // Only for RESP messages + repeated VehiclePropGet prop = 3; // Provided for getConfig, getProperty commands + repeated VehiclePropConfig config = 4; + repeated VehiclePropValue value = 5; + repeated string debug_commands = 6; // Required for debug command + optional string debug_result = 7; // Required for debug RESP messages +}; diff --git a/bluetooth/audio/2.2/default/OWNERS b/bluetooth/audio/2.2/default/OWNERS new file mode 100644 index 0000000000..17ea46424f --- /dev/null +++ b/bluetooth/audio/2.2/default/OWNERS @@ -0,0 +1,4 @@ +include platform/packages/modules/Bluetooth:/OWNERS + +cheneyni@google.com +aliceypkuo@google.com
\ No newline at end of file diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl index e2a08a0e6d..948c04a513 100644 --- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl @@ -37,11 +37,16 @@ parcelable CodecCapabilities { android.hardware.bluetooth.audio.CodecType codecType; android.hardware.bluetooth.audio.CodecCapabilities.Capabilities capabilities; @VintfStability + parcelable VendorCapabilities { + ParcelableHolder extension; + } + @VintfStability union Capabilities { android.hardware.bluetooth.audio.SbcCapabilities sbcCapabilities; android.hardware.bluetooth.audio.AacCapabilities aacCapabilities; android.hardware.bluetooth.audio.LdacCapabilities ldacCapabilities; android.hardware.bluetooth.audio.AptxCapabilities aptxCapabilities; android.hardware.bluetooth.audio.Lc3Capabilities lc3Capabilities; + android.hardware.bluetooth.audio.CodecCapabilities.VendorCapabilities vendorCapabilities; } } diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl index 34ebd60530..32bccd8501 100644 --- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl @@ -40,11 +40,18 @@ parcelable CodecConfiguration { boolean isScmstEnabled; android.hardware.bluetooth.audio.CodecConfiguration.CodecSpecific config; @VintfStability + parcelable VendorConfiguration { + int vendorId; + char codecId; + ParcelableHolder codecConfig; + } + @VintfStability union CodecSpecific { android.hardware.bluetooth.audio.SbcConfiguration sbcConfig; android.hardware.bluetooth.audio.AacConfiguration aacConfig; android.hardware.bluetooth.audio.LdacConfiguration ldacConfig; android.hardware.bluetooth.audio.AptxConfiguration aptxConfig; android.hardware.bluetooth.audio.Lc3Configuration lc3Config; + android.hardware.bluetooth.audio.CodecConfiguration.VendorConfiguration vendorConfig; } } diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl index 44b434bbd7..3a5f951005 100644 --- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl @@ -41,4 +41,5 @@ enum CodecType { APTX_HD = 4, LDAC = 5, LC3 = 6, + VENDOR = 7, } diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl index 5bf0252bf8..41e243168c 100644 --- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl @@ -30,12 +30,17 @@ import android.hardware.bluetooth.audio.SbcCapabilities; @VintfStability parcelable CodecCapabilities { @VintfStability + parcelable VendorCapabilities { + ParcelableHolder extension; + } + @VintfStability union Capabilities { SbcCapabilities sbcCapabilities; AacCapabilities aacCapabilities; LdacCapabilities ldacCapabilities; AptxCapabilities aptxCapabilities; Lc3Capabilities lc3Capabilities; + VendorCapabilities vendorCapabilities; } CodecType codecType; Capabilities capabilities; diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl index 9e43f2244a..3679537af7 100644 --- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl @@ -30,12 +30,19 @@ import android.hardware.bluetooth.audio.SbcConfiguration; @VintfStability parcelable CodecConfiguration { @VintfStability + parcelable VendorConfiguration { + int vendorId; + char codecId; + ParcelableHolder codecConfig; + } + @VintfStability union CodecSpecific { SbcConfiguration sbcConfig; AacConfiguration aacConfig; LdacConfiguration ldacConfig; AptxConfiguration aptxConfig; Lc3Configuration lc3Config; + VendorConfiguration vendorConfig; } CodecType codecType; /** diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecType.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecType.aidl index 68c60f548e..9c3308194e 100644 --- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecType.aidl +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecType.aidl @@ -26,4 +26,5 @@ enum CodecType { APTX_HD, LDAC, LC3, + VENDOR, } diff --git a/bluetooth/audio/aidl/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/aidl/default/A2dpSoftwareAudioProvider.cpp index 7e4907409e..5a413e07a8 100644 --- a/bluetooth/audio/aidl/default/A2dpSoftwareAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/A2dpSoftwareAudioProvider.cpp @@ -88,8 +88,9 @@ ndk::ScopedAStatus A2dpSoftwareAudioProvider::onSessionReady( return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } *_aidl_return = data_mq_->dupeDesc(); + auto desc = data_mq_->dupeDesc(); BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_, - _aidl_return, *audio_config_); + &desc, *audio_config_); return ndk::ScopedAStatus::ok(); } diff --git a/bluetooth/audio/aidl/default/Android.bp b/bluetooth/audio/aidl/default/Android.bp index 846702fa0e..fc882d4cef 100644 --- a/bluetooth/audio/aidl/default/Android.bp +++ b/bluetooth/audio/aidl/default/Android.bp @@ -8,7 +8,7 @@ package { } cc_library_shared { - name: "android.hardware.bluetooth.audio-V1-impl", + name: "android.hardware.bluetooth.audio-impl", vendor: true, vintf_fragments: ["bluetooth_audio.xml"], srcs: [ diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp index 8e6cee7b2f..1e55a0bf7c 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp +++ b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "BTAudioProvidersFactory" +#define LOG_TAG "BTAudioProviderFactoryAIDL" #include "BluetoothAudioProviderFactory.h" diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.h b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.h index 96d888c166..b38cfd2520 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.h +++ b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.h @@ -18,13 +18,6 @@ #include <aidl/android/hardware/bluetooth/audio/BnBluetoothAudioProviderFactory.h> -#include "A2dpOffloadAudioProvider.h" -#include "A2dpSoftwareAudioProvider.h" -#include "BluetoothAudioProvider.h" -#include "HearingAidAudioProvider.h" -#include "LeAudioOffloadAudioProvider.h" -#include "LeAudioSoftwareAudioProvider.h" - namespace aidl { namespace android { namespace hardware { diff --git a/bluetooth/audio/aidl/default/HearingAidAudioProvider.cpp b/bluetooth/audio/aidl/default/HearingAidAudioProvider.cpp index a993059b65..66ce93bd35 100644 --- a/bluetooth/audio/aidl/default/HearingAidAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/HearingAidAudioProvider.cpp @@ -81,10 +81,10 @@ ndk::ScopedAStatus HearingAidAudioProvider::onSessionReady( *_aidl_return = DataMQDesc(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + *_aidl_return = data_mq_->dupeDesc(); auto desc = data_mq_->dupeDesc(); BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_, &desc, *audio_config_); - *_aidl_return = data_mq_->dupeDesc(); return ndk::ScopedAStatus::ok(); } diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp index 4078783c99..72ac9bdede 100644 --- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "BTAudioProviderLeAudioSW" +#define LOG_TAG "BTAudioProviderLeAudioHW" #include "LeAudioOffloadAudioProvider.h" diff --git a/bluetooth/audio/aidl/default/LeAudioSoftwareAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioSoftwareAudioProvider.cpp index f9962fd9d9..67b7d60c0c 100644 --- a/bluetooth/audio/aidl/default/LeAudioSoftwareAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/LeAudioSoftwareAudioProvider.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "BTAudioProviderLeAudioHW" +#define LOG_TAG "BTAudioProviderLeAudioSW" #include "LeAudioSoftwareAudioProvider.h" @@ -88,6 +88,16 @@ ndk::ScopedAStatus LeAudioSoftwareAudioProvider::startSession( channel_mode_to_channel_count(pcm_config.channelMode) * (pcm_config.bitsPerSample / 8) * (pcm_config.dataIntervalUs / 1000) * buffer_modifier; + if (data_mq_size <= 0) { + LOG(ERROR) << __func__ << "Unexpected audio buffer size: " << data_mq_size + << ", SampleRateHz: " << pcm_config.sampleRateHz + << ", ChannelMode: " << toString(pcm_config.channelMode) + << ", BitsPerSample: " + << static_cast<int>(pcm_config.bitsPerSample) + << ", DataIntervalUs: " << pcm_config.dataIntervalUs + << ", SessionType: " << toString(session_type_); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } LOG(INFO) << __func__ << " - size of audio buffer " << data_mq_size << " byte(s)"; @@ -113,8 +123,9 @@ ndk::ScopedAStatus LeAudioSoftwareAudioProvider::onSessionReady( return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } *_aidl_return = data_mq_->dupeDesc(); + auto desc = data_mq_->dupeDesc(); BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_, - _aidl_return, *audio_config_); + &desc, *audio_config_); return ndk::ScopedAStatus::ok(); } diff --git a/bluetooth/audio/aidl/default/OWNERS b/bluetooth/audio/aidl/default/OWNERS new file mode 100644 index 0000000000..17ea46424f --- /dev/null +++ b/bluetooth/audio/aidl/default/OWNERS @@ -0,0 +1,4 @@ +include platform/packages/modules/Bluetooth:/OWNERS + +cheneyni@google.com +aliceypkuo@google.com
\ No newline at end of file diff --git a/bluetooth/audio/aidl/vts/OWNERS b/bluetooth/audio/aidl/vts/OWNERS new file mode 100644 index 0000000000..17ea46424f --- /dev/null +++ b/bluetooth/audio/aidl/vts/OWNERS @@ -0,0 +1,4 @@ +include platform/packages/modules/Bluetooth:/OWNERS + +cheneyni@google.com +aliceypkuo@google.com
\ No newline at end of file diff --git a/bluetooth/audio/utils/OWNERS b/bluetooth/audio/utils/OWNERS index ed9284766b..17ea46424f 100644 --- a/bluetooth/audio/utils/OWNERS +++ b/bluetooth/audio/utils/OWNERS @@ -1,3 +1,4 @@ include platform/packages/modules/Bluetooth:/OWNERS -cheneyni@google.com
\ No newline at end of file +cheneyni@google.com +aliceypkuo@google.com
\ No newline at end of file diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp index 92cd0f5b14..516ebe8d7a 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp +++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp @@ -78,7 +78,7 @@ static const AptxCapabilities kDefaultOffloadAptxHdCapability = { .bitsPerSample = {24}, }; -static const Lc3Capabilities kDefaultOffloadLc3Capability = { +static const Lc3Capabilities kDefaultA2dpOffloadLc3Capability = { .samplingFrequencyHz = {44100, 48000}, .frameDurationUs = {7500, 10000}, .channelMode = {ChannelMode::MONO, ChannelMode::STEREO}, @@ -285,11 +285,11 @@ bool BluetoothAudioCodecs::IsOffloadLc3ConfigurationValid( const Lc3Configuration lc3_data = codec_specific.get<CodecConfiguration::CodecSpecific::lc3Config>(); - if (ContainedInVector(kDefaultOffloadLc3Capability.samplingFrequencyHz, + if (ContainedInVector(kDefaultA2dpOffloadLc3Capability.samplingFrequencyHz, lc3_data.samplingFrequencyHz) && - ContainedInVector(kDefaultOffloadLc3Capability.frameDurationUs, + ContainedInVector(kDefaultA2dpOffloadLc3Capability.frameDurationUs, lc3_data.frameDurationUs) && - ContainedInVector(kDefaultOffloadLc3Capability.channelMode, + ContainedInVector(kDefaultA2dpOffloadLc3Capability.channelMode, lc3_data.channelMode)) { return true; } @@ -352,10 +352,10 @@ BluetoothAudioCodecs::GetA2dpOffloadCodecCapabilities( case CodecType::LC3: codec_capability.capabilities .set<CodecCapabilities::Capabilities::lc3Capabilities>( - kDefaultOffloadLc3Capability); + kDefaultA2dpOffloadLc3Capability); break; case CodecType::UNKNOWN: - codec_capability = {}; + case CodecType::VENDOR: break; } } @@ -420,6 +420,7 @@ bool BluetoothAudioCodecs::IsOffloadCodecConfigurationValid( } break; case CodecType::UNKNOWN: + case CodecType::VENDOR: break; } return false; diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h index c542ce5be7..0259a7e770 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h +++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h @@ -45,11 +45,7 @@ class BluetoothAudioCodecs { const SessionType& session_type, const CodecConfiguration& codec_config); static bool IsOffloadLeAudioConfigurationValid( - const SessionType& session_type, const Lc3Configuration& codec_config); - - static bool IsOffloadLeAudioConfigurationValid( - const SessionType& session_type, - const LeAudioConfiguration& codec_config); + const SessionType& session_type, const LeAudioConfiguration&); static std::vector<LeAudioCodecCapabilitiesSetting> GetLeAudioOffloadCodecCapabilities(const SessionType& session_type); @@ -77,8 +73,6 @@ class BluetoothAudioCodecs { const CodecConfiguration::CodecSpecific& codec_specific); static bool IsOffloadLc3ConfigurationValid( const CodecConfiguration::CodecSpecific& codec_specific); - static bool IsOffloadLeAudioConfigurationValid( - const SessionType& session_type, const LeAudioCodecConfiguration&); }; } // namespace audio diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp index 95e473e5b9..f626db83bd 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp +++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp @@ -35,22 +35,8 @@ static constexpr int kFmqReceiveTimeoutMs = static constexpr int kWritePollMs = 1; // polled non-blocking interval static constexpr int kReadPollMs = 1; // polled non-blocking interval -const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {}; -const LeAudioConfiguration kInvalidLeAudioConfiguration = {}; -AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration = - {}; -AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {}; -AudioConfiguration BluetoothAudioSession::invalidLeOffloadAudioConfig = {}; - BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type) - : session_type_(session_type), stack_iface_(nullptr), data_mq_(nullptr) { - invalidSoftwareAudioConfiguration.set<AudioConfiguration::pcmConfig>( - kInvalidPcmConfiguration); - invalidOffloadAudioConfiguration.set<AudioConfiguration::a2dpConfig>( - kInvalidCodecConfiguration); - invalidLeOffloadAudioConfig.set<AudioConfiguration::leAudioConfig>( - kInvalidLeAudioConfiguration); -} + : session_type_(session_type), stack_iface_(nullptr), data_mq_(nullptr) {} /*** * @@ -72,13 +58,7 @@ void BluetoothAudioSession::OnSessionStarted( } else if (!UpdateDataPath(mq_desc)) { LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_) << " MqDescriptor Invalid"; - if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) { - audio_config_ = std::make_unique<AudioConfiguration>( - invalidOffloadAudioConfiguration); - } else { - audio_config_ = std::make_unique<AudioConfiguration>( - invalidSoftwareAudioConfiguration); - } + audio_config_ = nullptr; } else { stack_iface_ = stack_iface; LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) @@ -91,13 +71,7 @@ void BluetoothAudioSession::OnSessionEnded() { std::lock_guard<std::recursive_mutex> guard(mutex_); bool toggled = IsSessionReady(); LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_); - if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) { - audio_config_ = - std::make_unique<AudioConfiguration>(invalidOffloadAudioConfiguration); - } else { - audio_config_ = - std::make_unique<AudioConfiguration>(invalidSoftwareAudioConfiguration); - } + audio_config_ = nullptr; stack_iface_ = nullptr; UpdateDataPath(nullptr); if (toggled) { @@ -111,22 +85,17 @@ void BluetoothAudioSession::OnSessionEnded() { * ***/ -const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() { +const AudioConfiguration BluetoothAudioSession::GetAudioConfig() { std::lock_guard<std::recursive_mutex> guard(mutex_); if (!IsSessionReady()) { - if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) { - return invalidOffloadAudioConfiguration; - } else { - return invalidSoftwareAudioConfiguration; - } switch (session_type_) { case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH: - return invalidOffloadAudioConfiguration; + return AudioConfiguration(CodecConfiguration{}); case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH: case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH: - return invalidLeOffloadAudioConfig; + return AudioConfiguration(LeAudioConfiguration{}); default: - return invalidSoftwareAudioConfiguration; + return AudioConfiguration(PcmConfiguration{}); } } return *audio_config_; @@ -169,7 +138,7 @@ bool BluetoothAudioSession::IsSessionReady() { session_type_ == SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH || (data_mq_ != nullptr && data_mq_->isValid())); - return stack_iface_ != nullptr && is_mq_valid; + return stack_iface_ != nullptr && is_mq_valid && audio_config_ != nullptr; } /*** diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h index 85fd571f28..73bc0f8b4e 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h +++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h @@ -144,7 +144,7 @@ class BluetoothAudioSession { * The control function is for the bluetooth_audio module to get the current * AudioConfiguration ***/ - const AudioConfiguration& GetAudioConfig(); + const AudioConfiguration GetAudioConfig(); /*** * The report function is used to report that the Bluetooth stack has notified @@ -173,14 +173,6 @@ class BluetoothAudioSession { // Return if IBluetoothAudioProviderFactory implementation existed static bool IsAidlAvailable(); - static constexpr PcmConfiguration kInvalidPcmConfiguration = {}; - // can't be constexpr because of non-literal type - static const CodecConfiguration kInvalidCodecConfiguration; - - static AudioConfiguration invalidSoftwareAudioConfiguration; - static AudioConfiguration invalidOffloadAudioConfiguration; - static AudioConfiguration invalidLeOffloadAudioConfig; - private: // using recursive_mutex to allow hwbinder to re-enter again. std::recursive_mutex mutex_; diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h index a3ed42894c..aff01e5a26 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h +++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h @@ -79,11 +79,15 @@ class BluetoothAudioSessionControl { BluetoothAudioSessionInstance::GetSessionInstance(session_type); if (session_ptr != nullptr) { return session_ptr->GetAudioConfig(); - } else if (session_type == - SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) { - return BluetoothAudioSession::invalidOffloadAudioConfiguration; - } else { - return BluetoothAudioSession::invalidSoftwareAudioConfiguration; + } + switch (session_type) { + case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH: + return AudioConfiguration(CodecConfiguration{}); + case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH: + case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH: + return AudioConfiguration(LeAudioConfiguration{}); + default: + return AudioConfiguration(PcmConfiguration{}); } } diff --git a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp index 91e0238783..632a389d8c 100644 --- a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp +++ b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp @@ -93,16 +93,6 @@ std::unordered_map< std::unordered_map<uint16_t, std::shared_ptr<PortStatusCallbacks_2_2>>> legacy_callback_table; -const static std::unordered_map<SessionType_2_0, SessionType> - session_type_2_0_to_aidl_map{ - {SessionType_2_0::A2DP_SOFTWARE_ENCODING_DATAPATH, - SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH}, - {SessionType_2_0::A2DP_HARDWARE_OFFLOAD_DATAPATH, - SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH}, - {SessionType_2_0::HEARING_AID_SOFTWARE_ENCODING_DATAPATH, - SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH}, - }; - const static std::unordered_map<SessionType_2_1, SessionType> session_type_2_1_to_aidl_map{ {SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH, @@ -121,18 +111,6 @@ const static std::unordered_map<SessionType_2_1, SessionType> SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH}, }; -const static std::unordered_map<int32_t, SampleRate_2_0> - sample_rate_to_hidl_2_0_map{ - {44100, SampleRate_2_0::RATE_44100}, - {48000, SampleRate_2_0::RATE_48000}, - {88200, SampleRate_2_0::RATE_88200}, - {96000, SampleRate_2_0::RATE_96000}, - {176400, SampleRate_2_0::RATE_176400}, - {192000, SampleRate_2_0::RATE_192000}, - {16000, SampleRate_2_0::RATE_16000}, - {24000, SampleRate_2_0::RATE_24000}, - }; - const static std::unordered_map<int32_t, SampleRate_2_1> sample_rate_to_hidl_2_1_map{ {44100, SampleRate_2_1::RATE_44100}, @@ -211,20 +189,6 @@ const static std::unordered_map<LdacQualityIndex, LdacQualityIndex_2_0> {LdacQualityIndex::ABR, LdacQualityIndex_2_0::QUALITY_ABR}, }; -const static std::unordered_map<LeAudioMode, LeAudioMode_2_2> - leaudio_mode_to_hidl_map{ - {LeAudioMode::UNKNOWN, LeAudioMode_2_2::UNKNOWN}, - {LeAudioMode::UNICAST, LeAudioMode_2_2::UNICAST}, - {LeAudioMode::BROADCAST, LeAudioMode_2_2::BROADCAST}, - }; - -inline SessionType from_session_type_2_0( - const SessionType_2_0& session_type_hidl) { - auto it = session_type_2_0_to_aidl_map.find(session_type_hidl); - if (it != session_type_2_0_to_aidl_map.end()) return it->second; - return SessionType::UNKNOWN; -} - inline SessionType from_session_type_2_1( const SessionType_2_1& session_type_hidl) { auto it = session_type_2_1_to_aidl_map.find(session_type_hidl); @@ -232,6 +196,11 @@ inline SessionType from_session_type_2_1( return SessionType::UNKNOWN; } +inline SessionType from_session_type_2_0( + const SessionType_2_0& session_type_hidl) { + return from_session_type_2_1(static_cast<SessionType_2_1>(session_type_hidl)); +} + inline HidlStatus to_hidl_status(const BluetoothAudioStatus& status) { switch (status) { case BluetoothAudioStatus::SUCCESS: @@ -243,18 +212,19 @@ inline HidlStatus to_hidl_status(const BluetoothAudioStatus& status) { } } -inline SampleRate_2_0 to_hidl_sample_rate_2_0(const int32_t sample_rate_hz) { - auto it = sample_rate_to_hidl_2_0_map.find(sample_rate_hz); - if (it != sample_rate_to_hidl_2_0_map.end()) return it->second; - return SampleRate_2_0::RATE_UNKNOWN; -} - inline SampleRate_2_1 to_hidl_sample_rate_2_1(const int32_t sample_rate_hz) { auto it = sample_rate_to_hidl_2_1_map.find(sample_rate_hz); if (it != sample_rate_to_hidl_2_1_map.end()) return it->second; return SampleRate_2_1::RATE_UNKNOWN; } +inline SampleRate_2_0 to_hidl_sample_rate_2_0(const int32_t sample_rate_hz) { + auto it = sample_rate_to_hidl_2_1_map.find(sample_rate_hz); + if (it != sample_rate_to_hidl_2_1_map.end()) + return static_cast<SampleRate_2_0>(it->second); + return SampleRate_2_0::RATE_UNKNOWN; +} + inline BitsPerSample_2_0 to_hidl_bits_per_sample(const int8_t bit_per_sample) { switch (bit_per_sample) { case 16: @@ -449,18 +419,49 @@ inline Lc3Config_2_1 to_hidl_lc3_config_2_1( inline Lc3CodecConfig_2_1 to_hidl_leaudio_config_2_1( const LeAudioConfiguration& leaudio_config) { - auto& unicast_config = - leaudio_config.modeConfig - .get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>(); - - auto& le_codec_config = unicast_config.leAudioCodecConfig - .get<LeAudioCodecConfiguration::lc3Config>(); + Lc3CodecConfig_2_1 hidl_lc3_codec_config = { + .audioChannelAllocation = 0, + }; + if (leaudio_config.modeConfig.getTag() == + LeAudioConfiguration::LeAudioModeConfig::unicastConfig) { + auto& unicast_config = + leaudio_config.modeConfig + .get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>(); + if (unicast_config.leAudioCodecConfig.getTag() == + LeAudioCodecConfiguration::lc3Config) { + LOG(FATAL) << __func__ << ": unexpected codec type(vendor?)"; + } + auto& le_codec_config = unicast_config.leAudioCodecConfig + .get<LeAudioCodecConfiguration::lc3Config>(); - Lc3CodecConfig_2_1 hidl_lc3_codec_config; - hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config); + hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config); - hidl_lc3_codec_config.audioChannelAllocation = - unicast_config.streamMap.size(); + for (const auto& map : unicast_config.streamMap) { + hidl_lc3_codec_config.audioChannelAllocation |= + map.audioChannelAllocation; + } + } else { + // NOTE: Broadcast is not officially supported in HIDL + auto& bcast_config = + leaudio_config.modeConfig + .get<LeAudioConfiguration::LeAudioModeConfig::broadcastConfig>(); + if (bcast_config.streamMap.empty()) { + return hidl_lc3_codec_config; + } + if (bcast_config.streamMap[0].leAudioCodecConfig.getTag() != + LeAudioCodecConfiguration::lc3Config) { + LOG(FATAL) << __func__ << ": unexpected codec type(vendor?)"; + } + auto& le_codec_config = + bcast_config.streamMap[0] + .leAudioCodecConfig.get<LeAudioCodecConfiguration::lc3Config>(); + hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config); + + for (const auto& map : bcast_config.streamMap) { + hidl_lc3_codec_config.audioChannelAllocation |= + map.audioChannelAllocation; + } + } return hidl_lc3_codec_config; } @@ -468,13 +469,10 @@ inline Lc3CodecConfig_2_1 to_hidl_leaudio_config_2_1( inline LeAudioConfig_2_2 to_hidl_leaudio_config_2_2( const LeAudioConfiguration& leaudio_config) { LeAudioConfig_2_2 hidl_leaudio_config; - if (leaudio_mode_to_hidl_map.find(leaudio_config.mode) != - leaudio_mode_to_hidl_map.end()) { - hidl_leaudio_config.mode = leaudio_mode_to_hidl_map.at(leaudio_config.mode); - } if (leaudio_config.modeConfig.getTag() == LeAudioConfiguration::LeAudioModeConfig::unicastConfig) { + hidl_leaudio_config.mode = LeAudioMode_2_2::UNICAST; auto& unicast_config = leaudio_config.modeConfig .get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>(); @@ -497,6 +495,7 @@ inline LeAudioConfig_2_2 to_hidl_leaudio_config_2_2( } } else if (leaudio_config.modeConfig.getTag() == LeAudioConfiguration::LeAudioModeConfig::broadcastConfig) { + hidl_leaudio_config.mode = LeAudioMode_2_2::BROADCAST; auto bcast_config = leaudio_config.modeConfig .get<LeAudioConfiguration::LeAudioModeConfig::broadcastConfig>(); @@ -641,6 +640,12 @@ size_t HidlToAidlMiddleware_2_0::OutWritePcmData( from_session_type_2_0(session_type), buffer, bytes); } +size_t HidlToAidlMiddleware_2_0::InReadPcmData( + const SessionType_2_0& session_type, void* buffer, size_t bytes) { + return BluetoothAudioSessionControl::InReadPcmData( + from_session_type_2_0(session_type), buffer, bytes); +} + bool HidlToAidlMiddleware_2_0::IsAidlAvailable() { return BluetoothAudioSession::IsAidlAvailable(); } @@ -761,6 +766,13 @@ void HidlToAidlMiddleware_2_2::StopStream(const SessionType_2_1& session_type) { from_session_type_2_1(session_type)); } +void HidlToAidlMiddleware_2_2::UpdateTracksMetadata( + const SessionType_2_1& session_type, + const struct source_metadata* source_metadata) { + return BluetoothAudioSessionControl::UpdateSourceMetadata( + from_session_type_2_1(session_type), *source_metadata); +} + void HidlToAidlMiddleware_2_2::UpdateSinkMetadata( const SessionType_2_1& session_type, const struct sink_metadata* sink_metadata) { diff --git a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h index d10ee3759a..b124d8ff0b 100644 --- a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h +++ b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h @@ -64,6 +64,9 @@ class HidlToAidlMiddleware_2_0 { static size_t OutWritePcmData(const SessionType_2_0& session_type, const void* buffer, size_t bytes); + + static size_t InReadPcmData(const SessionType_2_0& session_type, void* buffer, + size_t bytes); }; } // namespace audio diff --git a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_2.h b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_2.h index 149e4042d1..f6c3e5c731 100644 --- a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_2.h +++ b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_2.h @@ -54,6 +54,10 @@ class HidlToAidlMiddleware_2_2 { static void StopStream(const SessionType_2_1& session_type); + static void UpdateTracksMetadata( + const SessionType_2_1& session_type, + const struct source_metadata* source_metadata); + static void UpdateSinkMetadata(const SessionType_2_1& session_type, const struct sink_metadata* sink_metadata); }; diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp index 6d5608b0a4..283952e52c 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSession.cpp +++ b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp @@ -437,6 +437,9 @@ size_t BluetoothAudioSession::OutWritePcmData(const void* buffer, // The control function reads stream from FMQ size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) { + if (HidlToAidlMiddleware_2_0::IsAidlAvailable()) + return HidlToAidlMiddleware_2_0::InReadPcmData(session_type_, buffer, + bytes); if (buffer == nullptr || !bytes) return 0; size_t totalRead = 0; int ms_timeout = kFmqReceiveTimeoutMs; diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h index 368939e6f8..c270ef0d6b 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h +++ b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h @@ -152,7 +152,7 @@ class BluetoothAudioSessionControl_2_2 { std::shared_ptr<BluetoothAudioSession_2_2> session_ptr = BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); if (session_ptr != nullptr) { - session_ptr->GetAudioSession()->UpdateTracksMetadata(source_metadata); + session_ptr->UpdateTracksMetadata(source_metadata); } } diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp index 4613ddc05a..ceb0662b0a 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp +++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp @@ -31,9 +31,13 @@ namespace audio { using ::aidl::android::hardware::bluetooth::audio::HidlToAidlMiddleware_2_0; using ::aidl::android::hardware::bluetooth::audio::HidlToAidlMiddleware_2_2; +using ::android::hardware::audio::common::V5_0::AudioContentType; using ::android::hardware::audio::common::V5_0::AudioSource; +using ::android::hardware::audio::common::V5_0::AudioUsage; +using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata; using ::android::hardware::audio::common::V5_0::RecordTrackMetadata; using ::android::hardware::audio::common::V5_0::SinkMetadata; +using ::android::hardware::audio::common::V5_0::SourceMetadata; using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample; using ::android::hardware::bluetooth::audio::V2_0::ChannelMode; using ::android::hardware::bluetooth::audio::V2_2::LeAudioConfiguration; @@ -128,6 +132,53 @@ BluetoothAudioSession_2_2::GetAudioSession_2_1() { return audio_session_2_1; } +void BluetoothAudioSession_2_2::UpdateTracksMetadata( + const struct source_metadata* source_metadata) { + if (HidlToAidlMiddleware_2_0::IsAidlAvailable()) + return HidlToAidlMiddleware_2_2::UpdateTracksMetadata(raw_session_type_, + source_metadata); + std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_); + if (!IsSessionReady()) { + LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << " has NO session"; + return; + } + + ssize_t track_count = source_metadata->track_count; + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << ", " << track_count << " track(s)"; + + if (session_type_2_1_ == SessionType_2_1::UNKNOWN) { + audio_session->UpdateTracksMetadata(source_metadata); + return; + } + + struct playback_track_metadata* track = source_metadata->tracks; + SourceMetadata sourceMetadata; + PlaybackTrackMetadata* halMetadata; + + sourceMetadata.tracks.resize(track_count); + halMetadata = sourceMetadata.tracks.data(); + while (track_count && track) { + halMetadata->usage = static_cast<AudioUsage>(track->usage); + halMetadata->contentType = + static_cast<AudioContentType>(track->content_type); + halMetadata->gain = track->gain; + LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << ", usage=" << toString(halMetadata->usage) + << ", content=" << toString(halMetadata->contentType) + << ", gain=" << halMetadata->gain; + --track_count; + ++track; + ++halMetadata; + } + auto hal_retval = audio_session->stack_iface_->updateMetadata(sourceMetadata); + if (!hal_retval.isOk()) { + LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType=" + << toString(session_type_2_1_) << " failed"; + } +} + void BluetoothAudioSession_2_2::UpdateSinkMetadata( const struct sink_metadata* sink_metadata) { if (HidlToAidlMiddleware_2_0::IsAidlAvailable()) diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h index b6f96ab25c..e04ad80ffa 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h +++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h @@ -152,6 +152,7 @@ class BluetoothAudioSession_2_2 { const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration GetAudioConfig(); + void UpdateTracksMetadata(const struct source_metadata* source_metadata); void UpdateSinkMetadata(const struct sink_metadata* sink_metadata); static constexpr ::android::hardware::bluetooth::audio::V2_2:: diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp index dd45b0dca8..8c44010441 100644 --- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp +++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp @@ -969,7 +969,6 @@ public: void getPrivacyTestPatternModes( const camera_metadata_t* staticMetadata, std::unordered_set<int32_t>* privacyTestPatternModes/*out*/); - static bool isColorCamera(const camera_metadata_t *metadata); static V3_2::DataspaceFlags getDataspace(PixelFormat format); @@ -6880,142 +6879,6 @@ TEST_P(CameraHidlTest, configureInjectionStreamsWithSessionParameters) { } } -// Test the multi-camera API requirement for Google Requirement Freeze S -// Note that this requirement can only be partially tested. If a vendor -// device doesn't expose a physical camera in any shape or form, there is no way -// the test can catch it. -TEST_P(CameraHidlTest, grfSMultiCameraTest) { - const int socGrfApi = property_get_int32("ro.board.first_api_level", /*default*/ -1); - if (socGrfApi < 31 /*S*/) { - // Non-GRF devices, or version < 31 Skip - ALOGI("%s: socGrfApi level is %d. Skipping", __FUNCTION__, socGrfApi); - return; - } - - // Test that if more than one rear-facing color camera is - // supported, there must be at least one rear-facing logical camera. - hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider); - // Back facing non-logical color cameras - std::set<std::string> rearColorCameras; - // Back facing logical cameras' physical camera Id sets - std::set<std::set<std::string>> rearPhysicalIds; - for (const auto& name : cameraDeviceNames) { - std::string cameraId; - int deviceVersion = getCameraDeviceVersionAndId(name, mProviderType, &cameraId); - switch (deviceVersion) { - case CAMERA_DEVICE_API_VERSION_3_8: - case CAMERA_DEVICE_API_VERSION_3_7: - case CAMERA_DEVICE_API_VERSION_3_6: - case CAMERA_DEVICE_API_VERSION_3_5: - case CAMERA_DEVICE_API_VERSION_3_4: - case CAMERA_DEVICE_API_VERSION_3_3: - case CAMERA_DEVICE_API_VERSION_3_2: { - ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x; - ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str()); - Return<void> ret; - ret = mProvider->getCameraDeviceInterface_V3_x( - name, [&](auto status, const auto& device) { - ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status); - ASSERT_EQ(Status::OK, status); - ASSERT_NE(device, nullptr); - device3_x = device; - }); - ASSERT_TRUE(ret.isOk()); - - ret = device3_x->getCameraCharacteristics([&](auto status, const auto& chars) { - ASSERT_EQ(Status::OK, status); - const camera_metadata_t* metadata = (camera_metadata_t*)chars.data(); - - // Skip if this is not a color camera. - if (!CameraHidlTest::isColorCamera(metadata)) { - return; - } - - // Check camera facing. Skip if facing is not BACK. - // If this is not a logical camera, only note down - // the camera ID, and skip. - camera_metadata_ro_entry entry; - int retcode = find_camera_metadata_ro_entry( - metadata, ANDROID_LENS_FACING, &entry); - ASSERT_EQ(retcode, 0); - ASSERT_GT(entry.count, 0); - uint8_t facing = entry.data.u8[0]; - bool isLogicalCamera = (isLogicalMultiCamera(metadata) == Status::OK); - if (facing != ANDROID_LENS_FACING_BACK) { - // Not BACK facing. Skip. - return; - } - if (!isLogicalCamera) { - rearColorCameras.insert(cameraId); - return; - } - - // Check logical camera's physical camera IDs for color - // cameras. - std::unordered_set<std::string> physicalCameraIds; - Status s = getPhysicalCameraIds(metadata, &physicalCameraIds); - ASSERT_EQ(Status::OK, s); - rearPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end()); - for (const auto& physicalId : physicalCameraIds) { - // Skip if the physicalId is publicly available - for (auto& deviceName : cameraDeviceNames) { - std::string publicVersion, publicId; - ASSERT_TRUE(::matchDeviceName(deviceName, mProviderType, - &publicVersion, &publicId)); - if (physicalId == publicId) { - // Skip because public Ids will be iterated in outer loop. - return; - } - } - - auto castResult = device::V3_5::ICameraDevice::castFrom(device3_x); - ASSERT_TRUE(castResult.isOk()); - ::android::sp<::android::hardware::camera::device::V3_5::ICameraDevice> - device3_5 = castResult; - ASSERT_NE(device3_5, nullptr); - - // Check camera characteristics for hidden camera id - Return<void> ret = device3_5->getPhysicalCameraCharacteristics( - physicalId, [&](auto status, const auto& chars) { - ASSERT_EQ(Status::OK, status); - const camera_metadata_t* physicalMetadata = - (camera_metadata_t*)chars.data(); - - if (CameraHidlTest::isColorCamera(physicalMetadata)) { - rearColorCameras.insert(physicalId); - } - }); - ASSERT_TRUE(ret.isOk()); - } - }); - ASSERT_TRUE(ret.isOk()); - } break; - case CAMERA_DEVICE_API_VERSION_1_0: { - // Not applicable - } break; - default: { - ALOGE("%s: Unsupported device version %d", __func__, deviceVersion); - ADD_FAILURE(); - } break; - } - } - - // If there are more than one rear-facing color camera, a logical - // multi-camera must be defined consisting of all rear-facing color - // cameras. - if (rearColorCameras.size() > 1) { - bool hasRearLogical = false; - for (const auto& physicalIds : rearPhysicalIds) { - if (std::includes(physicalIds.begin(), physicalIds.end(), - rearColorCameras.begin(), rearColorCameras.end())) { - hasRearLogical = true; - break; - } - } - ASSERT_TRUE(hasRearLogical); - } -} - // Retrieve all valid output stream resolutions from the camera // static characteristics. Status CameraHidlTest::getAvailableOutputStreams(const camera_metadata_t* staticMeta, @@ -7523,23 +7386,6 @@ Status CameraHidlTest::isMonochromeCamera(const camera_metadata_t *staticMeta) { return ret; } -bool CameraHidlTest::isColorCamera(const camera_metadata_t *metadata) { - camera_metadata_ro_entry entry; - int retcode = find_camera_metadata_ro_entry( - metadata, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry); - if ((0 == retcode) && (entry.count > 0)) { - bool isBackwardCompatible = (std::find(entry.data.u8, entry.data.u8 + entry.count, - ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) != - entry.data.u8 + entry.count); - bool isMonochrome = (std::find(entry.data.u8, entry.data.u8 + entry.count, - ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) != - entry.data.u8 + entry.count); - bool isColor = isBackwardCompatible && !isMonochrome; - return isColor; - } - return false; -} - // Retrieve the reprocess input-output format map from the static // camera characteristics. Status CameraHidlTest::getZSLInputOutputMap(camera_metadata_t *staticMeta, diff --git a/camera/provider/2.7/default/Android.bp b/camera/provider/2.7/default/Android.bp new file mode 100644 index 0000000000..bd5da2dbfa --- /dev/null +++ b/camera/provider/2.7/default/Android.bp @@ -0,0 +1,111 @@ +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"], +} + +cc_library_shared { + name: "android.hardware.camera.provider@2.7-external", + proprietary: true, + srcs: ["ExternalCameraProviderImpl_2_7.cpp"], + shared_libs: [ + "android.hardware.camera.common@1.0", + "android.hardware.camera.device@1.0", + "android.hardware.camera.device@3.2", + "android.hardware.camera.device@3.3", + "android.hardware.camera.device@3.4", + "android.hardware.camera.device@3.5", + "android.hardware.camera.device@3.6", + "android.hardware.camera.provider@2.4", + "android.hardware.camera.provider@2.5", + "android.hardware.camera.provider@2.6", + "android.hardware.camera.provider@2.7", + "android.hardware.graphics.mapper@2.0", + "android.hardware.graphics.mapper@3.0", + "android.hardware.graphics.mapper@4.0", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "camera.device@3.3-impl", + "camera.device@3.4-external-impl", + "camera.device@3.4-impl", + "camera.device@3.5-external-impl", + "camera.device@3.5-impl", + "camera.device@3.6-external-impl", + "libcamera_metadata", + "libcutils", + "libhardware", + "libhidlbase", + "liblog", + "libtinyxml2", + "libutils", + ], + static_libs: [ + "android.hardware.camera.common@1.0-helper", + ], + header_libs: [ + "camera.device@3.4-external-impl_headers", + "camera.device@3.5-external-impl_headers", + "camera.device@3.6-external-impl_headers", + ], + export_include_dirs: ["."], +} + +cc_defaults { + name: "camera_external_service_2_7_defaults", + defaults: ["hidl_defaults"], + proprietary: true, + relative_install_path: "hw", + srcs: ["external-service.cpp"], + compile_multilib: "32", + shared_libs: [ + "android.hardware.camera.common@1.0", + "android.hardware.camera.device@1.0", + "android.hardware.camera.device@3.2", + "android.hardware.camera.device@3.3", + "android.hardware.camera.device@3.4", + "android.hardware.camera.device@3.5", + "android.hardware.camera.provider@2.4", + "android.hardware.camera.provider@2.4-external", + "android.hardware.camera.provider@2.5", + "android.hardware.camera.provider@2.5-external", + "android.hardware.camera.provider@2.6", + "android.hardware.camera.provider@2.7", + "android.hardware.camera.provider@2.7-external", + "android.hardware.graphics.mapper@2.0", + "android.hardware.graphics.mapper@3.0", + "android.hardware.graphics.mapper@4.0", + "libbinder", + "libcamera_metadata", + "libhidlbase", + "liblog", + "libtinyxml2", + "libutils", + ], + static_libs: [ + "android.hardware.camera.common@1.0-helper", + ], + header_libs: [ + "camera.device@3.4-external-impl_headers", + "camera.device@3.4-impl_headers", + "camera.device@3.5-external-impl_headers", + "camera.device@3.5-impl_headers", + "camera.device@3.6-external-impl_headers", + ], +} + +cc_binary { + name: "android.hardware.camera.provider@2.7-external-service", + defaults: ["camera_external_service_2_7_defaults"], + init_rc: ["android.hardware.camera.provider@2.7-external-service.rc"], +} + +cc_binary { + name: "android.hardware.camera.provider@2.7-external-service-lazy", + overrides: ["android.hardware.camera.provider@2.7-external-service"], + defaults: ["camera_external_service_2_7_defaults"], + init_rc: ["android.hardware.camera.provider@2.7-external-service-lazy.rc"], + cflags: ["-DLAZY_SERVICE"], +} diff --git a/camera/provider/2.7/default/CameraProvider_2_7.h b/camera/provider/2.7/default/CameraProvider_2_7.h new file mode 100644 index 0000000000..c34905f3f3 --- /dev/null +++ b/camera/provider/2.7/default/CameraProvider_2_7.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_PROVIDER_V2_7_CAMERAPROVIDER_H +#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_7_CAMERAPROVIDER_H + +#include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h> +#include <android/hardware/camera/provider/2.7/ICameraProvider.h> +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> + +namespace android { +namespace hardware { +namespace camera { +namespace provider { +namespace V2_7 { +namespace implementation { + +using ::android::sp; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::camera::common::V1_0::CameraDeviceStatus; +using ::android::hardware::camera::common::V1_0::Status; +using ::android::hardware::camera::common::V1_0::TorchModeStatus; +using ::android::hardware::camera::common::V1_0::VendorTag; +using ::android::hardware::camera::common::V1_0::VendorTagSection; +using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback; +using ::android::hardware::camera::provider::V2_5::DeviceState; +using ::android::hardware::camera::provider::V2_7::CameraIdAndStreamCombination; +using ::android::hardware::camera::provider::V2_7::ICameraProvider; +using ::android::hidl::base::V1_0::IBase; + +// Default recommended RPC thread count for camera provider implementations +const int HWBINDER_THREAD_COUNT = 6; + +template <typename IMPL> +struct CameraProvider : public ICameraProvider { + CameraProvider() : impl() {} + ~CameraProvider() {} + + // Caller must use this method to check if CameraProvider ctor failed + bool isInitFailed() { return impl.isInitFailed(); } + + // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow. + Return<Status> setCallback(const sp<ICameraProviderCallback>& callback) override { + return impl.setCallback(callback); + } + + Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override { + return impl.getVendorTags(_hidl_cb); + } + + Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override { + return impl.getCameraIdList(_hidl_cb); + } + + Return<void> isSetTorchModeSupported(isSetTorchModeSupported_cb _hidl_cb) override { + return impl.isSetTorchModeSupported(_hidl_cb); + } + + Return<void> getCameraDeviceInterface_V1_x(const hidl_string& cameraDeviceName, + getCameraDeviceInterface_V1_x_cb _hidl_cb) override { + return impl.getCameraDeviceInterface_V1_x(cameraDeviceName, _hidl_cb); + } + + Return<void> getCameraDeviceInterface_V3_x(const hidl_string& cameraDeviceName, + getCameraDeviceInterface_V3_x_cb _hidl_cb) override { + return impl.getCameraDeviceInterface_V3_x(cameraDeviceName, _hidl_cb); + } + + // Methods from ::android::hardware::camera::provider::V2_5::ICameraProvider follow. + Return<void> notifyDeviceStateChange(hardware::hidl_bitfield<DeviceState> newState) override { + return impl.notifyDeviceStateChange(newState); + } + + // Methods from ::android::hardware::camera::provider::V2_7::ICameraProvider follow. + Return<void> getConcurrentStreamingCameraIds( + getConcurrentStreamingCameraIds_cb _hidl_cb) override { + return impl.getConcurrentStreamingCameraIds(_hidl_cb); + } + + Return<void> isConcurrentStreamCombinationSupported( + const hidl_vec< + ::android::hardware::camera::provider::V2_6::CameraIdAndStreamCombination>& + configs, + isConcurrentStreamCombinationSupported_cb _hidl_cb) override { + return impl.isConcurrentStreamCombinationSupported(configs, _hidl_cb); + } + + Return<void> isConcurrentStreamCombinationSupported_2_7( + const hidl_vec<CameraIdAndStreamCombination>& configs, + isConcurrentStreamCombinationSupported_2_7_cb _hidl_cb) override { + return impl.isConcurrentStreamCombinationSupported_2_7(configs, _hidl_cb); + } + + private: + IMPL impl; +}; + +} // namespace implementation +} // namespace V2_7 +} // namespace provider +} // namespace camera +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_CAMERAPROVIDER_H
\ No newline at end of file diff --git a/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp b/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp new file mode 100644 index 0000000000..b63e3bb88b --- /dev/null +++ b/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "CamPrvdr@2.7-external" +//#define LOG_NDEBUG 0 +#include <log/log.h> + +#include <cutils/properties.h> +#include <errno.h> +#include <linux/videodev2.h> +#include <sys/inotify.h> +#include <regex> +#include "ExternalCameraDevice_3_4.h" +#include "ExternalCameraDevice_3_5.h" +#include "ExternalCameraDevice_3_6.h" +#include "ExternalCameraProviderImpl_2_7.h" + +namespace android { +namespace hardware { +namespace camera { +namespace provider { +namespace V2_7 { +namespace implementation { + +namespace { +// "device@<version>/external/<id>" +const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/external/(.+)"); +const int kMaxDevicePathLen = 256; +const char* kDevicePath = "/dev/"; +constexpr char kPrefix[] = "video"; +constexpr int kPrefixLen = sizeof(kPrefix) - 1; +constexpr int kDevicePrefixLen = sizeof(kDevicePath) + kPrefixLen + 1; + +bool matchDeviceName(int cameraIdOffset, const hidl_string& deviceName, std::string* deviceVersion, + std::string* cameraDevicePath) { + std::string deviceNameStd(deviceName.c_str()); + std::smatch sm; + if (std::regex_match(deviceNameStd, sm, kDeviceNameRE)) { + if (deviceVersion != nullptr) { + *deviceVersion = sm[1]; + } + if (cameraDevicePath != nullptr) { + *cameraDevicePath = "/dev/video" + std::to_string(std::stoi(sm[2]) - cameraIdOffset); + } + return true; + } + return false; +} + +} // anonymous namespace + +ExternalCameraProviderImpl_2_7::ExternalCameraProviderImpl_2_7() + : mCfg(ExternalCameraConfig::loadFromCfg()) { + mHotPlugThread = sp<HotplugThread>::make(this); + mHotPlugThread->run("ExtCamHotPlug", PRIORITY_BACKGROUND); + + mPreferredHal3MinorVersion = + property_get_int32("ro.vendor.camera.external.hal3TrebleMinorVersion", 4); + ALOGV("Preferred HAL 3 minor version is %d", mPreferredHal3MinorVersion); + switch (mPreferredHal3MinorVersion) { + case 4: + case 5: + case 6: + // OK + break; + default: + ALOGW("Unknown minor camera device HAL version %d in property " + "'camera.external.hal3TrebleMinorVersion', defaulting to 4", + mPreferredHal3MinorVersion); + mPreferredHal3MinorVersion = 4; + } +} + +ExternalCameraProviderImpl_2_7::~ExternalCameraProviderImpl_2_7() { + mHotPlugThread->requestExit(); +} + +Return<Status> ExternalCameraProviderImpl_2_7::setCallback( + const sp<ICameraProviderCallback>& callback) { + Mutex::Autolock _l(mLock); + mCallbacks = callback; + if (mCallbacks == nullptr) { + return Status::OK; + } + // Send a callback for all devices to initialize + { + for (const auto& pair : mCameraStatusMap) { + mCallbacks->cameraDeviceStatusChange(pair.first, pair.second); + } + } + + return Status::OK; +} + +Return<void> ExternalCameraProviderImpl_2_7::getVendorTags( + ICameraProvider::getVendorTags_cb _hidl_cb) { + // No vendor tag support for USB camera + hidl_vec<VendorTagSection> zeroSections; + _hidl_cb(Status::OK, zeroSections); + return Void(); +} + +Return<void> ExternalCameraProviderImpl_2_7::getCameraIdList( + ICameraProvider::getCameraIdList_cb _hidl_cb) { + // External camera HAL always report 0 camera, and extra cameras + // are just reported via cameraDeviceStatusChange callbacks + hidl_vec<hidl_string> hidlDeviceNameList; + _hidl_cb(Status::OK, hidlDeviceNameList); + return Void(); +} + +void ExternalCameraProviderImpl_2_7::updateAttachedCameras() { + ALOGV("%s start scaning for existing V4L2 devices", __FUNCTION__); + // Find existing /dev/video* devices + DIR* devdir = opendir(kDevicePath); + if (devdir == 0) { + ALOGE("%s: cannot open %s! Exiting threadloop", __FUNCTION__, kDevicePath); + return; + } + + struct dirent* de; + while ((de = readdir(devdir)) != 0) { + // Find external v4l devices that's existing before we start watching and add them + if (!strncmp(kPrefix, de->d_name, kPrefixLen)) { + // TODO: This might reject some valid devices. Ex: internal is 33 and a device named 3 + // is added. + std::string deviceId(de->d_name + kPrefixLen); + if (mCfg.mInternalDevices.count(deviceId) == 0) { + ALOGV("Non-internal v4l device %s found", de->d_name); + char v4l2DevicePath[kMaxDevicePathLen]; + snprintf(v4l2DevicePath, kMaxDevicePathLen, "%s%s", kDevicePath, de->d_name); + deviceAdded(v4l2DevicePath); + } + } + } + closedir(devdir); +} + +Return<void> ExternalCameraProviderImpl_2_7::isSetTorchModeSupported( + ICameraProvider::isSetTorchModeSupported_cb _hidl_cb) { + // setTorchMode API is supported, though right now no external camera device + // has a flash unit. + _hidl_cb(Status::OK, true); + return Void(); +} + +Return<void> ExternalCameraProviderImpl_2_7::getCameraDeviceInterface_V1_x( + const hidl_string&, ICameraProvider::getCameraDeviceInterface_V1_x_cb _hidl_cb) { + // External Camera HAL does not support HAL1 + _hidl_cb(Status::OPERATION_NOT_SUPPORTED, nullptr); + return Void(); +} + +Return<void> ExternalCameraProviderImpl_2_7::getCameraDeviceInterface_V3_x( + const hidl_string& cameraDeviceName, + ICameraProvider::getCameraDeviceInterface_V3_x_cb _hidl_cb) { + std::string cameraDevicePath, deviceVersion; + bool match = matchDeviceName(mCfg.cameraIdOffset, cameraDeviceName, &deviceVersion, + &cameraDevicePath); + if (!match) { + _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr); + return Void(); + } + + if (mCameraStatusMap.count(cameraDeviceName) == 0 || + mCameraStatusMap[cameraDeviceName] != CameraDeviceStatus::PRESENT) { + _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr); + return Void(); + } + + sp<device::V3_4::implementation::ExternalCameraDevice> deviceImpl; + switch (mPreferredHal3MinorVersion) { + case 4: { + ALOGV("Constructing v3.4 external camera device"); + deviceImpl = + new device::V3_4::implementation::ExternalCameraDevice(cameraDevicePath, mCfg); + break; + } + case 5: { + ALOGV("Constructing v3.5 external camera device"); + deviceImpl = + new device::V3_5::implementation::ExternalCameraDevice(cameraDevicePath, mCfg); + break; + } + case 6: { + ALOGV("Constructing v3.6 external camera device"); + deviceImpl = + new device::V3_6::implementation::ExternalCameraDevice(cameraDevicePath, mCfg); + break; + } + default: + ALOGE("%s: Unknown HAL minor version %d!", __FUNCTION__, mPreferredHal3MinorVersion); + _hidl_cb(Status::INTERNAL_ERROR, nullptr); + return Void(); + } + + if (deviceImpl == nullptr || deviceImpl->isInitFailed()) { + ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraDevicePath.c_str()); + _hidl_cb(Status::INTERNAL_ERROR, nullptr); + return Void(); + } + + IF_ALOGV() { + deviceImpl->getInterface()->interfaceChain( + [](::android::hardware::hidl_vec<::android::hardware::hidl_string> interfaceChain) { + ALOGV("Device interface chain:"); + for (auto iface : interfaceChain) { + ALOGV(" %s", iface.c_str()); + } + }); + } + + _hidl_cb(Status::OK, deviceImpl->getInterface()); + + return Void(); +} + +void ExternalCameraProviderImpl_2_7::addExternalCamera(const char* devName) { + ALOGV("ExtCam: adding %s to External Camera HAL!", devName); + Mutex::Autolock _l(mLock); + std::string deviceName; + std::string cameraId = + std::to_string(mCfg.cameraIdOffset + std::atoi(devName + kDevicePrefixLen)); + if (mPreferredHal3MinorVersion == 6) { + deviceName = std::string("device@3.6/external/") + cameraId; + } else if (mPreferredHal3MinorVersion == 5) { + deviceName = std::string("device@3.5/external/") + cameraId; + } else { + deviceName = std::string("device@3.4/external/") + cameraId; + } + mCameraStatusMap[deviceName] = CameraDeviceStatus::PRESENT; + if (mCallbacks != nullptr) { + mCallbacks->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::PRESENT); + } +} + +void ExternalCameraProviderImpl_2_7::deviceAdded(const char* devName) { + { + base::unique_fd fd(::open(devName, O_RDWR)); + if (fd.get() < 0) { + ALOGE("%s open v4l2 device %s failed:%s", __FUNCTION__, devName, strerror(errno)); + return; + } + + struct v4l2_capability capability; + int ret = ioctl(fd.get(), VIDIOC_QUERYCAP, &capability); + if (ret < 0) { + ALOGE("%s v4l2 QUERYCAP %s failed", __FUNCTION__, devName); + return; + } + + if (!(capability.device_caps & V4L2_CAP_VIDEO_CAPTURE)) { + ALOGW("%s device %s does not support VIDEO_CAPTURE", __FUNCTION__, devName); + return; + } + } + // See if we can initialize ExternalCameraDevice correctly + sp<device::V3_4::implementation::ExternalCameraDevice> deviceImpl = + new device::V3_4::implementation::ExternalCameraDevice(devName, mCfg); + if (deviceImpl == nullptr || deviceImpl->isInitFailed()) { + ALOGW("%s: Attempt to init camera device %s failed!", __FUNCTION__, devName); + return; + } + deviceImpl.clear(); + + addExternalCamera(devName); + return; +} + +void ExternalCameraProviderImpl_2_7::deviceRemoved(const char* devName) { + Mutex::Autolock _l(mLock); + std::string deviceName; + std::string cameraId = + std::to_string(mCfg.cameraIdOffset + std::atoi(devName + kDevicePrefixLen)); + if (mPreferredHal3MinorVersion == 6) { + deviceName = std::string("device@3.6/external/") + cameraId; + } else if (mPreferredHal3MinorVersion == 5) { + deviceName = std::string("device@3.5/external/") + cameraId; + } else { + deviceName = std::string("device@3.4/external/") + cameraId; + } + if (mCameraStatusMap.erase(deviceName) != 0) { + if (mCallbacks != nullptr) { + mCallbacks->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::NOT_PRESENT); + } + } else { + ALOGE("%s: cannot find camera device %s", __FUNCTION__, devName); + } +} + +ExternalCameraProviderImpl_2_7::HotplugThread::HotplugThread(ExternalCameraProviderImpl_2_7* parent) + : Thread(/*canCallJava*/ false), + mParent(parent), + mInternalDevices(parent->mCfg.mInternalDevices) {} + +ExternalCameraProviderImpl_2_7::HotplugThread::~HotplugThread() {} + +bool ExternalCameraProviderImpl_2_7::HotplugThread::threadLoop() { + // Update existing cameras + mParent->updateAttachedCameras(); + + // Watch new video devices + mINotifyFD = inotify_init(); + if (mINotifyFD < 0) { + ALOGE("%s: inotify init failed! Exiting threadloop", __FUNCTION__); + return true; + } + + mWd = inotify_add_watch(mINotifyFD, kDevicePath, IN_CREATE | IN_DELETE); + if (mWd < 0) { + ALOGE("%s: inotify add watch failed! Exiting threadloop", __FUNCTION__); + return true; + } + + bool done = false; + char eventBuf[512]; + while (!done) { + int offset = 0; + int ret = read(mINotifyFD, eventBuf, sizeof(eventBuf)); + if (ret >= (int)sizeof(struct inotify_event)) { + while (offset < ret) { + struct inotify_event* event = (struct inotify_event*)&eventBuf[offset]; + if (event->wd == mWd) { + ALOGV("%s inotify_event %s", __FUNCTION__, event->name); + if (!strncmp(kPrefix, event->name, kPrefixLen)) { + std::string deviceId(event->name + kPrefixLen); + if (mInternalDevices.count(deviceId) == 0) { + char v4l2DevicePath[kMaxDevicePathLen]; + snprintf(v4l2DevicePath, kMaxDevicePathLen, "%s%s", kDevicePath, + event->name); + if (event->mask & IN_CREATE) { + mParent->deviceAdded(v4l2DevicePath); + } + if (event->mask & IN_DELETE) { + mParent->deviceRemoved(v4l2DevicePath); + } + } + } + } + offset += sizeof(struct inotify_event) + event->len; + } + } + } + + return true; +} + +Return<void> ExternalCameraProviderImpl_2_7::notifyDeviceStateChange( + hidl_bitfield<DeviceState> /*newState*/) { + return Void(); +} + +Return<void> ExternalCameraProviderImpl_2_7::getConcurrentStreamingCameraIds( + ICameraProvider::getConcurrentStreamingCameraIds_cb _hidl_cb) { + hidl_vec<hidl_vec<hidl_string>> hidl_camera_id_combinations; + _hidl_cb(Status::OK, hidl_camera_id_combinations); + return Void(); +} + +Return<void> ExternalCameraProviderImpl_2_7::isConcurrentStreamCombinationSupported( + const hidl_vec<::android::hardware::camera::provider::V2_6:: + CameraIdAndStreamCombination>& /* configs */, + ICameraProvider::isConcurrentStreamCombinationSupported_cb _hidl_cb) { + _hidl_cb(Status::OK, false); + return Void(); +} + +Return<void> ExternalCameraProviderImpl_2_7::isConcurrentStreamCombinationSupported_2_7( + const hidl_vec<CameraIdAndStreamCombination>& /* configs */, + ICameraProvider::isConcurrentStreamCombinationSupported_2_7_cb _hidl_cb) { + _hidl_cb(Status::OK, false); + return Void(); +} + +} // namespace implementation +} // namespace V2_7 +} // namespace provider +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.h b/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.h new file mode 100644 index 0000000000..da9f6b3a91 --- /dev/null +++ b/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_PROVIDER_V2_7_EXTCAMERAPROVIDER_H +#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_7_EXTCAMERAPROVIDER_H + +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> +#include <utils/Mutex.h> +#include <utils/Thread.h> +#include <string> +#include <unordered_map> +#include <unordered_set> +#include "ExternalCameraUtils.h" + +#include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h> +#include <android/hardware/camera/provider/2.7/ICameraProvider.h> + +namespace android { +namespace hardware { +namespace camera { +namespace provider { +namespace V2_7 { +namespace implementation { + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::camera::common::V1_0::CameraDeviceStatus; +using ::android::hardware::camera::common::V1_0::Status; +using ::android::hardware::camera::common::V1_0::VendorTagSection; +using ::android::hardware::camera::external::common::ExternalCameraConfig; +using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback; +using ::android::hardware::camera::provider::V2_5::DeviceState; +using ::android::hardware::camera::provider::V2_7::CameraIdAndStreamCombination; +using ::android::hardware::camera::provider::V2_7::ICameraProvider; +using ::android::hidl::base::V1_0::IBase; + +/** + * The implementation of external webcam CameraProvider 2.7, separated + * from the HIDL interface layer to allow for implementation reuse by later + * provider versions. + * + * This camera provider supports standard UVC webcameras via the Linux V4L2 + * UVC driver. + */ +struct ExternalCameraProviderImpl_2_7 { + ExternalCameraProviderImpl_2_7(); + ~ExternalCameraProviderImpl_2_7(); + + // Caller must use this method to check if CameraProvider ctor failed + bool isInitFailed() { return false; } + + // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow. + Return<Status> setCallback(const sp<ICameraProviderCallback>& callback); + Return<void> getVendorTags(ICameraProvider::getVendorTags_cb _hidl_cb); + Return<void> getCameraIdList(ICameraProvider::getCameraIdList_cb _hidl_cb); + Return<void> isSetTorchModeSupported(ICameraProvider::isSetTorchModeSupported_cb _hidl_cb); + Return<void> getCameraDeviceInterface_V1_x(const hidl_string&, + ICameraProvider::getCameraDeviceInterface_V1_x_cb); + Return<void> getCameraDeviceInterface_V3_x(const hidl_string&, + ICameraProvider::getCameraDeviceInterface_V3_x_cb); + + // Methods from ::android::hardware::camera::provider::V2_5::ICameraProvider follow. + Return<void> notifyDeviceStateChange(hidl_bitfield<DeviceState> newState); + + // Methods from ::android::hardware::camera::provider::V2_7::ICameraProvider follow. + Return<void> getConcurrentStreamingCameraIds( + ICameraProvider::getConcurrentStreamingCameraIds_cb _hidl_cb); + + Return<void> isConcurrentStreamCombinationSupported( + const hidl_vec< + ::android::hardware::camera::provider::V2_6::CameraIdAndStreamCombination>& + configs, + ICameraProvider::isConcurrentStreamCombinationSupported_cb _hidl_cb); + + Return<void> isConcurrentStreamCombinationSupported_2_7( + const hidl_vec<CameraIdAndStreamCombination>& configs, + ICameraProvider::isConcurrentStreamCombinationSupported_2_7_cb _hidl_cb); + + private: + void addExternalCamera(const char* devName); + + void deviceAdded(const char* devName); + + void deviceRemoved(const char* devName); + + void updateAttachedCameras(); + + class HotplugThread : public android::Thread { + public: + HotplugThread(ExternalCameraProviderImpl_2_7* parent); + ~HotplugThread(); + + virtual bool threadLoop() override; + + private: + ExternalCameraProviderImpl_2_7* mParent = nullptr; + const std::unordered_set<std::string> mInternalDevices; + + int mINotifyFD = -1; + int mWd = -1; + }; + + Mutex mLock; + sp<ICameraProviderCallback> mCallbacks = nullptr; + std::unordered_map<std::string, CameraDeviceStatus> mCameraStatusMap; // camera id -> status + const ExternalCameraConfig mCfg; + sp<HotplugThread> mHotPlugThread; + int mPreferredHal3MinorVersion; +}; + +} // namespace implementation +} // namespace V2_7 +} // namespace provider +} // namespace camera +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_7_EXTCAMERAPROVIDER_H diff --git a/camera/provider/2.7/default/android.hardware.camera.provider@2.7-external-service-lazy.rc b/camera/provider/2.7/default/android.hardware.camera.provider@2.7-external-service-lazy.rc new file mode 100644 index 0000000000..9292c4f959 --- /dev/null +++ b/camera/provider/2.7/default/android.hardware.camera.provider@2.7-external-service-lazy.rc @@ -0,0 +1,13 @@ +service vendor.camera-provider-2-7-ext /vendor/bin/hw/android.hardware.camera.provider@2.7-external-service-lazy + interface android.hardware.camera.provider@2.4::ICameraProvider external/0 + interface android.hardware.camera.provider@2.5::ICameraProvider external/0 + interface android.hardware.camera.provider@2.6::ICameraProvider external/0 + interface android.hardware.camera.provider@2.7::ICameraProvider external/0 + class hal + oneshot + disabled + user cameraserver + group audio camera input drmrpc usb + ioprio rt 4 + capabilities SYS_NICE + task_profiles CameraServiceCapacity MaxPerformance
\ No newline at end of file diff --git a/camera/provider/2.7/default/android.hardware.camera.provider@2.7-external-service.rc b/camera/provider/2.7/default/android.hardware.camera.provider@2.7-external-service.rc new file mode 100644 index 0000000000..2c9b782c1c --- /dev/null +++ b/camera/provider/2.7/default/android.hardware.camera.provider@2.7-external-service.rc @@ -0,0 +1,11 @@ +service vendor.camera-provider-2-7-ext /vendor/bin/hw/android.hardware.camera.provider@2.7-external-service + interface android.hardware.camera.provider@2.4::ICameraProvider external/0 + interface android.hardware.camera.provider@2.5::ICameraProvider external/0 + interface android.hardware.camera.provider@2.6::ICameraProvider external/0 + interface android.hardware.camera.provider@2.7::ICameraProvider external/0 + class hal + user cameraserver + group audio camera input drmrpc usb + ioprio rt 4 + capabilities SYS_NICE + task_profiles CameraServiceCapacity MaxPerformance diff --git a/camera/provider/2.7/default/external-service.cpp b/camera/provider/2.7/default/external-service.cpp new file mode 100644 index 0000000000..90b823970f --- /dev/null +++ b/camera/provider/2.7/default/external-service.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2021 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. + */ + +#ifdef LAZY_SERVICE +#define LOG_TAG "android.hardware.camera.provider@2.7-external-service-lazy" +#else +#define LOG_TAG "android.hardware.camera.provider@2.7-external-service" +#endif + +#include <android/hardware/camera/provider/2.7/ICameraProvider.h> +#include <binder/ProcessState.h> +#include <hidl/HidlLazyUtils.h> +#include <hidl/HidlTransportSupport.h> + +#include "CameraProvider_2_7.h" +#include "ExternalCameraProviderImpl_2_7.h" + +using android::status_t; +using android::hardware::camera::provider::V2_7::ICameraProvider; +using android::hidl::base::V1_0::IBase; + +#ifdef LAZY_SERVICE +const bool kLazyService = true; +#else +const bool kLazyService = false; +#endif + +int main() { + using namespace android::hardware::camera::provider::V2_7::implementation; + + ALOGI("CameraProvider@2.7 external webcam service is starting."); + + ::android::hardware::configureRpcThreadpool(/*threads*/ HWBINDER_THREAD_COUNT, + /*willJoin*/ true); + + ::android::sp<CameraProvider<ExternalCameraProviderImpl_2_7>> provider = + new CameraProvider<ExternalCameraProviderImpl_2_7>(); + + status_t status; + if (kLazyService) { + auto serviceRegistrar = ::android::hardware::LazyServiceRegistrar::getInstance(); + status = serviceRegistrar.registerService(provider, "external/0"); + } else { + status = provider->registerAsService("external/0"); + } + + LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering provider service: %d", + status); + + ::android::hardware::joinRpcThreadpool(); + + return 0; +}
\ No newline at end of file diff --git a/common/support/Android.bp b/common/support/Android.bp index 718901ef6a..12ab1f78a1 100644 --- a/common/support/Android.bp +++ b/common/support/Android.bp @@ -25,6 +25,7 @@ cc_library_static { apex_available: [ "//apex_available:platform", "com.android.neuralnetworks", + "com.android.media.swcodec", ], min_sdk_version: "29", } diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index 7a70f10cf6..8d37f1897a 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -60,6 +60,14 @@ <regex-instance>.*</regex-instance> </interface> </hal> + <hal format="aidl" optional="true"> + <name>android.hardware.automotive.evs</name> + <interface> + <name>IEvsEnumerator</name> + <instance>default</instance> + <regex-instance>[a-z]+/[0-9]+</regex-instance> + </interface> + </hal> <hal format="hidl" optional="true"> <name>android.hardware.automotive.evs</name> <version>1.0-1</version> @@ -284,6 +292,14 @@ <instance>default</instance> </interface> </hal> + <hal format="aidl" optional="true"> + <name>android.hardware.graphics.allocator</name> + <version>1</version> + <interface> + <name>IAllocator</name> + <instance>default</instance> + </interface> + </hal> <hal format="hidl" optional="false"> <name>android.hardware.graphics.composer</name> <version>2.1-4</version> @@ -470,6 +486,13 @@ <instance>default</instance> </interface> </hal> + <hal format="aidl" optional="true"> + <name>android.hardware.nfc</name> + <interface> + <name>INfc</name> + <instance>default</instance> + </interface> + </hal> <hal format="hidl" optional="true"> <name>android.hardware.oemlock</name> <version>1.0</version> @@ -688,7 +711,7 @@ <instance>default</instance> </interface> </hal> - <hal format="hidl" optional="true"> + <hal format="hidl" optional="false"> <name>android.hardware.thermal</name> <version>2.0</version> <interface> @@ -736,6 +759,13 @@ <instance>default</instance> </interface> </hal> + <hal format="aidl" optional="true"> + <name>android.hardware.usb</name> + <interface> + <name>IUsb</name> + <instance>default</instance> + </interface> + </hal> <hal format="hidl" optional="true"> <name>android.hardware.usb.gadget</name> <version>1.0-2</version> diff --git a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/Setting.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/Setting.aidl index 41bc9ae881..d998478db6 100644 --- a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/Setting.aidl +++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/Setting.aidl @@ -39,4 +39,6 @@ enum Setting { WIFI_SCANNING = 3, AIRPLANE_MODE = 4, MICROPHONE = 5, + BT_MAIN = 6, + BT_SCANNING = 7, } diff --git a/contexthub/aidl/android/hardware/contexthub/Setting.aidl b/contexthub/aidl/android/hardware/contexthub/Setting.aidl index f2e55dbeb7..91d4c3f0be 100644 --- a/contexthub/aidl/android/hardware/contexthub/Setting.aidl +++ b/contexthub/aidl/android/hardware/contexthub/Setting.aidl @@ -39,4 +39,12 @@ enum Setting { * by CHRE. */ MICROPHONE, + /** + * The main BT toggle in the Android settings for BT connectivity. + */ + BT_MAIN, + /** + * The "BT scanning" setting for location scans. + */ + BT_SCANNING, } diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp index a47f64e5be..f0583be581 100644 --- a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp +++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp @@ -300,6 +300,14 @@ TEST_P(ContextHubAidl, TestOnMicrophoneSettingChanged) { testSettingChanged(Setting::MICROPHONE); } +TEST_P(ContextHubAidl, TestOnBtMainSettingChanged) { + testSettingChanged(Setting::BT_MAIN); +} + +TEST_P(ContextHubAidl, TestOnBtScanningSettingChanged) { + testSettingChanged(Setting::BT_SCANNING); +} + std::vector<std::tuple<std::string, int32_t>> generateContextHubMapping() { std::vector<std::tuple<std::string, int32_t>> tuples; auto contextHubAidlNames = android::getAidlHalInstanceNames(IContextHub::descriptor); diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IAGnssRil.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IAGnssRil.aidl new file mode 100644 index 0000000000..73df1950bf --- /dev/null +++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IAGnssRil.aidl @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.gnss; +@VintfStability +interface IAGnssRil { + void setCallback(in android.hardware.gnss.IAGnssRilCallback callback); + void setRefLocation(in android.hardware.gnss.IAGnssRil.AGnssRefLocation agnssReflocation); + void setSetId(in android.hardware.gnss.IAGnssRil.SetIDType type, in @utf8InCpp String setid); + void updateNetworkState(in android.hardware.gnss.IAGnssRil.NetworkAttributes attributes); + const int NETWORK_CAPABILITY_NOT_METERED = 1; + const int NETWORK_CAPABILITY_NOT_ROAMING = 2; + @Backing(type="int") @VintfStability + enum AGnssRefLocationType { + GSM_CELLID = 1, + UMTS_CELLID = 2, + LTE_CELLID = 4, + NR_CELLID = 8, + } + @Backing(type="int") @VintfStability + enum SetIDType { + NONE = 0, + IMSI = 1, + MSISDM = 2, + } + @VintfStability + parcelable AGnssRefLocationCellID { + android.hardware.gnss.IAGnssRil.AGnssRefLocationType type; + int mcc; + int mnc; + int lac; + long cid; + int tac; + int pcid; + int arfcn; + } + @VintfStability + parcelable AGnssRefLocation { + android.hardware.gnss.IAGnssRil.AGnssRefLocationType type; + android.hardware.gnss.IAGnssRil.AGnssRefLocationCellID cellID; + } + @VintfStability + parcelable NetworkAttributes { + long networkHandle; + boolean isConnected; + int capabilities; + @utf8InCpp String apn; + } +} diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IAGnssRilCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IAGnssRilCallback.aidl new file mode 100644 index 0000000000..152b10aea0 --- /dev/null +++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IAGnssRilCallback.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// 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 <name>-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.gnss; +@VintfStability +interface IAGnssRilCallback { + void requestSetIdCb(in int setIdflag); + void requestRefLocCb(); +} diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl index fb13e026bb..a16d27ba2d 100644 --- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl +++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl @@ -44,6 +44,7 @@ interface IGnss { @nullable android.hardware.gnss.IGnssGeofence getExtensionGnssGeofence(); @nullable android.hardware.gnss.IGnssNavigationMessageInterface getExtensionGnssNavigationMessage(); android.hardware.gnss.IAGnss getExtensionAGnss(); + android.hardware.gnss.IAGnssRil getExtensionAGnssRil(); android.hardware.gnss.IGnssDebug getExtensionGnssDebug(); android.hardware.gnss.visibility_control.IGnssVisibilityControl getExtensionGnssVisibilityControl(); void start(); diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssBatching.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssBatching.aidl index 492edc3161..e1beed3b89 100644 --- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssBatching.aidl +++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssBatching.aidl @@ -36,9 +36,15 @@ package android.hardware.gnss; interface IGnssBatching { void init(in android.hardware.gnss.IGnssBatchingCallback callback); int getBatchSize(); - void start(in long periodNanos, in int flags); + void start(in android.hardware.gnss.IGnssBatching.Options options); void flush(); void stop(); void cleanup(); const int WAKEUP_ON_FIFO_FULL = 1; + @VintfStability + parcelable Options { + long periodNanos; + float minDistanceMeters; + int flags; + } } diff --git a/gnss/aidl/android/hardware/gnss/IAGnssRil.aidl b/gnss/aidl/android/hardware/gnss/IAGnssRil.aidl new file mode 100644 index 0000000000..c506b04968 --- /dev/null +++ b/gnss/aidl/android/hardware/gnss/IAGnssRil.aidl @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.gnss; + +import android.hardware.gnss.IAGnssRilCallback; +import android.hardware.gnss.IAGnssRilCallback.SetIDType; + +/** + * Extended interface for AGNSS RIL support. An Assisted GNSS Radio Interface + * Layer interface allows the GNSS chipset to request radio interface layer + * information from Android platform. Examples of such information are reference + * location, unique subscriber ID, phone number string and network availability changes. + */ +@VintfStability +interface IAGnssRil { + /** Network capability mode bitmask for not metered. */ + const int NETWORK_CAPABILITY_NOT_METERED = 0x01; + + /** Network capability mode bitmask for not roaming. */ + const int NETWORK_CAPABILITY_NOT_ROAMING = 0x02; + + /** AGNSS reference location type */ + @VintfStability + @Backing(type="int") + enum AGnssRefLocationType { + GSM_CELLID = 1, + UMTS_CELLID = 2, + LTE_CELLID = 4, + NR_CELLID = 8, + } + + /** SET ID type*/ + @VintfStability + @Backing(type="int") + enum SetIDType { + NONE = 0, + IMSI = 1, + MSISDM = 2, + } + + /** + * CellID for 2G, 3G ,LTE and NR used in AGNSS. This is defined in + * UserPlane Location Protocol (Version 2.0.4). + */ + @VintfStability + parcelable AGnssRefLocationCellID { + AGnssRefLocationType type; + + /** Mobile Country Code. */ + int mcc; + + /** Mobile Network Code .*/ + int mnc; + + /** + * Location Area Code in 2G, 3G and LTE. In 3G lac is discarded. In LTE, + * lac is populated with tac, to ensure that we don't break old clients that + * might rely on the old (wrong) behavior. + */ + int lac; + + /** + * Cell id in 2G. Utran Cell id in 3G. Cell Global Id EUTRA in LTE. + * Cell Global Id NR in 5G. + */ + long cid; + + /** Tracking Area Code in LTE and NR. */ + int tac; + + /** Physical Cell id in LTE and NR (not used in 2G and 3G) */ + int pcid; + + /** Absolute Radio Frequency Channel Number in NR. */ + int arfcn; + } + + /** Represents ref locations */ + @VintfStability + parcelable AGnssRefLocation { + AGnssRefLocationType type; + + AGnssRefLocationCellID cellID; + } + + /** Represents network connection status and capabilities. */ + @VintfStability + parcelable NetworkAttributes { + /** Network handle of the network for use with the NDK API. */ + long networkHandle; + + /** + * True indicates that network connectivity exists and it is possible to + * establish connections and pass data. If false, only the networkHandle field + * is populated to indicate that this network has just disconnected. + */ + boolean isConnected; + + /** + * A bitfield of flags indicating the capabilities of this network. The bit masks are + * defined in NETWORK_CAPABILITY_*. + */ + int capabilities; + + /** + * Telephony preferred Access Point Name to use for carrier data connection when + * connected to a cellular network. Empty string, otherwise. + */ + @utf8InCpp String apn; + } + + /** + * Opens the AGNSS interface and provides the callback routines + * to the implementation of this interface. + * + * @param callback Interface for AGnssRil callbacks. + * + */ + void setCallback(in IAGnssRilCallback callback); + + /** + * Sets the reference location. + * + * @param agnssReflocation AGNSS reference location CellID. + * + */ + void setRefLocation(in AGnssRefLocation agnssReflocation); + + /** + * Sets the SET ID. + * + * @param type Must be populated with either IMSI or MSISDN or NONE. + * @param setid If type is IMSI then setid is populated with + * a string representing the unique Subscriber ID, for example, the IMSI for + * a GMS phone. If type is MSISDN, then setid must contain + * the phone number string for line 1. For example, the MSISDN for a GSM phone. + * If the type is NONE, then the string must be empty. + * + */ + void setSetId(in SetIDType type, in @utf8InCpp String setid); + + /** + * Notifies GNSS of network status changes. + * + * The framework calls this method to update the GNSS HAL implementation of network + * state changes. + * + * @param attributes Updated network attributes. + * + */ + void updateNetworkState(in NetworkAttributes attributes); +} diff --git a/gnss/aidl/android/hardware/gnss/IAGnssRilCallback.aidl b/gnss/aidl/android/hardware/gnss/IAGnssRilCallback.aidl new file mode 100644 index 0000000000..6fb093e165 --- /dev/null +++ b/gnss/aidl/android/hardware/gnss/IAGnssRilCallback.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.gnss; + +/** + * Callback for IAGnssRil interface. Used to request SET ID and + * Reference Location. + */ +@VintfStability +interface IAGnssRilCallback { + /** + * The Hal uses this API to request a SET ID. + * + * @param setIdflag A bitfield of IAGnssRil.SetIDType that is required by + * the HAL. The framework will inject an empty SET ID if the flag is NONE. + * + */ + void requestSetIdCb(in int setIdflag); + + /** + * The Hal uses this API to request a reference location. + */ + void requestRefLocCb(); +} diff --git a/gnss/aidl/android/hardware/gnss/IGnss.aidl b/gnss/aidl/android/hardware/gnss/IGnss.aidl index 1e1c0fab96..99f2ee4b26 100644 --- a/gnss/aidl/android/hardware/gnss/IGnss.aidl +++ b/gnss/aidl/android/hardware/gnss/IGnss.aidl @@ -18,6 +18,7 @@ package android.hardware.gnss; import android.hardware.gnss.GnssLocation; import android.hardware.gnss.IAGnss; +import android.hardware.gnss.IAGnssRil; import android.hardware.gnss.IGnssAntennaInfo; import android.hardware.gnss.IGnssBatching; import android.hardware.gnss.IGnssCallback; @@ -188,6 +189,13 @@ interface IGnss { IAGnss getExtensionAGnss(); /** + * This method returns the IAGnssRil interface. + * + * @return The IAGnssRil interface. + */ + IAGnssRil getExtensionAGnssRil(); + + /** * This method returns the IGnssDebug interface. * * This method must return non-null. diff --git a/gnss/aidl/android/hardware/gnss/IGnssBatching.aidl b/gnss/aidl/android/hardware/gnss/IGnssBatching.aidl index 0d48ee1c29..0d03a0f009 100644 --- a/gnss/aidl/android/hardware/gnss/IGnssBatching.aidl +++ b/gnss/aidl/android/hardware/gnss/IGnssBatching.aidl @@ -46,6 +46,25 @@ interface IGnssBatching { */ const int WAKEUP_ON_FIFO_FULL = 0x01; + /** Options specifying the batching request. */ + @VintfStability + parcelable Options { + /** Time interval between samples in the location batch, in nanoseconds. */ + long periodNanos; + + /** + * The minimum distance in meters that the batching engine should + * accumulate before trying another GPS fix when in a challenging GPS environment. + * + * This is an optional field. If it is set as 0, the chipset can operate in an automatic + * mode. + */ + float minDistanceMeters; + + /** A bit field of Flags (WAKEUP_ON_FIFO_FULL) indicating the batching behavior. */ + int flags; + } + /** * Open the interface and provides the callback routines to the implementation of this * interface. @@ -83,10 +102,9 @@ interface IGnssBatching { * for using flushBatchedLocation to explicitly ask for the location as needed, to avoid it * being dropped. * - * @param periodNanos Time interval between samples in the location batch, in nanoseconds - * @param flags A bitfield of flags (WAKEUP_ON_FIFO_FULL) indicating the batching behavior + * @param options Options specifying the batching request. */ - void start(in long periodNanos, in int flags); + void start(in Options options); /** * Retrieve all batched locations currently stored. diff --git a/gnss/aidl/default/AGnssRil.cpp b/gnss/aidl/default/AGnssRil.cpp new file mode 100644 index 0000000000..afe0039105 --- /dev/null +++ b/gnss/aidl/default/AGnssRil.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "AGnssRilAidl" + +#include "AGnssRil.h" +#include <inttypes.h> +#include <log/log.h> + +namespace aidl::android::hardware::gnss { + +std::shared_ptr<IAGnssRilCallback> AGnssRil::sCallback = nullptr; + +ndk::ScopedAStatus AGnssRil::setCallback(const std::shared_ptr<IAGnssRilCallback>& callback) { + ALOGD("AGnssRil::setCallback"); + std::unique_lock<std::mutex> lock(mMutex); + sCallback = callback; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus AGnssRil::setRefLocation(const AGnssRefLocation& agnssReflocation) { + const AGnssRefLocationCellID& cellInfo = agnssReflocation.cellID; + ALOGD("AGnssRil::setRefLocation: type: %s, mcc: %d, mnc: %d, lac: %d, cid: %" PRId64 + ", tac: %d, pcid: " + "%d, arfcn: %d", + toString(agnssReflocation.type).c_str(), cellInfo.mcc, cellInfo.mnc, cellInfo.lac, + cellInfo.cid, cellInfo.tac, cellInfo.pcid, cellInfo.arfcn); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus AGnssRil::setSetId(SetIDType type, const std::string& setid) { + ALOGD("AGnssRil::setSetId: type:%s, setid: %s", toString(type).c_str(), setid.c_str()); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus AGnssRil::updateNetworkState(const NetworkAttributes& attributes) { + ALOGD("AGnssRil::updateNetworkState: networkHandle:%" PRId64 + ", isConnected: %d, capabilities: %d, " + "apn: %s", + attributes.networkHandle, attributes.isConnected, attributes.capabilities, + attributes.apn.c_str()); + return ndk::ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::gnss diff --git a/gnss/aidl/default/AGnssRil.h b/gnss/aidl/default/AGnssRil.h new file mode 100644 index 0000000000..7e429ee8f4 --- /dev/null +++ b/gnss/aidl/default/AGnssRil.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 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 <aidl/android/hardware/gnss/BnAGnssRil.h> + +namespace aidl::android::hardware::gnss { + +struct AGnssRil : public BnAGnssRil { + public: + ndk::ScopedAStatus setCallback(const std::shared_ptr<IAGnssRilCallback>& callback) override; + ndk::ScopedAStatus setRefLocation(const AGnssRefLocation& agnssReflocation) override; + ndk::ScopedAStatus setSetId(SetIDType type, const std::string& setid) override; + ndk::ScopedAStatus updateNetworkState(const NetworkAttributes& attributes) override; + + private: + // Synchronization lock for sCallback + mutable std::mutex mMutex; + // Guarded by mMutex + static std::shared_ptr<IAGnssRilCallback> sCallback; +}; + +} // namespace aidl::android::hardware::gnss diff --git a/gnss/aidl/default/Android.bp b/gnss/aidl/default/Android.bp index 3be7fb929e..4543665346 100644 --- a/gnss/aidl/default/Android.bp +++ b/gnss/aidl/default/Android.bp @@ -55,6 +55,7 @@ cc_binary { "android.hardware.gnss-V2-ndk", ], srcs: [ + "AGnssRil.cpp", "AGnss.cpp", "Gnss.cpp", "GnssAntennaInfo.cpp", diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp index c11a99ae9b..6331dfd8dc 100644 --- a/gnss/aidl/default/Gnss.cpp +++ b/gnss/aidl/default/Gnss.cpp @@ -20,6 +20,7 @@ #include <inttypes.h> #include <log/log.h> #include "AGnss.h" +#include "AGnssRil.h" #include "DeviceFileReader.h" #include "GnssAntennaInfo.h" #include "GnssBatching.h" @@ -171,7 +172,7 @@ ScopedAStatus Gnss::close() { return ScopedAStatus::ok(); } -ndk::ScopedAStatus Gnss::getExtensionAGnss(std::shared_ptr<IAGnss>* iAGnss) { +ScopedAStatus Gnss::getExtensionAGnss(std::shared_ptr<IAGnss>* iAGnss) { ALOGD("Gnss::getExtensionAGnss"); *iAGnss = SharedRefBase::make<AGnss>(); return ndk::ScopedAStatus::ok(); @@ -183,6 +184,12 @@ ScopedAStatus Gnss::injectTime(int64_t timeMs, int64_t timeReferenceMs, int unce return ScopedAStatus::ok(); } +ScopedAStatus Gnss::getExtensionAGnssRil(std::shared_ptr<IAGnssRil>* iAGnssRil) { + ALOGD("Gnss::getExtensionAGnssRil"); + *iAGnssRil = SharedRefBase::make<AGnssRil>(); + return ndk::ScopedAStatus::ok(); +} + ScopedAStatus Gnss::injectLocation(const GnssLocation& location) { ALOGD("injectLocation. lat:%lf, lng:%lf, acc:%f", location.latitudeDegrees, location.longitudeDegrees, location.horizontalAccuracyMeters); diff --git a/gnss/aidl/default/Gnss.h b/gnss/aidl/default/Gnss.h index 478dc94e4d..36874b897c 100644 --- a/gnss/aidl/default/Gnss.h +++ b/gnss/aidl/default/Gnss.h @@ -17,6 +17,7 @@ #pragma once #include <aidl/android/hardware/gnss/BnAGnss.h> +#include <aidl/android/hardware/gnss/BnAGnssRil.h> #include <aidl/android/hardware/gnss/BnGnss.h> #include <aidl/android/hardware/gnss/BnGnssAntennaInfo.h> #include <aidl/android/hardware/gnss/BnGnssBatching.h> @@ -67,6 +68,7 @@ class Gnss : public BnGnss { ndk::ScopedAStatus getExtensionGnssNavigationMessage( std::shared_ptr<IGnssNavigationMessageInterface>* iGnssNavigationMessage) override; ndk::ScopedAStatus getExtensionAGnss(std::shared_ptr<IAGnss>* iAGnss) override; + ndk::ScopedAStatus getExtensionAGnssRil(std::shared_ptr<IAGnssRil>* iAGnssRil) override; ndk::ScopedAStatus getExtensionGnssDebug(std::shared_ptr<IGnssDebug>* iGnssDebug) override; ndk::ScopedAStatus getExtensionGnssVisibilityControl( std::shared_ptr<android::hardware::gnss::visibility_control::IGnssVisibilityControl>* diff --git a/gnss/aidl/default/GnssBatching.cpp b/gnss/aidl/default/GnssBatching.cpp index b8be5e5baf..33e1fd500e 100644 --- a/gnss/aidl/default/GnssBatching.cpp +++ b/gnss/aidl/default/GnssBatching.cpp @@ -52,17 +52,19 @@ ndk::ScopedAStatus GnssBatching::getBatchSize(int* size) { return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus GnssBatching::start(int64_t periodNanos, int flags) { - ALOGD("start: periodNanos=%" PRId64 ", flags=%d", periodNanos, flags); +ndk::ScopedAStatus GnssBatching::start(const Options& options) { + ALOGD("start: periodNanos=%" PRId64 ", minDistanceMeters=%f, flags=%d", options.periodNanos, + options.minDistanceMeters, options.flags); if (mIsActive) { ALOGW("Gnss has started. Restarting..."); stop(); } - mWakeUpOnFifoFull = (flags & IGnssBatching::WAKEUP_ON_FIFO_FULL) ? true : false; // mMinIntervalMs is not smaller than 1 sec - periodNanos = (periodNanos < 1e9) ? 1e9 : periodNanos; + long periodNanos = (options.periodNanos < 1e9) ? 1e9 : options.periodNanos; mMinIntervalMs = periodNanos / 1e6; + mWakeUpOnFifoFull = (options.flags & IGnssBatching::WAKEUP_ON_FIFO_FULL) ? true : false; + mMinDistanceMeters = options.minDistanceMeters; mIsActive = true; mThread = std::thread([this]() { diff --git a/gnss/aidl/default/GnssBatching.h b/gnss/aidl/default/GnssBatching.h index 7cd6e85840..6d1d809230 100644 --- a/gnss/aidl/default/GnssBatching.h +++ b/gnss/aidl/default/GnssBatching.h @@ -28,7 +28,7 @@ struct GnssBatching : public BnGnssBatching { ~GnssBatching(); ndk::ScopedAStatus init(const std::shared_ptr<IGnssBatchingCallback>& callback) override; ndk::ScopedAStatus getBatchSize(int* size) override; - ndk::ScopedAStatus start(int64_t periodNanos, int flags) override; + ndk::ScopedAStatus start(const Options& options) override; ndk::ScopedAStatus flush() override; ndk::ScopedAStatus stop() override; ndk::ScopedAStatus cleanup() override; @@ -42,6 +42,7 @@ struct GnssBatching : public BnGnssBatching { std::thread mThread; std::atomic<bool> mIsActive; std::atomic<long> mMinIntervalMs; + std::atomic<float> mMinDistanceMeters; std::atomic<bool> mWakeUpOnFifoFull; // Synchronization lock for sCallback diff --git a/gnss/aidl/vts/AGnssRilCallbackAidl.cpp b/gnss/aidl/vts/AGnssRilCallbackAidl.cpp new file mode 100644 index 0000000000..4e4166d61d --- /dev/null +++ b/gnss/aidl/vts/AGnssRilCallbackAidl.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AGnssRilCallbackAidl.h" +#include <log/log.h> + +android::binder::Status AGnssRilCallbackAidl::requestSetIdCb(int setIdflag) { + ALOGI("requestSetIdCb setIdflag %d", setIdflag); + return android::binder::Status::ok(); +} + +android::binder::Status AGnssRilCallbackAidl::requestRefLocCb() { + ALOGI("requestRefLocCb"); + return android::binder::Status::ok(); +} diff --git a/gnss/aidl/vts/AGnssRilCallbackAidl.h b/gnss/aidl/vts/AGnssRilCallbackAidl.h new file mode 100644 index 0000000000..74b34eee94 --- /dev/null +++ b/gnss/aidl/vts/AGnssRilCallbackAidl.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/hardware/gnss/BnAGnssRilCallback.h> + +/** Implementation for IAGnssRilCallback. */ +class AGnssRilCallbackAidl : public android::hardware::gnss::BnAGnssRilCallback { + public: + AGnssRilCallbackAidl(){}; + ~AGnssRilCallbackAidl(){}; + android::binder::Status requestSetIdCb(int setIdflag) override; + android::binder::Status requestRefLocCb() override; +}; diff --git a/gnss/aidl/vts/Android.bp b/gnss/aidl/vts/Android.bp index 4244ab354d..f02a41e248 100644 --- a/gnss/aidl/vts/Android.bp +++ b/gnss/aidl/vts/Android.bp @@ -31,6 +31,7 @@ cc_test { "gnss_hal_test.cpp", "gnss_hal_test_cases.cpp", "AGnssCallbackAidl.cpp", + "AGnssRilCallbackAidl.cpp", "GnssAntennaInfoCallbackAidl.cpp", "GnssBatchingCallback.cpp", "GnssCallbackAidl.cpp", diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp index 6e363f9815..1fa68253c7 100644 --- a/gnss/aidl/vts/gnss_hal_test_cases.cpp +++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp @@ -29,6 +29,7 @@ #include <android/hardware/gnss/visibility_control/IGnssVisibilityControl.h> #include <cutils/properties.h> #include "AGnssCallbackAidl.h" +#include "AGnssRilCallbackAidl.h" #include "GnssAntennaInfoCallbackAidl.h" #include "GnssBatchingCallback.h" #include "GnssGeofenceCallback.h" @@ -48,6 +49,7 @@ using android::hardware::gnss::GnssData; using android::hardware::gnss::GnssMeasurement; using android::hardware::gnss::GnssPowerStats; using android::hardware::gnss::IAGnss; +using android::hardware::gnss::IAGnssRil; using android::hardware::gnss::IGnss; using android::hardware::gnss::IGnssAntennaInfo; using android::hardware::gnss::IGnssAntennaInfoCallback; @@ -811,6 +813,10 @@ TEST_P(GnssHalTest, BlocklistConstellationLocationOn) { * TestAllExtensions. */ TEST_P(GnssHalTest, TestAllExtensions) { + if (aidl_gnss_hal_->getInterfaceVersion() == 1) { + return; + } + sp<IGnssBatching> iGnssBatching; auto status = aidl_gnss_hal_->getExtensionGnssBatching(&iGnssBatching); if (status.isOk() && iGnssBatching != nullptr) { @@ -867,6 +873,42 @@ TEST_P(GnssHalTest, TestAGnssExtension) { } /* + * TestAGnssRilExtension: + * 1. Gets the IAGnssRil extension. + * 2. Sets AGnssRilCallback. + * 3. Sets reference location. + */ +TEST_P(GnssHalTest, TestAGnssRilExtension) { + if (aidl_gnss_hal_->getInterfaceVersion() == 1) { + return; + } + sp<IAGnssRil> iAGnssRil; + auto status = aidl_gnss_hal_->getExtensionAGnssRil(&iAGnssRil); + ASSERT_TRUE(status.isOk()); + ASSERT_TRUE(iAGnssRil != nullptr); + + auto agnssRilCallback = sp<AGnssRilCallbackAidl>::make(); + status = iAGnssRil->setCallback(agnssRilCallback); + ASSERT_TRUE(status.isOk()); + + // Set RefLocation + IAGnssRil::AGnssRefLocationCellID agnssReflocationCellId; + agnssReflocationCellId.type = IAGnssRil::AGnssRefLocationType::LTE_CELLID; + agnssReflocationCellId.mcc = 466; + agnssReflocationCellId.mnc = 97; + agnssReflocationCellId.lac = 46697; + agnssReflocationCellId.cid = 59168142; + agnssReflocationCellId.pcid = 420; + agnssReflocationCellId.tac = 11460; + IAGnssRil::AGnssRefLocation agnssReflocation; + agnssReflocation.type = IAGnssRil::AGnssRefLocationType::LTE_CELLID; + agnssReflocation.cellID = agnssReflocationCellId; + + status = iAGnssRil->setRefLocation(agnssReflocation); + ASSERT_TRUE(status.isOk()); +} + +/* * GnssDebugValuesSanityTest: * Ensures that GnssDebug values make sense. */ diff --git a/graphics/allocator/aidl/Android.bp b/graphics/allocator/aidl/Android.bp new file mode 100644 index 0000000000..ea8a599578 --- /dev/null +++ b/graphics/allocator/aidl/Android.bp @@ -0,0 +1,40 @@ +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"], +} + +aidl_interface { + name: "android.hardware.graphics.allocator", + vendor_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, + srcs: ["android/hardware/graphics/allocator/*.aidl"], + imports: [ + "android.hardware.common-V2", + ], + stability: "vintf", + backend: { + cpp: { + enabled: false, + }, + java: { + enabled: false, + }, + ndk: { + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], + vndk: { + enabled: true, + }, + min_sdk_version: "29", + }, + }, +} diff --git a/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationError.aidl b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationError.aidl new file mode 100644 index 0000000000..6e7b739ed5 --- /dev/null +++ b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationError.aidl @@ -0,0 +1,40 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS 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 <name>-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.graphics.allocator; +@Backing(type="int") @VintfStability +enum AllocationError { + BAD_DESCRIPTOR = 0, + NO_RESOURCES = 1, + UNSUPPORTED = 2, +} diff --git a/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationResult.aidl b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationResult.aidl new file mode 100644 index 0000000000..73cfeb5422 --- /dev/null +++ b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationResult.aidl @@ -0,0 +1,39 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS 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 <name>-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.graphics.allocator; +@VintfStability +parcelable AllocationResult { + int stride; + android.hardware.common.NativeHandle[] buffers; +} diff --git a/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/IAllocator.aidl b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/IAllocator.aidl new file mode 100644 index 0000000000..fe0b0a214b --- /dev/null +++ b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/IAllocator.aidl @@ -0,0 +1,38 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS 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 <name>-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.graphics.allocator; +@VintfStability +interface IAllocator { + android.hardware.graphics.allocator.AllocationResult allocate(in byte[] descriptor, in int count); +} diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationError.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationError.aidl new file mode 100644 index 0000000000..c6b77b9f7f --- /dev/null +++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationError.aidl @@ -0,0 +1,36 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.graphics.allocator; + +@VintfStability +@Backing(type="int") +enum AllocationError { + /** + * Invalid BufferDescriptor. + */ + BAD_DESCRIPTOR, + + /** + * Resource unavailable. + */ + NO_RESOURCES, + + /** + * Permanent failure. + */ + UNSUPPORTED +}
\ No newline at end of file diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl new file mode 100644 index 0000000000..0774e2554c --- /dev/null +++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl @@ -0,0 +1,30 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.graphics.allocator; + +import android.hardware.common.NativeHandle; + + /** + * Result of an IAllocator::allocate call. + * + * @sa +ndk libnativewindow#AHardwareBuffer_Desc + */ +@VintfStability +parcelable AllocationResult { + int stride; + NativeHandle[] buffers; +}
\ No newline at end of file diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/IAllocator.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/IAllocator.aidl new file mode 100644 index 0000000000..8c3ca9601e --- /dev/null +++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/IAllocator.aidl @@ -0,0 +1,36 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.graphics.allocator; + +import android.hardware.graphics.allocator.AllocationResult; + +@VintfStability +interface IAllocator { + /** + * Allocates buffers with the properties specified by the descriptor. + * + * Allocations should be optimized for usage bits provided in the + * descriptor. + * + * @param descriptor Properties of the buffers to allocate. This must be + * obtained from IMapper::createDescriptor(). + * @param count The number of buffers to allocate. + * @return An AllocationResult containing the result of an error, or + * an AllocationError status + */ + AllocationResult allocate(in byte[] descriptor, in int count); +} diff --git a/graphics/common/OWNERS b/graphics/common/OWNERS new file mode 100644 index 0000000000..94999ea409 --- /dev/null +++ b/graphics/common/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 1075130 +adyabr@google.com +alecmouri@google.com +jreck@google.com +scroggo@google.com diff --git a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/BufferUsage.aidl b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/BufferUsage.aidl index b4ef4515c7..e1edb17793 100644 --- a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/BufferUsage.aidl +++ b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/BufferUsage.aidl @@ -54,10 +54,11 @@ enum BufferUsage { RENDERSCRIPT = 1048576, VIDEO_DECODER = 4194304, SENSOR_DIRECT_DATA = 8388608, + GPU_DATA_BUFFER = 16777216, GPU_CUBE_MAP = 33554432, GPU_MIPMAP_COMPLETE = 67108864, HW_IMAGE_ENCODER = 134217728, - GPU_DATA_BUFFER = 16777216, + FRONT_BUFFER = 4294967296, VENDOR_MASK = -268435456, VENDOR_MASK_HI = -281474976710656, } diff --git a/graphics/common/aidl/android/hardware/graphics/common/BufferUsage.aidl b/graphics/common/aidl/android/hardware/graphics/common/BufferUsage.aidl index d978f46345..4b5a306881 100644 --- a/graphics/common/aidl/android/hardware/graphics/common/BufferUsage.aidl +++ b/graphics/common/aidl/android/hardware/graphics/common/BufferUsage.aidl @@ -87,6 +87,12 @@ enum BufferUsage { /** buffer is used as a sensor direct report output */ SENSOR_DIRECT_DATA = 1 << 23, + /** + * buffer is used as as an OpenGL shader storage or uniform + * buffer object + */ + GPU_DATA_BUFFER = 1 << 24, + /** buffer is used as a cube map texture */ GPU_CUBE_MAP = 1 << 25, @@ -98,17 +104,17 @@ enum BufferUsage { */ HW_IMAGE_ENCODER = 1 << 27, + /* Bits 28-31 are reserved for vendor usage */ + /** - * buffer is used as as an OpenGL shader storage or uniform - * buffer object - */ - GPU_DATA_BUFFER = 1 << 24, + * Buffer is used for front-buffer rendering + */ + FRONT_BUFFER = 1L << 32, - /** bits 25-27 must be zero and are reserved for future versions */ /** bits 28-31 are reserved for vendor extensions */ VENDOR_MASK = 0xf << 28, - /** bits 32-47 must be zero and are reserved for future versions */ + /** bits 33-47 must be zero and are reserved for future versions */ /** bits 48-63 are reserved for vendor extensions */ VENDOR_MASK_HI = (1L * 0xffff) << 48, } diff --git a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl index 7719d6e61d..74a9ce309b 100644 --- a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl +++ b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl @@ -22,8 +22,9 @@ package android.hardware.graphics.common; * This is an enum that defines the common types of gralloc 4 buffer metadata. The comments for * each enum include a description of the metadata that is associated with the type. * - * IMapper@4.x must support getting the following standard buffer metadata types. IMapper@4.x may - * support setting these standard buffer metadata types as well. + * IMapper@4.x must support getting the following standard buffer metadata types, with the exception + * of SMPTE 2094-10 metadata. IMapper@4.x may support setting these standard buffer metadata types + * as well. * * When encoding these StandardMetadataTypes into a byte stream, the associated MetadataType is * is first encoded followed by the StandardMetadataType value. The MetadataType is encoded by diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp index b071f71e85..fa294ff259 100644 --- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp +++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp @@ -161,7 +161,8 @@ class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> { return mGralloc->allocate( width, height, /*layerCount*/ 1, static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888), - static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN)); + static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN | + BufferUsage::COMPOSER_OVERLAY)); } struct TestParameters { diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/Android.bp b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/Android.bp index 741572db57..bd2c3b1248 100644 --- a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/Android.bp +++ b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/Android.bp @@ -50,6 +50,7 @@ cc_test { "libgui", "libhidlbase", "libprocessgroup", + "libtinyxml2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", "android.hardware.graphics.mapper@3.0", diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/VtsHalGraphicsComposer3_ReadbackTest.cpp index e519221159..3f1e703031 100644 --- a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/VtsHalGraphicsComposer3_ReadbackTest.cpp +++ b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/VtsHalGraphicsComposer3_ReadbackTest.cpp @@ -24,10 +24,18 @@ #include <composer-vts/include/ReadbackVts.h> #include <composer-vts/include/RenderEngineVts.h> #include <gtest/gtest.h> +#include <ui/DisplayId.h> +#include <ui/DisplayIdentification.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicBufferAllocator.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> + +// tinyxml2 does implicit conversions >:( +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#include <tinyxml2.h> +#pragma clang diagnostic pop #include "composer-vts/include/GraphicsComposerCallback.h" namespace aidl::android::hardware::graphics::composer3::vts { @@ -124,6 +132,76 @@ class GraphicsCompositionTestBase : public ::testing::Test { /*layerCount*/ 1u, usage, "VtsHalGraphicsComposer3_ReadbackTest"); } + uint64_t getStableDisplayId(int64_t display) { + DisplayIdentification identification; + const auto error = mComposerClient->getDisplayIdentificationData(display, &identification); + EXPECT_TRUE(error.isOk()); + + if (const auto info = ::android::parseDisplayIdentificationData( + static_cast<uint8_t>(identification.port), identification.data)) { + return info->id.value; + } + + return ::android::PhysicalDisplayId::fromPort(static_cast<uint8_t>(identification.port)) + .value; + } + + // Gets the per-display XML config + std::unique_ptr<tinyxml2::XMLDocument> getDisplayConfigXml(int64_t display) { + std::stringstream pathBuilder; + pathBuilder << "/vendor/etc/displayconfig/display_id_" << getStableDisplayId(display) + << ".xml"; + const std::string path = pathBuilder.str(); + auto document = std::make_unique<tinyxml2::XMLDocument>(); + const tinyxml2::XMLError error = document->LoadFile(path.c_str()); + if (error == tinyxml2::XML_SUCCESS) { + return document; + } else { + return nullptr; + } + } + + // Gets the max display brightness for this display. + // If the display config xml does not exist, then assume that the display is not well-configured + // enough to provide a display brightness, so return nullopt. + std::optional<float> getMaxDisplayBrightnessNits(int64_t display) { + const auto document = getDisplayConfigXml(display); + if (!document) { + // Assume the device doesn't support display brightness + return std::nullopt; + } + + const auto root = document->RootElement(); + if (!root) { + // If there's somehow no root element, then this isn't a valid config + return std::nullopt; + } + + const auto screenBrightnessMap = root->FirstChildElement("screenBrightnessMap"); + if (!screenBrightnessMap) { + // A valid display config must have a screen brightness map + return std::nullopt; + } + + auto point = screenBrightnessMap->FirstChildElement("point"); + float maxNits = -1.f; + while (point != nullptr) { + const auto nits = point->FirstChildElement("nits"); + if (nits) { + maxNits = std::max(maxNits, nits->FloatText(-1.f)); + } + point = point->NextSiblingElement("point"); + } + + if (maxNits < 0.f) { + // If we got here, then there were no point elements containing a nit value, so this + // config isn't valid + return std::nullopt; + } + + return maxNits; + } + void writeLayers(const std::vector<std::shared_ptr<TestLayer>>& layers) { for (auto layer : layers) { layer->write(mWriter); @@ -250,8 +328,8 @@ TEST_P(GraphicsCompositionTest, SingleSolidColorLayer) { std::vector<Color> expectedColors(static_cast<size_t>(mDisplayWidth * mDisplayHeight)); ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, coloredSquare, BLUE); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); writeLayers(layers); @@ -286,8 +364,8 @@ TEST_P(GraphicsCompositionTest, SetLayerBuffer) { return; } - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); std::vector<Color> expectedColors(static_cast<size_t>(mDisplayWidth * mDisplayHeight)); ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, @@ -363,8 +441,8 @@ TEST_P(GraphicsCompositionTest, SetLayerBufferNoEffect) { std::vector<Color> expectedColors(static_cast<size_t>(mDisplayWidth * mDisplayHeight)); ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, coloredSquare, BLUE); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); mWriter.validateDisplay(mPrimaryDisplay, ComposerClientWriter::kNoTimestamp); @@ -389,8 +467,8 @@ TEST_P(GraphicsCompositionTest, SetReadbackBuffer) { return; } - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, mDisplayHeight, + mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); } @@ -419,8 +497,13 @@ TEST_P(GraphicsCompositionTest, SetReadbackBufferBadParameter) { return; } - aidl::android::hardware::common::NativeHandle bufferHandle = - ::android::dupToAidl(mGraphicBuffer->handle); + aidl::android::hardware::common::NativeHandle bufferHandle; + { + ::android::sp<::android::GraphicBuffer> buffer = allocate(); + ASSERT_EQ(::android::OK, mGraphicBuffer->initCheck()); + ::android::makeToAidl(mGraphicBuffer->handle); + } + ndk::ScopedFileDescriptor releaseFence = ndk::ScopedFileDescriptor(-1); const auto error = mComposerClient->setReadbackBuffer(mPrimaryDisplay, bufferHandle, releaseFence); @@ -438,8 +521,9 @@ TEST_P(GraphicsCompositionTest, GetReadbackBufferFenceInactive) { ndk::ScopedFileDescriptor releaseFence; const auto error = mComposerClient->getReadbackBufferFence(mPrimaryDisplay, &releaseFence); - EXPECT_TRUE(error.isOk()); + ASSERT_FALSE(error.isOk()); EXPECT_EQ(IComposerClient::EX_UNSUPPORTED, error.getServiceSpecificError()); + EXPECT_EQ(-1, releaseFence.get()); } TEST_P(GraphicsCompositionTest, ClientComposition) { @@ -474,8 +558,8 @@ TEST_P(GraphicsCompositionTest, ClientComposition) { std::vector<std::shared_ptr<TestLayer>> layers = {layer}; - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); writeLayers(layers); ASSERT_TRUE(mReader.takeErrors().empty()); @@ -553,8 +637,8 @@ TEST_P(GraphicsCompositionTest, DeviceAndClientComposition) { ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, RED); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); auto deviceLayer = std::make_shared<TestBufferLayer>( @@ -657,8 +741,8 @@ TEST_P(GraphicsCompositionTest, SetLayerDamage) { std::vector<std::shared_ptr<TestLayer>> layers = {layer}; - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); writeLayers(layers); @@ -720,8 +804,8 @@ TEST_P(GraphicsCompositionTest, SetLayerPlaneAlpha) { std::vector<std::shared_ptr<TestLayer>> layers = {layer}; - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); @@ -781,8 +865,8 @@ TEST_P(GraphicsCompositionTest, SetLayerSourceCrop) { // update expected colors to match crop ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, {0, 0, mDisplayWidth, mDisplayHeight}, BLUE); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); writeLayers(layers); ASSERT_TRUE(mReader.takeErrors().empty()); @@ -834,8 +918,8 @@ TEST_P(GraphicsCompositionTest, SetLayerZOrder) { ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, blueRect, BLUE); ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); writeLayers(layers); @@ -876,6 +960,90 @@ TEST_P(GraphicsCompositionTest, SetLayerZOrder) { } } +TEST_P(GraphicsCompositionTest, SetLayerWhitePointDims) { + std::vector<DisplayCapability> capabilities; + const auto error = mComposerClient->getDisplayCapabilities(mPrimaryDisplay, &capabilities); + ASSERT_TRUE(error.isOk()); + + const bool brightnessSupport = std::find(capabilities.begin(), capabilities.end(), + DisplayCapability::BRIGHTNESS) != capabilities.end(); + + if (!brightnessSupport) { + GTEST_SUCCEED() << "Cannot verify dimming behavior without brightness support"; + return; + } + + const std::optional<float> maxBrightnessNitsOptional = + getMaxDisplayBrightnessNits(mPrimaryDisplay); + + ASSERT_TRUE(maxBrightnessNitsOptional.has_value()); + + const float maxBrightnessNits = *maxBrightnessNitsOptional; + + // Preconditions to successfully run are knowing the max brightness and successfully applying + // the max brightness + ASSERT_GT(maxBrightnessNits, 0.f); + mWriter.setDisplayBrightness(mPrimaryDisplay, 1.f); + execute(); + ASSERT_TRUE(mReader.takeErrors().empty()); + + for (ColorMode mode : mTestColorModes) { + ASSERT_NO_FATAL_FAILURE( + mComposerClient->setColorMode(mPrimaryDisplay, mode, RenderIntent::COLORIMETRIC)); + + if (!getHasReadbackBuffer()) { + GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace for " + "color mode: " + << toString(mode); + continue; + } + const common::Rect redRect = {0, 0, mDisplayWidth, mDisplayHeight / 2}; + const common::Rect dimmerRedRect = {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}; + const auto redLayer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); + redLayer->setColor(RED); + redLayer->setDisplayFrame(redRect); + redLayer->setWhitePointNits(maxBrightnessNits); + + const auto dimmerRedLayer = + std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); + dimmerRedLayer->setColor(RED); + dimmerRedLayer->setDisplayFrame(dimmerRedRect); + // Intentionally use a small dimming ratio as some implementations may be more likely to + // kick into GPU composition to apply dithering when the dimming ratio is high. + static constexpr float kDimmingRatio = 0.9f; + dimmerRedLayer->setWhitePointNits(maxBrightnessNits * kDimmingRatio); + + const std::vector<std::shared_ptr<TestLayer>> layers = {redLayer, dimmerRedLayer}; + std::vector<Color> expectedColors(static_cast<size_t>(mDisplayWidth * mDisplayHeight)); + + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, dimmerRedRect, DIM_RED); + + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + + writeLayers(layers); + ASSERT_TRUE(mReader.takeErrors().empty()); + mWriter.validateDisplay(mPrimaryDisplay, ComposerClientWriter::kNoTimestamp); + execute(); + if (!mReader.takeChangedCompositionTypes(mPrimaryDisplay).empty()) { + GTEST_SUCCEED() + << "Readback verification not supported for GPU composition for color mode: " + << toString(mode); + continue; + } + mWriter.presentDisplay(mPrimaryDisplay); + execute(); + ASSERT_TRUE(mReader.takeErrors().empty()); + + ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); + mTestRenderEngine->setRenderLayers(layers); + ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers()); + ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors)); + } +} + class GraphicsBlendModeCompositionTest : public GraphicsCompositionTestBase, public testing::WithParamInterface<std::tuple<std::string, std::string>> { @@ -973,8 +1141,8 @@ TEST_P(GraphicsBlendModeCompositionTest, None) { setUpLayers(BlendMode::NONE); setExpectedColors(expectedColors); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); writeLayers(mLayers); ASSERT_TRUE(mReader.takeErrors().empty()); @@ -1014,8 +1182,8 @@ TEST_P(GraphicsBlendModeCompositionTest, Coverage) { setUpLayers(BlendMode::COVERAGE); setExpectedColors(expectedColors); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); writeLayers(mLayers); ASSERT_TRUE(mReader.takeErrors().empty()); @@ -1050,8 +1218,8 @@ TEST_P(GraphicsBlendModeCompositionTest, Premultiplied) { setUpLayers(BlendMode::PREMULTIPLIED); setExpectedColors(expectedColors); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); writeLayers(mLayers); ASSERT_TRUE(mReader.takeErrors().empty()); @@ -1120,8 +1288,8 @@ TEST_P(GraphicsTransformCompositionTest, FLIP_H) { GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; return; } - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); mLayer->setTransform(Transform::FLIP_H); mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); @@ -1161,8 +1329,8 @@ TEST_P(GraphicsTransformCompositionTest, FLIP_V) { GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; return; } - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); mLayer->setTransform(Transform::FLIP_V); @@ -1202,8 +1370,8 @@ TEST_P(GraphicsTransformCompositionTest, ROT_180) { GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; return; } - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer, - mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); mLayer->setTransform(Transform::ROT_180); diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/VtsHalGraphicsComposer3_TargetTest.cpp index 1cfd3f95bb..0a12f1a592 100644 --- a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/VtsHalGraphicsComposer3_TargetTest.cpp +++ b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/VtsHalGraphicsComposer3_TargetTest.cpp @@ -76,7 +76,10 @@ class GraphicsComposerAidlTest : public ::testing::TestWithParam<std::string> { ASSERT_NE(binder, nullptr); ASSERT_NO_FATAL_FAILURE(mComposer = IComposer::fromBinder(binder)); ASSERT_NE(mComposer, nullptr); - ASSERT_NO_FATAL_FAILURE(mComposer->createClient(&mComposerClient)); + + ndk::ScopedAStatus status; + ASSERT_NO_FATAL_FAILURE(status = mComposer->createClient(&mComposerClient)); + ASSERT_TRUE(status.isOk()); mComposerCallback = ::ndk::SharedRefBase::make<GraphicsComposerCallback>(); EXPECT_TRUE(mComposerClient->registerCallback(mComposerCallback).isOk()); @@ -188,6 +191,14 @@ class GraphicsComposerAidlTest : public ::testing::TestWithParam<std::string> { resourceIt->second.layers.erase(layer); } + bool hasCapability(Capability capability) { + std::vector<Capability> capabilities; + EXPECT_TRUE(mComposer->getCapabilities(&capabilities).isOk()); + return std::any_of( + capabilities.begin(), capabilities.end(), + [&](const Capability& activeCapability) { return activeCapability == capability; }); + } + // returns an invalid display id (one that has not been registered to a // display. Currently assuming that a device will never have close to // std::numeric_limit<uint64_t>::max() displays registered while running tests @@ -1474,6 +1485,11 @@ class GraphicsComposerAidlCommandTest : public GraphicsComposerAidlTest { } void Test_expectedPresentTime(std::optional<int> framesDelay) { + if (hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) { + GTEST_SUCCEED() << "Device has unreliable present fences capability, skipping"; + return; + } + ASSERT_TRUE(mComposerClient->setPowerMode(mPrimaryDisplay, PowerMode::ON).isOk()); const auto vsyncPeriod = getVsyncPeriod(); @@ -1538,7 +1554,7 @@ TEST_P(GraphicsComposerAidlCommandTest, SetLayerColorTransform) { execute(); const auto errors = mReader.takeErrors(); - if (errors.size() == 1 && errors[0].errorCode == EX_UNSUPPORTED_OPERATION) { + if (errors.size() == 1 && errors[0].errorCode == IComposerClient::EX_UNSUPPORTED) { GTEST_SUCCEED() << "setLayerColorTransform is not supported"; return; } @@ -1555,7 +1571,7 @@ TEST_P(GraphicsComposerAidlCommandTest, SetDisplayBrightness) { execute(); const auto errors = mReader.takeErrors(); EXPECT_EQ(1, errors.size()); - EXPECT_EQ(EX_UNSUPPORTED_OPERATION, errors[0].errorCode); + EXPECT_EQ(IComposerClient::EX_UNSUPPORTED, errors[0].errorCode); GTEST_SUCCEED() << "SetDisplayBrightness is not supported"; return; } @@ -1650,10 +1666,7 @@ TEST_P(GraphicsComposerAidlCommandTest, PRESENT_DISPLAY) { */ // TODO(b/208441745) fix the test failure TEST_P(GraphicsComposerAidlCommandTest, PRESENT_DISPLAY_NO_LAYER_STATE_CHANGES) { - std::vector<Capability> capabilities; - EXPECT_TRUE(mComposer->getCapabilities(&capabilities).isOk()); - if (none_of(capabilities.begin(), capabilities.end(), - [&](auto item) { return item == Capability::SKIP_VALIDATE; })) { + if (!hasCapability(Capability::SKIP_VALIDATE)) { GTEST_SUCCEED() << "Device does not have skip validate capability, skipping"; return; } @@ -1881,10 +1894,7 @@ TEST_P(GraphicsComposerAidlCommandTest, SET_LAYER_PLANE_ALPHA) { } TEST_P(GraphicsComposerAidlCommandTest, SET_LAYER_SIDEBAND_STREAM) { - std::vector<Capability> capabilities; - EXPECT_TRUE(mComposer->getCapabilities(&capabilities).isOk()); - if (none_of(capabilities.begin(), capabilities.end(), - [&](auto& item) { return item == Capability::SIDEBAND_STREAM; })) { + if (!hasCapability(Capability::SIDEBAND_STREAM)) { GTEST_SUCCEED() << "no sideband stream support"; return; } @@ -2024,6 +2034,27 @@ TEST_P(GraphicsComposerAidlCommandTest, SET_LAYER_PER_FRAME_METADATA) { EXPECT_TRUE(mComposerClient->destroyLayer(mPrimaryDisplay, layer).isOk()); } +TEST_P(GraphicsComposerAidlCommandTest, setLayerWhitePointNits) { + int64_t layer; + EXPECT_TRUE(mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount, &layer).isOk()); + + mWriter.setLayerWhitePointNits(mPrimaryDisplay, layer, 200.f); + execute(); + ASSERT_TRUE(mReader.takeErrors().empty()); + + mWriter.setLayerWhitePointNits(mPrimaryDisplay, layer, 1000.f); + execute(); + ASSERT_TRUE(mReader.takeErrors().empty()); + + mWriter.setLayerWhitePointNits(mPrimaryDisplay, layer, 0.f); + execute(); + ASSERT_TRUE(mReader.takeErrors().empty()); + + mWriter.setLayerWhitePointNits(mPrimaryDisplay, layer, -1.f); + execute(); + ASSERT_TRUE(mReader.takeErrors().empty()); +} + TEST_P(GraphicsComposerAidlCommandTest, setActiveConfigWithConstraints) { Test_setActiveConfigWithConstraints({.delayForChange = 0, .refreshMiss = false}); } diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/ReadbackVts.cpp b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/ReadbackVts.cpp index ee597a1b1c..587c523d19 100644 --- a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/ReadbackVts.cpp +++ b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/ReadbackVts.cpp @@ -41,6 +41,7 @@ void TestLayer::write(ComposerClientWriter& writer) { writer.setLayerTransform(mDisplay, mLayer, mTransform); writer.setLayerPlaneAlpha(mDisplay, mLayer, mAlpha); writer.setLayerBlendMode(mDisplay, mLayer, mBlendMode); + writer.setLayerWhitePointNits(mDisplay, mLayer, mWhitePointNits); } std::string ReadbackHelper::getColorModeString(ColorMode mode) { @@ -103,6 +104,7 @@ LayerSettings TestLayer::toRenderEngineLayerSettings() { 1.0f, 1.0f)); layerSettings.geometry.positionTransform = scale * translation; + layerSettings.whitePointNits = mWhitePointNits; return layerSettings; } @@ -175,18 +177,17 @@ bool ReadbackHelper::readbackSupported(const common::PixelFormat& pixelFormat, return true; } -void ReadbackHelper::compareColorBuffers(std::vector<Color>& expectedColors, void* bufferData, - const int32_t stride, const uint32_t width, +void ReadbackHelper::compareColorBuffers(const std::vector<Color>& expectedColors, void* bufferData, + const uint32_t stride, const uint32_t width, const uint32_t height, common::PixelFormat pixelFormat) { const int32_t bytesPerPixel = ReadbackHelper::GetBytesPerPixel(pixelFormat); ASSERT_NE(-1, bytesPerPixel); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { auto pixel = row * static_cast<int32_t>(width) + col; - int offset = (row * stride + col) * bytesPerPixel; + int offset = (row * static_cast<int32_t>(stride) + col) * bytesPerPixel; uint8_t* pixelColor = (uint8_t*)bufferData + offset; const Color expectedColor = expectedColors[static_cast<size_t>(pixel)]; - ASSERT_EQ(std::round(255.0f * expectedColor.r), pixelColor[0]); ASSERT_EQ(std::round(255.0f * expectedColor.g), pixelColor[1]); ASSERT_EQ(std::round(255.0f * expectedColor.b), pixelColor[2]); @@ -195,13 +196,11 @@ void ReadbackHelper::compareColorBuffers(std::vector<Color>& expectedColors, voi } ReadbackBuffer::ReadbackBuffer(int64_t display, const std::shared_ptr<IComposerClient>& client, - const ::android::sp<::android::GraphicBuffer>& graphicBuffer, int32_t width, int32_t height, common::PixelFormat pixelFormat, common::Dataspace dataspace) { mDisplay = display; mComposerClient = client; - mGraphicBuffer = graphicBuffer; mPixelFormat = pixelFormat; mDataspace = dataspace; @@ -235,19 +234,24 @@ void ReadbackBuffer::setReadbackBuffer() { } void ReadbackBuffer::checkReadbackBuffer(std::vector<Color> expectedColors) { + ASSERT_NE(nullptr, mGraphicBuffer); // lock buffer for reading ndk::ScopedFileDescriptor fenceHandle; EXPECT_TRUE(mComposerClient->getReadbackBufferFence(mDisplay, &fenceHandle).isOk()); - int outBytesPerPixel; - int outBytesPerStride; + int bytesPerPixel = -1; + int bytesPerStride = -1; void* bufData = nullptr; - auto status = mGraphicBuffer->lockAsync(mUsage, mAccessRegion, &bufData, fenceHandle.get(), - &outBytesPerPixel, &outBytesPerStride); + + auto status = mGraphicBuffer->lockAsync(mUsage, mAccessRegion, &bufData, dup(fenceHandle.get()), + &bytesPerPixel, &bytesPerStride); EXPECT_EQ(::android::OK, status); ASSERT_TRUE(mPixelFormat == PixelFormat::RGB_888 || mPixelFormat == PixelFormat::RGBA_8888); - ReadbackHelper::compareColorBuffers(expectedColors, bufData, static_cast<int32_t>(mStride), - mWidth, mHeight, mPixelFormat); + const uint32_t stride = (bytesPerPixel > 0 && bytesPerStride > 0) + ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel) + : mGraphicBuffer->getStride(); + ReadbackHelper::compareColorBuffers(expectedColors, bufData, stride, mWidth, mHeight, + mPixelFormat); status = mGraphicBuffer->unlock(); EXPECT_EQ(::android::OK, status); } @@ -303,10 +307,7 @@ LayerSettings TestBufferLayer::toRenderEngineLayerSettings() { LayerSettings layerSettings = TestLayer::toRenderEngineLayerSettings(); layerSettings.source.buffer.buffer = std::make_shared<::android::renderengine::impl::ExternalTexture>( - ::android::sp<::android::GraphicBuffer>::make( - mGraphicBuffer->handle, ::android::GraphicBuffer::CLONE_HANDLE, mWidth, - mHeight, static_cast<int32_t>(mPixelFormat), 1, mUsage, mStride), - mRenderEngine.getInternalRenderEngine(), + mGraphicBuffer, mRenderEngine.getInternalRenderEngine(), ::android::renderengine::impl::ExternalTexture::Usage::READABLE); layerSettings.source.buffer.usePremultipliedAlpha = mBlendMode == BlendMode::PREMULTIPLIED; @@ -317,7 +318,7 @@ LayerSettings TestBufferLayer::toRenderEngineLayerSettings() { const float translateY = mSourceCrop.top / (static_cast<float>(mHeight)); layerSettings.source.buffer.textureTransform = - ::android::mat4::translate(::android::vec4(translateX, translateY, 0, 1)) * + ::android::mat4::translate(::android::vec4(translateX, translateY, 0, 1.0)) * ::android::mat4::scale(::android::vec4(scaleX, scaleY, 1.0, 1.0)); return layerSettings; @@ -325,9 +326,14 @@ LayerSettings TestBufferLayer::toRenderEngineLayerSettings() { void TestBufferLayer::fillBuffer(std::vector<Color>& expectedColors) { void* bufData; - auto status = mGraphicBuffer->lock(mUsage, &bufData); + int32_t bytesPerPixel = -1; + int32_t bytesPerStride = -1; + auto status = mGraphicBuffer->lock(mUsage, &bufData, &bytesPerPixel, &bytesPerStride); + const uint32_t stride = (bytesPerPixel > 0 && bytesPerStride > 0) + ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel) + : mGraphicBuffer->getStride(); EXPECT_EQ(::android::OK, status); - ASSERT_NO_FATAL_FAILURE(ReadbackHelper::fillBuffer(mWidth, mHeight, mStride, bufData, + ASSERT_NO_FATAL_FAILURE(ReadbackHelper::fillBuffer(mWidth, mHeight, stride, bufData, mPixelFormat, expectedColors)); EXPECT_EQ(::android::OK, mGraphicBuffer->unlock()); } diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/RenderEngineVts.cpp b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/RenderEngineVts.cpp index 6ff064f93c..0a55484c78 100644 --- a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/RenderEngineVts.cpp +++ b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/RenderEngineVts.cpp @@ -77,13 +77,18 @@ void TestRenderEngine::drawLayers() { } } -void TestRenderEngine::checkColorBuffer(std::vector<Color>& expectedColors) { +void TestRenderEngine::checkColorBuffer(const std::vector<Color>& expectedColors) { void* bufferData; - ASSERT_EQ(0, - mGraphicBuffer->lock(static_cast<uint32_t>(mGraphicBuffer->getUsage()), &bufferData)); - ReadbackHelper::compareColorBuffers( - expectedColors, bufferData, static_cast<int32_t>(mGraphicBuffer->getStride()), - mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight(), mFormat); + int32_t bytesPerPixel = -1; + int32_t bytesPerStride = -1; + ASSERT_EQ(0, mGraphicBuffer->lock(static_cast<uint32_t>(mGraphicBuffer->getUsage()), + &bufferData, &bytesPerPixel, &bytesPerStride)); + const uint32_t stride = (bytesPerPixel > 0 && bytesPerStride > 0) + ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel) + : mGraphicBuffer->getStride(); + ReadbackHelper::compareColorBuffers(expectedColors, bufferData, stride, + mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight(), + mFormat); ASSERT_EQ(::android::OK, mGraphicBuffer->unlock()); } diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/include/ReadbackVts.h b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/include/ReadbackVts.h index 0fac2b3bfe..a3ce795db1 100644 --- a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/include/ReadbackVts.h +++ b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/include/ReadbackVts.h @@ -43,6 +43,10 @@ using IMapper2_1 = ::android::hardware::graphics::mapper::V2_1::IMapper; static const Color BLACK = {0.0f, 0.0f, 0.0f, 1.0f}; static const Color RED = {1.0f, 0.0f, 0.0f, 1.0f}; +// DIM_RED is 90% dimmed from RED in linear space +// hard-code as value 243 in 8-bit space here, as calculating it requires +// oetf(eotf(value) * .9), which is a complex non-linear transformation +static const Color DIM_RED = {243.f / 255.f, 0.0f, 0.0f, 1.0f}; static const Color TRANSLUCENT_RED = {1.0f, 0.0f, 0.0f, 0.3f}; static const Color GREEN = {0.0f, 1.0f, 0.0f, 1.0f}; static const Color BLUE = {0.0f, 0.0f, 1.0f, 1.0f}; @@ -67,6 +71,7 @@ class TestLayer { void setDisplayFrame(Rect frame) { mDisplayFrame = frame; } void setSourceCrop(FRect crop) { mSourceCrop = crop; } void setZOrder(uint32_t z) { mZOrder = z; } + void setWhitePointNits(float whitePointNits) { mWhitePointNits = whitePointNits; } void setSurfaceDamage(std::vector<Rect> surfaceDamage) { mSurfaceDamage = std::move(surfaceDamage); @@ -84,10 +89,13 @@ class TestLayer { int64_t getLayer() const { return mLayer; } + float getWhitePointNits() const { return mWhitePointNits; } + protected: int64_t mDisplay; int64_t mLayer; Rect mDisplayFrame = {0, 0, 0, 0}; + float mWhitePointNits = -1.f; std::vector<Rect> mSurfaceDamage; Transform mTransform = static_cast<Transform>(0); FRect mSourceCrop = {0, 0, 0, 0}; @@ -153,7 +161,6 @@ class TestBufferLayer : public TestLayer { uint32_t mLayerCount; PixelFormat mPixelFormat; uint32_t mUsage; - uint32_t mStride; ::android::Rect mAccessRegion; }; @@ -181,15 +188,14 @@ class ReadbackHelper { static const std::vector<ColorMode> colorModes; static const std::vector<Dataspace> dataspaces; - static void compareColorBuffers(std::vector<Color>& expectedColors, void* bufferData, - const int32_t stride, const uint32_t width, + static void compareColorBuffers(const std::vector<Color>& expectedColors, void* bufferData, + const uint32_t stride, const uint32_t width, const uint32_t height, PixelFormat pixelFormat); }; class ReadbackBuffer { public: - ReadbackBuffer(int64_t display, const std::shared_ptr<IComposerClient>& client, - const ::android::sp<::android::GraphicBuffer>& graphicBuffer, int32_t width, + ReadbackBuffer(int64_t display, const std::shared_ptr<IComposerClient>& client, int32_t width, int32_t height, common::PixelFormat pixelFormat, common::Dataspace dataspace); void setReadbackBuffer(); @@ -203,7 +209,6 @@ class ReadbackBuffer { uint32_t mHeight; uint32_t mLayerCount; uint32_t mUsage; - uint32_t mStride; PixelFormat mPixelFormat; Dataspace mDataspace; int64_t mDisplay; diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/include/RenderEngineVts.h b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/include/RenderEngineVts.h index 2798e092a0..a776a279c4 100644 --- a/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/include/RenderEngineVts.h +++ b/graphics/composer/aidl/android/hardware/graphics/composer3/vts/functional/composer-vts/include/RenderEngineVts.h @@ -54,7 +54,7 @@ class TestRenderEngine { mDisplaySettings = displaySettings; }; void drawLayers(); - void checkColorBuffer(std::vector<Color>& expectedColors); + void checkColorBuffer(const std::vector<Color>& expectedColors); ::android::renderengine::RenderEngine& getInternalRenderEngine() { return *mRenderEngine; } diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp index 2ab9c01c5d..5e012f6ef7 100644 --- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp +++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp @@ -21,6 +21,7 @@ #include <thread> #include <vector> +#include <aidl/android/hardware/graphics/common/PixelFormat.h> #include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h> #include <android-base/logging.h> @@ -92,7 +93,13 @@ class GraphicsMapperHidlTest ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(descriptorInfo, true)); hidl_vec<uint8_t> vec; - ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, metadataType, &vec)); + const auto result = mGralloc->get(bufferHandle, metadataType, &vec); + + if (metadataType == gralloc4::MetadataType_Smpte2094_10 && result == Error::UNSUPPORTED) { + GTEST_SKIP() << "getting metadata for Smpte2094-10 is unsupported"; + } + + ASSERT_EQ(Error::NONE, result); ASSERT_NO_FATAL_FAILURE(decode(descriptorInfo, vec)); } @@ -1205,6 +1212,40 @@ TEST_P(GraphicsMapperHidlTest, IsSupportedY16) { } /** + * Test IMapper::isSupported with optional format R_8 + */ +TEST_P(GraphicsMapperHidlTest, IsSupportedR8) { + auto info = mDummyDescriptorInfo; + info.format = static_cast<android::hardware::graphics::common::V1_2::PixelFormat>( + aidl::android::hardware::graphics::common::PixelFormat::R_8); + bool supported = false; + + ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info)); + + if (!supported) { + GTEST_SUCCEED() << "R_8 is optional; unsupported so skipping allocation test"; + return; + } + + BufferDescriptor descriptor; + ASSERT_NO_FATAL_FAILURE(descriptor = mGralloc->createDescriptor(info)); + + constexpr uint32_t count = 1; + std::vector<const native_handle_t*> bufferHandles; + uint32_t stride; + ASSERT_NO_FATAL_FAILURE(bufferHandles = + mGralloc->allocate(descriptor, count, false, + Tolerance::kToleranceStrict, &stride)); + + EXPECT_LE(info.width, stride) << "invalid buffer stride"; + EXPECT_EQ(1u, bufferHandles.size()); + + for (auto bufferHandle : bufferHandles) { + mGralloc->freeBuffer(bufferHandle); + } +} + +/** * Test IMapper::get(BufferId) */ TEST_P(GraphicsMapperHidlTest, GetBufferId) { diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl index 34a87a66ec..97d9e845db 100644 --- a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl +++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl @@ -37,6 +37,7 @@ parcelable HealthInfo { boolean chargerAcOnline; boolean chargerUsbOnline; boolean chargerWirelessOnline; + boolean chargerDockOnline; int maxChargingCurrentMicroamps; int maxChargingVoltageMicrovolts; android.hardware.health.BatteryStatus batteryStatus; diff --git a/health/aidl/android/hardware/health/HealthInfo.aidl b/health/aidl/android/hardware/health/HealthInfo.aidl index 504e218b83..5b98baf13f 100644 --- a/health/aidl/android/hardware/health/HealthInfo.aidl +++ b/health/aidl/android/hardware/health/HealthInfo.aidl @@ -40,6 +40,10 @@ parcelable HealthInfo { */ boolean chargerWirelessOnline; /** + * Dock charger state - 'true' if online + */ + boolean chargerDockOnline; + /** * Maximum charging current supported by charger in µA */ int maxChargingCurrentMicroamps; diff --git a/health/aidl/default/HalHealthLoop.cpp b/health/aidl/default/HalHealthLoop.cpp index c9a081e0f2..ec23c10bc0 100644 --- a/health/aidl/default/HalHealthLoop.cpp +++ b/health/aidl/default/HalHealthLoop.cpp @@ -61,7 +61,7 @@ void HalHealthLoop::OnHealthInfoChanged(const HealthInfo& health_info) { void HalHealthLoop::set_charger_online(const HealthInfo& health_info) { charger_online_ = health_info.chargerAcOnline || health_info.chargerUsbOnline || - health_info.chargerWirelessOnline; + health_info.chargerWirelessOnline || health_info.chargerDockOnline; } } // namespace aidl::android::hardware::health diff --git a/health/aidl/default/health-convert.cpp b/health/aidl/default/health-convert.cpp index b5251f4bb5..6118865001 100644 --- a/health/aidl/default/health-convert.cpp +++ b/health/aidl/default/health-convert.cpp @@ -22,6 +22,7 @@ void convert(const HealthInfo& info, struct ::android::BatteryProperties* p) { p->chargerAcOnline = info.chargerAcOnline; p->chargerUsbOnline = info.chargerUsbOnline; p->chargerWirelessOnline = info.chargerWirelessOnline; + p->chargerDockOnline = info.chargerDockOnline; p->maxChargingCurrent = info.maxChargingCurrentMicroamps; p->maxChargingVoltage = info.maxChargingVoltageMicrovolts; p->batteryStatus = static_cast<int>(info.batteryStatus); diff --git a/keymaster/3.0/vts/functional/Android.bp b/keymaster/3.0/vts/functional/Android.bp index e2ae80373f..39bec3fffa 100644 --- a/keymaster/3.0/vts/functional/Android.bp +++ b/keymaster/3.0/vts/functional/Android.bp @@ -38,5 +38,11 @@ cc_test { "libcrypto_static", "libsoftkeymasterdevice", ], - test_suites: ["general-tests", "vts"], + test_suites: [ + "general-tests", + "vts", + ], + sanitize: { + cfi: false, + }, } diff --git a/keymaster/4.1/vts/functional/Android.bp b/keymaster/4.1/vts/functional/Android.bp index c650bec258..547ce38f60 100644 --- a/keymaster/4.1/vts/functional/Android.bp +++ b/keymaster/4.1/vts/functional/Android.bp @@ -48,4 +48,7 @@ cc_test { "general-tests", "vts", ], + sanitize: { + cfi: false, + }, } diff --git a/neuralnetworks/1.0/utils/Android.bp b/neuralnetworks/1.0/utils/Android.bp index 31cdded56e..ad30e30088 100644 --- a/neuralnetworks/1.0/utils/Android.bp +++ b/neuralnetworks/1.0/utils/Android.bp @@ -31,16 +31,11 @@ cc_library_static { export_include_dirs: ["include"], cflags: ["-Wthread-safety"], static_libs: [ + "android.hardware.neuralnetworks@1.0", "libarect", "neuralnetworks_types", "neuralnetworks_utils_hal_common", ], - shared_libs: [ - "android.hardware.neuralnetworks@1.0", - ], - export_static_lib_headers: [ - "neuralnetworks_utils_hal_common", - ], target: { android: { shared_libs: ["libnativewindow"], @@ -55,19 +50,14 @@ cc_test { static_libs: [ "android.hardware.neuralnetworks@1.0", "libgmock", - "libneuralnetworks_common", "neuralnetworks_types", "neuralnetworks_utils_hal_common", "neuralnetworks_utils_hal_1_0", ], shared_libs: [ - "android.hidl.allocator@1.0", - "android.hidl.memory@1.0", "libbase", "libcutils", - "libfmq", "libhidlbase", - "libhidlmemory", "liblog", "libutils", ], diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h index 8bd2fbed7d..cef76c6065 100644 --- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h +++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h @@ -45,12 +45,15 @@ class Burst final : public nn::IBurst { nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; private: const nn::SharedPreparedModel kPreparedModel; diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h index 0a6ca3edce..d7c43ef281 100644 --- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h +++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h @@ -65,8 +65,9 @@ class Device final : public nn::IDevice { nn::GeneralResult<nn::SharedPreparedModel> prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, - const nn::CacheToken& token) const override; + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache( nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h index bdb5b54281..337c13267d 100644 --- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h +++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h @@ -49,18 +49,23 @@ class PreparedModel final : public nn::IPreparedModel, nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced( const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const override; + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override; diff --git a/neuralnetworks/1.0/utils/src/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp index 128472110d..3642bc608f 100644 --- a/neuralnetworks/1.0/utils/src/Burst.cpp +++ b/neuralnetworks/1.0/utils/src/Burst.cpp @@ -50,15 +50,20 @@ Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& /*memory*/) nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { - return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration); + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { + return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration, hints, + extensionNameToPrefix); } nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { - return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration); + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { + return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration, hints, + extensionNameToPrefix); } } // namespace android::hardware::neuralnetworks::V1_0::utils diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp index b0c236efe8..620d04066f 100644 --- a/neuralnetworks/1.0/utils/src/Device.cpp +++ b/neuralnetworks/1.0/utils/src/Device.cpp @@ -143,7 +143,9 @@ nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Mo nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel( const nn::Model& model, nn::ExecutionPreference /*preference*/, nn::Priority /*priority*/, nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/, - const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const { + const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that model is ready for IPC. std::optional<nn::Model> maybeModelInShared; const nn::Model& modelInShared = diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp index 00e7d22916..b8055fc081 100644 --- a/neuralnetworks/1.0/utils/src/PreparedModel.cpp +++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp @@ -59,7 +59,9 @@ PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_0::IPreparedMo nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute( const nn::Request& request, nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/) const { + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -94,19 +96,22 @@ PreparedModel::executeInternal(const V1_0::Request& request, } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> -PreparedModel::executeFenced(const nn::Request& /*request*/, - const std::vector<nn::SyncFence>& /*waitFor*/, - nn::MeasureTiming /*measure*/, - const nn::OptionalTimePoint& /*deadline*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/, - const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const { +PreparedModel::executeFenced( + const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/, + nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/, + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const nn::OptionalDuration& /*timeoutDurationAfterFence*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "IPreparedModel::executeFenced is not supported on 1.0 HAL service"; } nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution( const nn::Request& request, nn::MeasureTiming /*measure*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/) const { + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; diff --git a/neuralnetworks/1.0/utils/test/DeviceTest.cpp b/neuralnetworks/1.0/utils/test/DeviceTest.cpp index 83e555fad5..9e9db1641d 100644 --- a/neuralnetworks/1.0/utils/test/DeviceTest.cpp +++ b/neuralnetworks/1.0/utils/test/DeviceTest.cpp @@ -380,7 +380,7 @@ TEST(DeviceTest, prepareModel) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -399,7 +399,7 @@ TEST(DeviceTest, prepareModelLaunchError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -417,7 +417,7 @@ TEST(DeviceTest, prepareModelReturnError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -435,7 +435,7 @@ TEST(DeviceTest, prepareModelNullptrError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -452,7 +452,7 @@ TEST(DeviceTest, prepareModelTransportFailure) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -469,7 +469,7 @@ TEST(DeviceTest, prepareModelDeadObject) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -488,7 +488,7 @@ TEST(DeviceTest, prepareModelAsyncCrash) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); diff --git a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp index 7820c06746..e03a98de32 100644 --- a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp +++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp @@ -121,7 +121,7 @@ TEST(PreparedModelTest, execute) { .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result EXPECT_TRUE(result.has_value()) @@ -138,7 +138,7 @@ TEST(PreparedModelTest, executeLaunchError) { V1_0::ErrorStatus::GENERAL_FAILURE))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -155,7 +155,7 @@ TEST(PreparedModelTest, executeReturnError) { makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -171,7 +171,7 @@ TEST(PreparedModelTest, executeTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -187,7 +187,7 @@ TEST(PreparedModelTest, executeDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -205,7 +205,7 @@ TEST(PreparedModelTest, executeCrash) { EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -218,7 +218,7 @@ TEST(PreparedModelTest, executeFencedNotSupported) { const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -235,7 +235,7 @@ TEST(PreparedModelTest, reusableExecute) { .WillRepeatedly(Invoke(makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -258,7 +258,7 @@ TEST(PreparedModelTest, reusableExecuteLaunchError) { V1_0::ErrorStatus::GENERAL_FAILURE))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -279,7 +279,7 @@ TEST(PreparedModelTest, reusableExecuteReturnError) { makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -299,7 +299,7 @@ TEST(PreparedModelTest, reusableExecuteTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -319,7 +319,7 @@ TEST(PreparedModelTest, reusableExecuteDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -341,7 +341,7 @@ TEST(PreparedModelTest, reusableExecuteCrash) { EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -358,7 +358,7 @@ TEST(PreparedModelTest, reusableExecuteFencedNotSupported) { const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); diff --git a/neuralnetworks/1.1/utils/Android.bp b/neuralnetworks/1.1/utils/Android.bp index 737ff58159..4b8999f559 100644 --- a/neuralnetworks/1.1/utils/Android.bp +++ b/neuralnetworks/1.1/utils/Android.bp @@ -31,16 +31,11 @@ cc_library_static { export_include_dirs: ["include"], cflags: ["-Wthread-safety"], static_libs: [ - "neuralnetworks_types", - "neuralnetworks_utils_hal_common", - "neuralnetworks_utils_hal_1_0", - ], - shared_libs: [ "android.hardware.neuralnetworks@1.0", "android.hardware.neuralnetworks@1.1", - ], - export_static_lib_headers: [ + "neuralnetworks_types", "neuralnetworks_utils_hal_common", + "neuralnetworks_utils_hal_1_0", ], } @@ -52,20 +47,15 @@ cc_test { "android.hardware.neuralnetworks@1.0", "android.hardware.neuralnetworks@1.1", "libgmock", - "libneuralnetworks_common", "neuralnetworks_types", "neuralnetworks_utils_hal_common", "neuralnetworks_utils_hal_1_0", "neuralnetworks_utils_hal_1_1", ], shared_libs: [ - "android.hidl.allocator@1.0", - "android.hidl.memory@1.0", "libbase", "libcutils", - "libfmq", "libhidlbase", - "libhidlmemory", "liblog", "libutils", ], diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h index d6bd36a7fe..38ca1382be 100644 --- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h +++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h @@ -64,8 +64,9 @@ class Device final : public nn::IDevice { nn::GeneralResult<nn::SharedPreparedModel> prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, - const nn::CacheToken& token) const override; + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache( nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp index 3effa8428d..28f3276389 100644 --- a/neuralnetworks/1.1/utils/src/Device.cpp +++ b/neuralnetworks/1.1/utils/src/Device.cpp @@ -143,7 +143,9 @@ nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Mo nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority /*priority*/, nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/, - const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const { + const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that model is ready for IPC. std::optional<nn::Model> maybeModelInShared; const nn::Model& modelInShared = diff --git a/neuralnetworks/1.1/utils/test/DeviceTest.cpp b/neuralnetworks/1.1/utils/test/DeviceTest.cpp index 2248da6ffe..8ab87bc97a 100644 --- a/neuralnetworks/1.1/utils/test/DeviceTest.cpp +++ b/neuralnetworks/1.1/utils/test/DeviceTest.cpp @@ -390,7 +390,7 @@ TEST(DeviceTest, prepareModel) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -409,7 +409,7 @@ TEST(DeviceTest, prepareModelLaunchError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -427,7 +427,7 @@ TEST(DeviceTest, prepareModelReturnError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -445,7 +445,7 @@ TEST(DeviceTest, prepareModelNullptrError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -462,7 +462,7 @@ TEST(DeviceTest, prepareModelTransportFailure) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -479,7 +479,7 @@ TEST(DeviceTest, prepareModelDeadObject) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -498,7 +498,7 @@ TEST(DeviceTest, prepareModelAsyncCrash) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); diff --git a/neuralnetworks/1.2/utils/Android.bp b/neuralnetworks/1.2/utils/Android.bp index 4eefb0f7dc..4c5f065dda 100644 --- a/neuralnetworks/1.2/utils/Android.bp +++ b/neuralnetworks/1.2/utils/Android.bp @@ -31,19 +31,14 @@ cc_library_static { export_include_dirs: ["include"], cflags: ["-Wthread-safety"], static_libs: [ - "neuralnetworks_types", - "neuralnetworks_utils_hal_common", - "neuralnetworks_utils_hal_1_0", - "neuralnetworks_utils_hal_1_1", - ], - shared_libs: [ "android.hardware.neuralnetworks@1.0", "android.hardware.neuralnetworks@1.1", "android.hardware.neuralnetworks@1.2", "libfmq", - ], - export_static_lib_headers: [ + "neuralnetworks_types", "neuralnetworks_utils_hal_common", + "neuralnetworks_utils_hal_1_0", + "neuralnetworks_utils_hal_1_1", ], product_variables: { debuggable: { // eng and userdebug builds @@ -71,7 +66,6 @@ cc_test { "android.hardware.neuralnetworks@1.1", "android.hardware.neuralnetworks@1.2", "libgmock", - "libneuralnetworks_common", "neuralnetworks_types", "neuralnetworks_utils_hal_common", "neuralnetworks_utils_hal_1_0", @@ -79,13 +73,10 @@ cc_test { "neuralnetworks_utils_hal_1_2", ], shared_libs: [ - "android.hidl.allocator@1.0", - "android.hidl.memory@1.0", "libbase", "libcutils", "libfmq", "libhidlbase", - "libhidlmemory", "liblog", "libutils", ], diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Burst.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Burst.h index ac9411c462..1b28476a88 100644 --- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Burst.h +++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Burst.h @@ -170,13 +170,16 @@ class Burst final : public nn::IBurst, public std::enable_shared_from_this<Burst // See IBurst::execute for information on this method. nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; // See IBurst::createReusableExecution for information on this method. nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; // If fallback is not nullptr, this method will invoke the fallback function to try another // execution path if the packet could not be sent. Otherwise, failing to send the packet will diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h index c3348aa8f2..4f13adc0a3 100644 --- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h +++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h @@ -37,7 +37,7 @@ GeneralResult<Operand> unvalidatedConvert(const hal::V1_2::Operand& operand); GeneralResult<Operand::ExtraParams> unvalidatedConvert( const hal::V1_2::Operand::ExtraParams& extraParams); GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model); -GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert( +GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix); GeneralResult<OutputShape> unvalidatedConvert(const hal::V1_2::OutputShape& outputShape); GeneralResult<MeasureTiming> unvalidatedConvert(const hal::V1_2::MeasureTiming& measureTiming); @@ -78,7 +78,7 @@ nn::GeneralResult<Operand::ExtraParams> unvalidatedConvert( const nn::Operand::ExtraParams& extraParams); nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model); nn::GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert( - const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix); + const nn::ExtensionNameAndPrefix& extensionNameAndPrefix); nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape); nn::GeneralResult<MeasureTiming> unvalidatedConvert(const nn::MeasureTiming& measureTiming); nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing); diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h index e7ac172211..d92cf50aa3 100644 --- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h +++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h @@ -83,8 +83,9 @@ class Device final : public nn::IDevice { nn::GeneralResult<nn::SharedPreparedModel> prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, - const nn::CacheToken& token) const override; + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache( nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h index 1150e5e79b..72a5b2f007 100644 --- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h +++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h @@ -49,18 +49,23 @@ class PreparedModel final : public nn::IPreparedModel, nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced( const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const override; + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override; diff --git a/neuralnetworks/1.2/utils/src/Burst.cpp b/neuralnetworks/1.2/utils/src/Burst.cpp index 911fbfa981..23e80709a0 100644 --- a/neuralnetworks/1.2/utils/src/Burst.cpp +++ b/neuralnetworks/1.2/utils/src/Burst.cpp @@ -305,8 +305,9 @@ Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& memory) cons nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // This is the first point when we know an execution is occurring, so begin to collect // systraces. Note that the first point we can begin collecting systraces in // ExecutionBurstServer is when the RequestChannelReceiver realizes there is data in the FMQ, so @@ -317,7 +318,7 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: // fall back to another execution path if (!compliantVersion(request).ok()) { // fallback to another execution path if the packet could not be sent - return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration); + return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration, {}, {}); } // ensure that request is ready for IPC @@ -346,7 +347,7 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: // send request packet const auto requestPacket = serialize(hidlRequest, hidlMeasure, slots); const auto fallback = [this, &request, measure, &deadline, &loopTimeoutDuration] { - return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration); + return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration, {}, {}); }; return executeInternal(requestPacket, relocation, fallback); } @@ -354,14 +355,17 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: // See IBurst::createReusableExecution for information on this method. nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "Burst::createReusableExecution"); // if the request is valid but of a higher version than what's supported in burst execution, // fall back to another execution path if (!compliantVersion(request).ok()) { // fallback to another execution path if the packet could not be sent - return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration); + return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration, {}, + {}); } // ensure that request is ready for IPC diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp index 838d9c4717..62ec2ed6c6 100644 --- a/neuralnetworks/1.2/utils/src/Conversions.cpp +++ b/neuralnetworks/1.2/utils/src/Conversions.cpp @@ -212,9 +212,9 @@ GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model) { }; } -GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert( +GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) { - return Model::ExtensionNameAndPrefix{ + return ExtensionNameAndPrefix{ .name = extensionNameAndPrefix.name, .prefix = extensionNameAndPrefix.prefix, }; @@ -495,7 +495,7 @@ nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) { } nn::GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert( - const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) { + const nn::ExtensionNameAndPrefix& extensionNameAndPrefix) { return Model::ExtensionNameAndPrefix{ .name = extensionNameAndPrefix.name, .prefix = extensionNameAndPrefix.prefix, diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp index e7acecdf7a..3a58d2c7cc 100644 --- a/neuralnetworks/1.2/utils/src/Device.cpp +++ b/neuralnetworks/1.2/utils/src/Device.cpp @@ -236,7 +236,9 @@ nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Mo nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority /*priority*/, nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const { + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that model is ready for IPC. std::optional<nn::Model> maybeModelInShared; const nn::Model& modelInShared = diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp index 6df3df332a..feb3951a4a 100644 --- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp +++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp @@ -91,7 +91,9 @@ PreparedModel::executeAsynchronously(const V1_0::Request& request, MeasureTiming nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute( const nn::Request& request, nn::MeasureTiming measure, const nn::OptionalTimePoint& /*deadline*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/) const { + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -123,19 +125,22 @@ PreparedModel::executeInternal(const V1_0::Request& request, MeasureTiming measu } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> -PreparedModel::executeFenced(const nn::Request& /*request*/, - const std::vector<nn::SyncFence>& /*waitFor*/, - nn::MeasureTiming /*measure*/, - const nn::OptionalTimePoint& /*deadline*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/, - const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const { +PreparedModel::executeFenced( + const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/, + nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/, + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const nn::OptionalDuration& /*timeoutDurationAfterFence*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "IPreparedModel::executeFenced is not supported on 1.2 HAL service"; } nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& /*loopTimeoutDuration*/) const { + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; diff --git a/neuralnetworks/1.2/utils/test/DeviceTest.cpp b/neuralnetworks/1.2/utils/test/DeviceTest.cpp index 1dc6285be5..0d8c141582 100644 --- a/neuralnetworks/1.2/utils/test/DeviceTest.cpp +++ b/neuralnetworks/1.2/utils/test/DeviceTest.cpp @@ -636,7 +636,7 @@ TEST(DeviceTest, prepareModel) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -655,7 +655,7 @@ TEST(DeviceTest, prepareModelLaunchError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -673,7 +673,7 @@ TEST(DeviceTest, prepareModelReturnError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -691,7 +691,7 @@ TEST(DeviceTest, prepareModelNullptrError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -708,7 +708,7 @@ TEST(DeviceTest, prepareModelTransportFailure) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -725,7 +725,7 @@ TEST(DeviceTest, prepareModelDeadObject) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -746,7 +746,7 @@ TEST(DeviceTest, prepareModelAsyncCrash) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); diff --git a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp index 5e2ad79df5..a5ec9d36cd 100644 --- a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp +++ b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp @@ -154,7 +154,7 @@ TEST(PreparedModelTest, executeSync) { .WillOnce(Invoke(makeExecuteSynchronously(V1_0::ErrorStatus::NONE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result EXPECT_TRUE(result.has_value()) @@ -172,7 +172,7 @@ TEST(PreparedModelTest, executeSyncError) { makeExecuteSynchronously(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -189,7 +189,7 @@ TEST(PreparedModelTest, executeSyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -206,7 +206,7 @@ TEST(PreparedModelTest, executeSyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -224,7 +224,7 @@ TEST(PreparedModelTest, executeAsync) { V1_0::ErrorStatus::NONE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result EXPECT_TRUE(result.has_value()) @@ -243,7 +243,7 @@ TEST(PreparedModelTest, executeAsyncLaunchError) { kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -261,7 +261,7 @@ TEST(PreparedModelTest, executeAsyncReturnError) { V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -278,7 +278,7 @@ TEST(PreparedModelTest, executeAsyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -295,7 +295,7 @@ TEST(PreparedModelTest, executeAsyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -314,7 +314,7 @@ TEST(PreparedModelTest, executeAsyncCrash) { EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(InvokeWithoutArgs(ret)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -328,7 +328,7 @@ TEST(PreparedModelTest, executeFencedNotSupported) { PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value(); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -347,7 +347,7 @@ TEST(PreparedModelTest, reusableExecuteSync) { Invoke(makeExecuteSynchronously(V1_0::ErrorStatus::NONE, {}, kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -371,7 +371,7 @@ TEST(PreparedModelTest, reusableExecuteSyncError) { makeExecuteSynchronously(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -392,7 +392,7 @@ TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -413,7 +413,7 @@ TEST(PreparedModelTest, reusableExecuteSyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -436,7 +436,7 @@ TEST(PreparedModelTest, reusableExecuteAsync) { V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, {}, kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -461,7 +461,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncLaunchError) { kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -483,7 +483,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncReturnError) { V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -504,7 +504,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -525,7 +525,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -548,7 +548,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncCrash) { EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(InvokeWithoutArgs(ret)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -566,7 +566,7 @@ TEST(PreparedModelTest, reusableExecuteFencedNotSupported) { PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value(); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); diff --git a/neuralnetworks/1.3/utils/Android.bp b/neuralnetworks/1.3/utils/Android.bp index 7acb4fcb30..c512dda772 100644 --- a/neuralnetworks/1.3/utils/Android.bp +++ b/neuralnetworks/1.3/utils/Android.bp @@ -31,21 +31,16 @@ cc_library_static { export_include_dirs: ["include"], cflags: ["-Wthread-safety"], static_libs: [ - "neuralnetworks_types", - "neuralnetworks_utils_hal_common", - "neuralnetworks_utils_hal_1_0", - "neuralnetworks_utils_hal_1_1", - "neuralnetworks_utils_hal_1_2", - ], - shared_libs: [ "android.hardware.neuralnetworks@1.0", "android.hardware.neuralnetworks@1.1", "android.hardware.neuralnetworks@1.2", "android.hardware.neuralnetworks@1.3", "libfmq", - ], - export_static_lib_headers: [ + "neuralnetworks_types", "neuralnetworks_utils_hal_common", + "neuralnetworks_utils_hal_1_0", + "neuralnetworks_utils_hal_1_1", + "neuralnetworks_utils_hal_1_2", ], target: { host: { @@ -69,7 +64,6 @@ cc_test { "android.hardware.neuralnetworks@1.2", "android.hardware.neuralnetworks@1.3", "libgmock", - "libneuralnetworks_common", "neuralnetworks_types", "neuralnetworks_utils_hal_common", "neuralnetworks_utils_hal_1_0", @@ -78,13 +72,10 @@ cc_test { "neuralnetworks_utils_hal_1_3", ], shared_libs: [ - "android.hidl.allocator@1.0", - "android.hidl.memory@1.0", "libbase", "libcutils", "libfmq", "libhidlbase", - "libhidlmemory", "liblog", "libutils", ], diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h index c3c6fc4eb8..cf5e5ea018 100644 --- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h +++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h @@ -66,8 +66,9 @@ class Device final : public nn::IDevice { nn::GeneralResult<nn::SharedPreparedModel> prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, - const nn::CacheToken& token) const override; + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache( nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h index 480438d9f2..124cc43938 100644 --- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h +++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h @@ -48,18 +48,23 @@ class PreparedModel final : public nn::IPreparedModel, nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced( const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const override; + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override; diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp index a1d414c700..09e9d80d38 100644 --- a/neuralnetworks/1.3/utils/src/Conversions.cpp +++ b/neuralnetworks/1.3/utils/src/Conversions.cpp @@ -396,7 +396,7 @@ nn::GeneralResult<V1_2::Operand::ExtraParams> unvalidatedConvert( } nn::GeneralResult<V1_2::Model::ExtensionNameAndPrefix> unvalidatedConvert( - const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) { + const nn::ExtensionNameAndPrefix& extensionNameAndPrefix) { return V1_2::utils::unvalidatedConvert(extensionNameAndPrefix); } diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp index 9517fda877..824cec61c8 100644 --- a/neuralnetworks/1.3/utils/src/Device.cpp +++ b/neuralnetworks/1.3/utils/src/Device.cpp @@ -187,7 +187,9 @@ nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Mo nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const { + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that model is ready for IPC. std::optional<nn::Model> maybeModelInShared; const nn::Model& modelInShared = diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp index ce977e55ed..b92f877a51 100644 --- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp +++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp @@ -135,8 +135,9 @@ PreparedModel::executeAsynchronously(const Request& request, V1_2::MeasureTiming nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -174,10 +175,13 @@ PreparedModel::executeInternal(const Request& request, V1_2::MeasureTiming measu } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> -PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, - nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const { +PreparedModel::executeFenced( + const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, + nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, + const nn::OptionalDuration& loopTimeoutDuration, + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -230,7 +234,9 @@ PreparedModel::executeFencedInternal(const Request& request, const hidl_vec<hidl nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; diff --git a/neuralnetworks/1.3/utils/test/DeviceTest.cpp b/neuralnetworks/1.3/utils/test/DeviceTest.cpp index 7eba4bc935..6f488376a5 100644 --- a/neuralnetworks/1.3/utils/test/DeviceTest.cpp +++ b/neuralnetworks/1.3/utils/test/DeviceTest.cpp @@ -658,7 +658,7 @@ TEST(DeviceTest, prepareModel) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -677,7 +677,7 @@ TEST(DeviceTest, prepareModelLaunchError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -695,7 +695,7 @@ TEST(DeviceTest, prepareModelReturnError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -713,7 +713,7 @@ TEST(DeviceTest, prepareModelNullptrError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -730,7 +730,7 @@ TEST(DeviceTest, prepareModelTransportFailure) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -747,7 +747,7 @@ TEST(DeviceTest, prepareModelDeadObject) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -768,7 +768,7 @@ TEST(DeviceTest, prepareModelAsyncCrash) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); diff --git a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp index 6dbbd6bd7e..51b5d29eac 100644 --- a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp +++ b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp @@ -182,7 +182,7 @@ TEST(PreparedModelTest, executeSync) { .WillOnce(Invoke(makeExecuteSynchronously(V1_3::ErrorStatus::NONE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result EXPECT_TRUE(result.has_value()) @@ -200,7 +200,7 @@ TEST(PreparedModelTest, executeSyncError) { makeExecuteSynchronously(V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -217,7 +217,7 @@ TEST(PreparedModelTest, executeSyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -234,7 +234,7 @@ TEST(PreparedModelTest, executeSyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -252,7 +252,7 @@ TEST(PreparedModelTest, executeAsync) { V1_3::ErrorStatus::NONE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result EXPECT_TRUE(result.has_value()) @@ -271,7 +271,7 @@ TEST(PreparedModelTest, executeAsyncLaunchError) { kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -289,7 +289,7 @@ TEST(PreparedModelTest, executeAsyncReturnError) { V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -306,7 +306,7 @@ TEST(PreparedModelTest, executeAsyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -323,7 +323,7 @@ TEST(PreparedModelTest, executeAsyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -344,7 +344,7 @@ TEST(PreparedModelTest, executeAsyncCrash) { .WillOnce(InvokeWithoutArgs(ret)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -366,7 +366,7 @@ TEST(PreparedModelTest, executeFenced) { .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback))); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -396,7 +396,7 @@ TEST(PreparedModelTest, executeFencedCallbackError) { .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback))); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -422,7 +422,7 @@ TEST(PreparedModelTest, executeFencedError) { makeExecuteFencedReturn(V1_3::ErrorStatus::GENERAL_FAILURE, {}, nullptr))); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -439,7 +439,7 @@ TEST(PreparedModelTest, executeFencedTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -456,7 +456,7 @@ TEST(PreparedModelTest, executeFencedDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -475,7 +475,7 @@ TEST(PreparedModelTest, reusableExecuteSync) { Invoke(makeExecuteSynchronously(V1_3::ErrorStatus::NONE, {}, kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -499,7 +499,7 @@ TEST(PreparedModelTest, reusableExecuteSyncError) { makeExecuteSynchronously(V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -520,7 +520,7 @@ TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -541,7 +541,7 @@ TEST(PreparedModelTest, reusableExecuteSyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -564,7 +564,7 @@ TEST(PreparedModelTest, reusableExecuteAsync) { V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, {}, kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -589,7 +589,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncLaunchError) { kNoTiming))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -611,7 +611,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncReturnError) { V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -628,7 +628,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -649,7 +649,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -674,7 +674,7 @@ TEST(PreparedModelTest, reusableExecuteAsyncCrash) { .WillOnce(InvokeWithoutArgs(ret)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -702,7 +702,7 @@ TEST(PreparedModelTest, reusableExecuteFenced) { Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -738,7 +738,7 @@ TEST(PreparedModelTest, reusableExecuteFencedCallbackError) { .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -768,7 +768,7 @@ TEST(PreparedModelTest, reusableExecuteFencedError) { makeExecuteFencedReturn(V1_3::ErrorStatus::GENERAL_FAILURE, {}, nullptr))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -789,7 +789,7 @@ TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -810,7 +810,7 @@ TEST(PreparedModelTest, reusableExecuteFencedDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionConfig.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionConfig.aidl new file mode 100644 index 0000000000..cb85743a01 --- /dev/null +++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionConfig.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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 <name>-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.neuralnetworks; +@VintfStability +parcelable ExecutionConfig { + boolean measureTiming; + long loopTimeoutDurationNs; + android.hardware.neuralnetworks.TokenValuePair[] executionHints; + android.hardware.neuralnetworks.ExtensionNameAndPrefix[] extensionNameToPrefix; +} diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl index eb3d0b004a..461fdfa2c6 100644 --- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl +++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl @@ -36,4 +36,5 @@ package android.hardware.neuralnetworks; interface IBurst { android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in long[] memoryIdentifierTokens, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs); void releaseMemoryResource(in long memoryIdentifierToken); + android.hardware.neuralnetworks.ExecutionResult executeSynchronouslyWithConfig(in android.hardware.neuralnetworks.Request request, in long[] memoryIdentifierTokens, in android.hardware.neuralnetworks.ExecutionConfig config, in long deadlineNs); } diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl index c9c67f2fcd..c0fba47d0e 100644 --- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl +++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl @@ -43,6 +43,7 @@ interface IDevice { String getVersionString(); void prepareModel(in android.hardware.neuralnetworks.Model model, in android.hardware.neuralnetworks.ExecutionPreference preference, in android.hardware.neuralnetworks.Priority priority, in long deadlineNs, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in android.hardware.neuralnetworks.IPreparedModelCallback callback); void prepareModelFromCache(in long deadlineNs, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in android.hardware.neuralnetworks.IPreparedModelCallback callback); + void prepareModelWithConfig(in android.hardware.neuralnetworks.Model model, in android.hardware.neuralnetworks.PrepareModelConfig config, in android.hardware.neuralnetworks.IPreparedModelCallback callback); const int BYTE_SIZE_OF_CACHE_TOKEN = 32; const int MAX_NUMBER_OF_CACHE_FILES = 32; const int EXTENSION_TYPE_HIGH_BITS_PREFIX = 15; diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl index f89956719e..fb0c372267 100644 --- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl +++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl @@ -37,7 +37,9 @@ interface IPreparedModel { android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs); android.hardware.neuralnetworks.FencedExecutionResult executeFenced(in android.hardware.neuralnetworks.Request request, in ParcelFileDescriptor[] waitFor, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs, in long durationNs); android.hardware.neuralnetworks.IBurst configureExecutionBurst(); - android.hardware.neuralnetworks.IExecution createReusableExecution(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long loopTimeoutDurationNs); + android.hardware.neuralnetworks.IExecution createReusableExecution(in android.hardware.neuralnetworks.Request request, in android.hardware.neuralnetworks.ExecutionConfig config); + android.hardware.neuralnetworks.ExecutionResult executeSynchronouslyWithConfig(in android.hardware.neuralnetworks.Request request, in android.hardware.neuralnetworks.ExecutionConfig config, in long deadlineNs); + android.hardware.neuralnetworks.FencedExecutionResult executeFencedWithConfig(in android.hardware.neuralnetworks.Request request, in ParcelFileDescriptor[] waitFor, in android.hardware.neuralnetworks.ExecutionConfig config, in long deadlineNs, in long durationNs); const long DEFAULT_LOOP_TIMEOUT_DURATION_NS = 2000000000; const long MAXIMUM_LOOP_TIMEOUT_DURATION_NS = 15000000000; } diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PrepareModelConfig.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PrepareModelConfig.aidl new file mode 100644 index 0000000000..85c924fe31 --- /dev/null +++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PrepareModelConfig.aidl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 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 <name>-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.neuralnetworks; +@VintfStability +parcelable PrepareModelConfig { + android.hardware.neuralnetworks.ExecutionPreference preference; + android.hardware.neuralnetworks.Priority priority; + long deadlineNs; + ParcelFileDescriptor[] modelCache; + ParcelFileDescriptor[] dataCache; + byte[] cacheToken; + android.hardware.neuralnetworks.TokenValuePair[] compilationHints; + android.hardware.neuralnetworks.ExtensionNameAndPrefix[] extensionNameToPrefix; +} diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/TokenValuePair.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/TokenValuePair.aidl new file mode 100644 index 0000000000..e477d6e555 --- /dev/null +++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/TokenValuePair.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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 <name>-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.neuralnetworks; +@VintfStability +parcelable TokenValuePair { + int token; + byte[] value; +} diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionConfig.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionConfig.aidl new file mode 100644 index 0000000000..00f1e11740 --- /dev/null +++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionConfig.aidl @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 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.neuralnetworks; + +import android.hardware.neuralnetworks.ExtensionNameAndPrefix; +import android.hardware.neuralnetworks.TokenValuePair; + +/** + * A type that is used to represent all configuration related to + * an Execution. + */ +@VintfStability +parcelable ExecutionConfig { + /** + * Specifies whether or not to measure duration of the execution. + * For {@link IPreparedModel::executeSynchronouslyWithConfig}, the duration runs from the time + * the driver sees the corresponding call to the execute function to the time the driver returns + * from the function. For {@link IPreparedModel::executeFencedWithConfig}, please refer to + * {@link IPreparedModelCallback} for details. + */ + boolean measureTiming; + /** + * The maximum amount of time in nanoseconds that should be spent + * executing a {@link OperationType::WHILE} operation. If a loop + * condition model does not output false within this duration, + * the execution must be aborted. If -1 is provided, the maximum + * amount of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. + * Other negative values are invalid. When provided, the duration + * must not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}. + */ + long loopTimeoutDurationNs; + /** + * A vector of token / value pairs represent vendor specific + * execution hints or metadata. The provided TokenValuePairs must not + * contain the same token twice. The driver must validate the + * data and ignore invalid hints. It is up to the driver to + * decide whether to respect the provided hints or not. + */ + TokenValuePair[] executionHints; + /** + * The mapping between extension names and prefixes of token values. + * The driver must ignore the corresponding execution hint, if + * the extension is not supported. + */ + ExtensionNameAndPrefix[] extensionNameToPrefix; +} diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl index 20109bd584..9f70a53608 100644 --- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl +++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl @@ -20,6 +20,10 @@ import android.hardware.neuralnetworks.ExtensionOperandTypeInformation; /** * Information about an extension. + * + * The extension can provide zero or more operation types (which are not enumerated), zero or more + * operand types (which are enumerated in {@link Extension::operandTypes}, and compilation and + * execution hints (which are not enumerated). */ @VintfStability parcelable Extension { diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl index 29be93f549..6c296e0f64 100644 --- a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl +++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl @@ -17,7 +17,8 @@ package android.hardware.neuralnetworks; /** - * The mapping between extension names and prefixes of operand and operation type values. + * The mapping between extension names and prefixes of values like operand and operation type, and + * token in {@link TokenValuePair}. * * An operand or operation whose numeric type value is above {@link IDevice::OPERAND_TYPE_BASE_MAX} * or {@link IDevice::OPERATION_TYPE_BASE_MAX} respectively should be interpreted as an extension diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl index b089c499c6..a05a7fb9c7 100644 --- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl +++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl @@ -17,6 +17,7 @@ package android.hardware.neuralnetworks; import android.hardware.neuralnetworks.ErrorStatus; +import android.hardware.neuralnetworks.ExecutionConfig; import android.hardware.neuralnetworks.ExecutionResult; import android.hardware.neuralnetworks.Request; @@ -68,6 +69,8 @@ interface IBurst { * * Only a single execution on a given burst object may be active at any time. * + * Also see {@link IBurst::executeSynchronouslyWithConfig}. + * * @param request The input and output information on which the prepared model is to be * executed. * @param memoryIdentifierTokens A list of tokens where each token is a non-negative number @@ -117,4 +120,13 @@ interface IBurst { * - INVALID_ARGUMENT if one of the input arguments is invalid */ void releaseMemoryResource(in long memoryIdentifierToken); + + /** + * For detailed specification, please refer to {@link IBurst::executeSynchronously}. The + * difference between the two methods is that executeSynchronouslyWithConfig takes {@link + * ExecutionConfig} instead of a list of configuration parameters, and ExecutionConfig contains + * more configuration parameters than are passed to executeSynchronously. + */ + ExecutionResult executeSynchronouslyWithConfig(in Request request, + in long[] memoryIdentifierTokens, in ExecutionConfig config, in long deadlineNs); } diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl index 72e26237f8..821b9febfe 100644 --- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl +++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl @@ -28,6 +28,7 @@ import android.hardware.neuralnetworks.IPreparedModelCallback; import android.hardware.neuralnetworks.IPreparedModelParcel; import android.hardware.neuralnetworks.Model; import android.hardware.neuralnetworks.NumberOfCacheFiles; +import android.hardware.neuralnetworks.PrepareModelConfig; import android.hardware.neuralnetworks.Priority; /** @@ -148,7 +149,7 @@ interface IDevice { * * If the device reports that caching is not supported, the user may avoid calling * IDevice::prepareModelFromCache or providing cache file descriptors to - * IDevice::prepareModel. + * IDevice::prepareModel or IDevice::prepareModelWithConfig. * * @return NumberOfCacheFiles structure indicating how many files for model and data cache the * driver needs to cache a single prepared model. It must be less than or equal to @@ -302,6 +303,8 @@ interface IDevice { * * Multiple threads may call prepareModel on the same model concurrently. * + * Also see {@link IDevice::prepareModelWithConfig}. + * * @param model The model to be prepared for execution. * @param preference Indicates the intended execution behavior of a prepared model. * @param priority The priority of the prepared model relative to other prepared models owned by @@ -403,17 +406,17 @@ interface IDevice { * @param modelCache A vector of file descriptors for the security-sensitive cache. The length * of the vector must match the numModelCache returned from * getNumberOfCacheFilesNeeded. The cache file descriptors will be provided in - * the same order as with prepareModel. + * the same order as with prepareModel or prepareModelWithConfig. * @param dataCache A vector of file descriptors for the constants' cache. The length of the * vector must match the numDataCache returned from * getNumberOfCacheFilesNeeded. The cache file descriptors will be provided in - * the same order as with prepareModel. + * the same order as with prepareModel or prepareModelWithConfig. * @param token A caching token of length BYTE_SIZE_OF_CACHE_TOKEN identifying the prepared * model. It is the same token provided when saving the cache files with - * prepareModel. Tokens should be chosen to have a low rate of collision for a - * particular application. The driver cannot detect a collision; a collision will - * result in a failed execution or in a successful execution that produces - * incorrect output values. + * prepareModel or prepareModelWithConfig. Tokens should be chosen to have a low + * rate of collision for a particular application. The driver cannot detect a + * collision; a collision will result in a failed execution or in a successful + * execution that produces incorrect output values. * @param callback A callback object used to return the error status of preparing the model for * execution and the prepared model if successful, nullptr otherwise. The * callback object's notify function must be called exactly once, even if the @@ -429,4 +432,28 @@ interface IDevice { void prepareModelFromCache(in long deadlineNs, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in IPreparedModelCallback callback); + + /** + * For detailed specification, please refer to {@link IDevice::prepareModel}. The only + * difference between the two methods is that prepareModelWithConfig takes {@link + * PrepareModelConfig} instead of standalone configuration parameters, which allows vendor + * specific compilation metadata to be passed. + * + * @param model The model to be prepared for execution. + * @param config Configuration parameters to prepare the model. + * @param callback A callback object used to return the error status of preparing the model for + * execution and the prepared model if successful, nullptr otherwise. The + * callback object's notify function must be called exactly once, even if the + * model could not be prepared. + * @throws ServiceSpecificException with one of the following ErrorStatus values: + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if there is an unspecified error + * - INVALID_ARGUMENT if one of the input arguments related to preparing the model is + * invalid + * - MISSED_DEADLINE_* if the preparation is aborted because the model cannot be prepared by + * the deadline + * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver + */ + void prepareModelWithConfig( + in Model model, in PrepareModelConfig config, in IPreparedModelCallback callback); } diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl index 79053e527f..949804ea59 100644 --- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl +++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl @@ -18,6 +18,7 @@ package android.hardware.neuralnetworks; import android.hardware.common.NativeHandle; import android.hardware.neuralnetworks.ErrorStatus; +import android.hardware.neuralnetworks.ExecutionConfig; import android.hardware.neuralnetworks.ExecutionResult; import android.hardware.neuralnetworks.FencedExecutionResult; import android.hardware.neuralnetworks.IBurst; @@ -68,6 +69,8 @@ interface IPreparedModel { * Any number of calls to the execute* functions, in any combination, may be made concurrently, * even on the same IPreparedModel object. * + * Also see {@link IPreparedModel::executeSynchronouslyWithConfig}. + * * @param request The input and output information on which the prepared model is to be * executed. * @param measure Specifies whether or not to measure duration of the execution. The duration @@ -134,6 +137,8 @@ interface IPreparedModel { * Any number of calls to the execute* functions, in any combination, may be made concurrently, * even on the same IPreparedModel object. * + * Also see {@link IPreparedModel::executeFencedWithConfig}. + * * @param request The input and output information on which the prepared model is to be * executed. The outputs in the request must have fully specified dimensions. * @param waitFor A vector of sync fence file descriptors. Execution must not start until all @@ -201,15 +206,7 @@ interface IPreparedModel { * * @param request The input and output information on which the prepared model is to be * executed. - * @param measure Specifies whether or not to measure duration of the execution. - * @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent - * executing a {@link OperationType::WHILE} operation. If a loop - * condition model does not output false within this duration, the - * computation performed on the returned reusable execution object - * must be aborted. If -1 is provided, the maximum amount - * of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other - * negative values are invalid. When provided, the duration must - * not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}. + * @param config Specifies the execution configuration parameters. * @return execution An IExecution object representing a reusable execution that has been * specialized for a fixed request. * @throws ServiceSpecificException with one of the following ErrorStatus values: @@ -218,6 +215,64 @@ interface IPreparedModel { * - INVALID_ARGUMENT if one of the input arguments is invalid * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver */ - IExecution createReusableExecution( - in Request request, in boolean measureTiming, in long loopTimeoutDurationNs); + IExecution createReusableExecution(in Request request, in ExecutionConfig config); + + /** + * For detailed specification, please refer to {@link IPreparedModel::executeSynchronously}. The + * difference between the two methods is that executeSynchronouslyWithConfig takes {@link + * ExecutionConfig} instead of a list of configuration parameters, and ExecutionConfig contains + * more configuration parameters than are passed to executeSynchronously. + * + * @param request The input and output information on which the prepared model is to be + * executed. + * @param config Specifies the execution configuration parameters. + * @param deadlineNs The time by which the execution is expected to complete. The time is + * measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME, + * &ts) or ::android::base::boot_clock). If the execution cannot be finished + * by the deadline, the execution may be aborted. Passing -1 means the + * deadline is omitted. Other negative valueggs are invalid. + * @return ExecutionResult parcelable, containing the status of the execution, output shapes and + * timing information. + * - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the + * deadline + * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver + */ + ExecutionResult executeSynchronouslyWithConfig( + in Request request, in ExecutionConfig config, in long deadlineNs); + + /** + * For detailed specification, please refer to {@link IPreparedModel::executeFenced}. The + * difference between the two methods is that executeFencedWithConfig takes {@link + * ExecutionConfig} instead of a list of configuration parameters, and ExecutionConfig contains + * more configuration parameters than are passed to executeFenced. + * + * @param request The input and output information on which the prepared model is to be + * executed. The outputs in the request must have fully specified dimensions. + * @param waitFor A vector of sync fence file descriptors. Execution must not start until all + * sync fences have been signaled. + * @param config Specifies the execution configuration parameters. + * @param deadlineNs The time by which the execution is expected to complete. The time is + * measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME, + * &ts) or ::android::base::boot_clock). If the execution cannot be finished + * by the deadline, the execution may be aborted. Passing -1 means the + * deadline is omitted. Other negative values are invalid. + * @param durationNs The length of time in nanoseconds within which the execution is expected to + * complete after all sync fences in waitFor are signaled. If the execution + * cannot be finished within the duration, the execution may be aborted. + * Passing -1 means the duration is omitted. Other negative values are + * invalid. + * @return The FencedExecutionResult parcelable, containing IFencedExecutionCallback and the + * sync fence. + * @throws ServiceSpecificException with one of the following ErrorStatus values: + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if there is an unspecified error + * - INVALID_ARGUMENT if one of the input arguments is invalid, including fences in error + * states. + * - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the + * deadline + * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver + */ + FencedExecutionResult executeFencedWithConfig(in Request request, + in ParcelFileDescriptor[] waitFor, in ExecutionConfig config, in long deadlineNs, + in long durationNs); } diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/PrepareModelConfig.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/PrepareModelConfig.aidl new file mode 100644 index 0000000000..96df968464 --- /dev/null +++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/PrepareModelConfig.aidl @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 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.neuralnetworks; + +import android.hardware.neuralnetworks.ExecutionPreference; +import android.hardware.neuralnetworks.ExtensionNameAndPrefix; +import android.hardware.neuralnetworks.Priority; +import android.hardware.neuralnetworks.TokenValuePair; + +/** + * A type that is used to represent all configuration needed to + * prepare a model. + */ +@VintfStability +parcelable PrepareModelConfig { + /** + * Indicates the intended execution behavior of a prepared model. + */ + ExecutionPreference preference; + /** + * The priority of the prepared model relative to other prepared + * models owned by the client. + */ + Priority priority; + /** + * The time by which the model is expected to be prepared. The + * time is measured in nanoseconds since boot (as from + * clock_gettime(CLOCK_BOOTTIME, &ts) or + * ::android::base::boot_clock). If the model cannot be prepared + * by the deadline, the preparation may be aborted. Passing -1 + * means the deadline is omitted. Other negative values are + * invalid. + */ + long deadlineNs; + /** + * A vector of file descriptors for the security-sensitive cache. + * The length of the vector must either be 0 indicating that + * caching information is not provided, or match the + * numModelCache returned from IDevice::getNumberOfCacheFilesNeeded. The + * cache file descriptors will be provided in the same order when + * retrieving the preparedModel from cache files with + * IDevice::prepareModelFromCache. + */ + ParcelFileDescriptor[] modelCache; + /** + * A vector of file descriptors for the constants' cache. The + * length of the vector must either be 0 indicating that caching + * information is not provided, or match the numDataCache + * returned from IDevice::getNumberOfCacheFilesNeeded. The cache file + * descriptors will be provided in the same order when retrieving + * the preparedModel from cache files with IDevice::prepareModelFromCache. + */ + ParcelFileDescriptor[] dataCache; + /** + * A caching token of length IDevice::BYTE_SIZE_OF_CACHE_TOKEN identifying + * the prepared model. The same token will be provided when + * retrieving the prepared model from the cache files with + * IDevice::prepareModelFromCache. Tokens should be chosen to have a low + * rate of collision for a particular application. The driver + * cannot detect a collision; a collision will result in a failed + * execution or in a successful execution that produces incorrect + * output values. If both modelCache and dataCache are empty + * indicating that caching information is not provided, this + * token must be ignored. + */ + byte[] cacheToken; + /** + * A vector of token / value pairs represent vendor specific + * compilation hints or metadata. The provided TokenValuePairs must not + * contain the same token twice. The driver must validate the + * data and ignore invalid hints. It is up to the driver to + * decide whether to respect the provided hints or not. + */ + TokenValuePair[] compilationHints; + /** + * The mapping between extension names and prefixes of token values. + * The driver must ignore the corresponding compilation hint, if + * the extension is not supported. + */ + ExtensionNameAndPrefix[] extensionNameToPrefix; +} diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/TokenValuePair.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/TokenValuePair.aidl new file mode 100644 index 0000000000..ec665b4611 --- /dev/null +++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/TokenValuePair.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 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.neuralnetworks; + +/** + * A type that is used to represent a token / byte array data pair. + */ +@VintfStability +parcelable TokenValuePair { + /** + * A 32bit integer token. The token is created by combining the + * extension prefix and enum defined within the extension. + * The low {@link IDevice::EXTENSION_TYPE_LOW_BITS_TYPE} bits of the value + * correspond to the hint within the extension and the high + * {@link IDevice::EXTENSION_TYPE_HIGH_BITS_PREFIX} bits encode the "prefix", which maps + * uniquely to the extension name. The sign bit is always 0. + * + * For example, if a token value is 0x7AAA000B and the corresponding + * {@link ExtensionNameAndPrefix} contains an entry with prefix=0x7AAA and + * name="vendor.test.test_extension", then the token should be interpreted as the hint + * 0x000B of the extension named vendor.test.test_extension. + */ + int token; + /** + * A byte array containing the raw data. + */ + byte[] value; +} diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp index 3faa613514..9148eaccdf 100644 --- a/neuralnetworks/aidl/utils/Android.bp +++ b/neuralnetworks/aidl/utils/Android.bp @@ -111,19 +111,13 @@ cc_test { static_libs: [ "libaidlcommonsupport", "libgmock", - "libneuralnetworks_common", "neuralnetworks_types", "neuralnetworks_utils_hal_common", ], shared_libs: [ - "android.hidl.allocator@1.0", "libbase", "libbinder_ndk", "libcutils", - "libhidlbase", - "libhidlmemory", - "liblog", - "libutils", ], target: { android: { diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h index 0cc78d4f5e..f2e6e75818 100644 --- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h +++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h @@ -86,10 +86,12 @@ class Burst final : public nn::IBurst, public std::enable_shared_from_this<Burst GUARDED_BY(mMutex); }; + // featureLevel is for testing purposes. static nn::GeneralResult<std::shared_ptr<const Burst>> create( - std::shared_ptr<aidl_hal::IBurst> burst); + std::shared_ptr<aidl_hal::IBurst> burst, nn::Version featureLevel); - Burst(PrivateConstructorTag tag, std::shared_ptr<aidl_hal::IBurst> burst); + Burst(PrivateConstructorTag tag, std::shared_ptr<aidl_hal::IBurst> burst, + nn::Version featureLevel); // See IBurst::cacheMemory for information. OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override; @@ -97,23 +99,29 @@ class Burst final : public nn::IBurst, public std::enable_shared_from_this<Burst // See IBurst::execute for information. nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; // See IBurst::createReusableExecution for information. nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal( const aidl_hal::Request& request, const std::vector<int64_t>& memoryIdentifierTokens, bool measure, int64_t deadline, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, const hal::utils::RequestRelocation& relocation) const; private: mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT; const std::shared_ptr<aidl_hal::IBurst> kBurst; const std::shared_ptr<MemoryCache> kMemoryCache; + const nn::Version kFeatureLevel; }; } // namespace aidl::android::hardware::neuralnetworks::utils diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h index 477b311598..af587150b1 100644 --- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h +++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h @@ -46,6 +46,10 @@ #include <aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.h> #include <aidl/android/hardware/neuralnetworks/Timing.h> +#ifdef NN_AIDL_V4_OR_ABOVE +#include <aidl/android/hardware/neuralnetworks/TokenValuePair.h> +#endif // NN_AIDL_V4_OR_ABOVE + #include <android/binder_auto_utils.h> #include <nnapi/Result.h> #include <nnapi/Types.h> @@ -74,7 +78,7 @@ GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert( const aidl_hal::SymmPerChannelQuantParams& symmPerChannelQuantParams); GeneralResult<Operation> unvalidatedConvert(const aidl_hal::Operation& operation); GeneralResult<Model> unvalidatedConvert(const aidl_hal::Model& model); -GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert( +GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( const aidl_hal::ExtensionNameAndPrefix& extensionNameAndPrefix); GeneralResult<Model::OperandValues> unvalidatedConvert(const std::vector<uint8_t>& operandValues); GeneralResult<Model::Subgraph> unvalidatedConvert(const aidl_hal::Subgraph& subgraph); @@ -97,6 +101,10 @@ GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert( const aidl_hal::ExtensionOperandTypeInformation& operandTypeInformation); GeneralResult<SharedHandle> unvalidatedConvert(const ndk::ScopedFileDescriptor& handle); +#ifdef NN_AIDL_V4_OR_ABOVE +GeneralResult<TokenValuePair> unvalidatedConvert(const aidl_hal::TokenValuePair& tokenValuePair); +#endif // NN_AIDL_V4_OR_ABOVE + GeneralResult<std::vector<Operation>> unvalidatedConvert( const std::vector<aidl_hal::Operation>& operations); @@ -116,6 +124,14 @@ GeneralResult<BufferDesc> convert(const aidl_hal::BufferDesc& bufferDesc); GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extension>& extension); GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories); +GeneralResult<std::vector<ExtensionNameAndPrefix>> convert( + const std::vector<aidl_hal::ExtensionNameAndPrefix>& extensionNameAndPrefix); + +#ifdef NN_AIDL_V4_OR_ABOVE +GeneralResult<std::vector<TokenValuePair>> convert( + const std::vector<aidl_hal::TokenValuePair>& metaData); +#endif // NN_AIDL_V4_OR_ABOVE + GeneralResult<std::vector<OutputShape>> convert( const std::vector<aidl_hal::OutputShape>& outputShapes); GeneralResult<std::vector<SharedHandle>> convert( @@ -152,7 +168,7 @@ nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgra nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert( const nn::Model::OperandValues& operandValues); nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( - const nn::Model::ExtensionNameAndPrefix& extensionNameToPrefix); + const nn::ExtensionNameAndPrefix& extensionNameToPrefix); nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model); nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority); nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request); @@ -166,6 +182,10 @@ nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::Shared nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities); nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension); +#ifdef NN_AIDL_V4_OR_ABOVE +nn::GeneralResult<TokenValuePair> unvalidatedConvert(const nn::TokenValuePair& tokenValuePair); +#endif // NN_AIDL_V4_OR_ABOVE + nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken); nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc); nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType); @@ -190,6 +210,13 @@ nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert( nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert( const std::vector<nn::SyncFence>& syncFences); nn::GeneralResult<std::vector<Extension>> convert(const std::vector<nn::Extension>& extensions); +nn::GeneralResult<std::vector<ExtensionNameAndPrefix>> convert( + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix); + +#ifdef NN_AIDL_V4_OR_ABOVE +nn::GeneralResult<std::vector<TokenValuePair>> convert( + const std::vector<nn::TokenValuePair>& metaData); +#endif // NN_AIDL_V4_OR_ABOVE nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& vec); diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h index d558f66b6b..615c6deeba 100644 --- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h +++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h @@ -42,6 +42,7 @@ class Device final : public nn::IDevice { struct PrivateConstructorTag {}; public: + // featureLevel is for testing purposes. static nn::GeneralResult<std::shared_ptr<const Device>> create( std::string name, std::shared_ptr<aidl_hal::IDevice> device, nn::Version featureLevel); @@ -67,8 +68,9 @@ class Device final : public nn::IDevice { nn::GeneralResult<nn::SharedPreparedModel> prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, - const nn::CacheToken& token) const override; + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache( nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalInterfaces.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalInterfaces.h index 205d428cf4..cacdc2653f 100644 --- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalInterfaces.h +++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalInterfaces.h @@ -63,7 +63,9 @@ #ifdef NN_AIDL_V4_OR_ABOVE #include <aidl/android/hardware/neuralnetworks/BnExecution.h> +#include <aidl/android/hardware/neuralnetworks/ExecutionConfig.h> #include <aidl/android/hardware/neuralnetworks/IExecution.h> +#include <aidl/android/hardware/neuralnetworks/PrepareModelConfig.h> #endif // NN_AIDL_V4_OR_ABOVE namespace android::nn { diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h index e66507aa17..9375c1d327 100644 --- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h +++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h @@ -53,6 +53,9 @@ class InvalidDevice : public BnDevice { const std::vector<ndk::ScopedFileDescriptor>& dataCache, const std::vector<uint8_t>& token, const std::shared_ptr<IPreparedModelCallback>& callback) override; + ndk::ScopedAStatus prepareModelWithConfig( + const Model& model, const PrepareModelConfig& config, + const std::shared_ptr<IPreparedModelCallback>& callback) override; ndk::ScopedAStatus prepareModelFromCache( int64_t deadline, const std::vector<ndk::ScopedFileDescriptor>& modelCache, const std::vector<ndk::ScopedFileDescriptor>& dataCache, diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h index 24cd681658..cb6a85b85f 100644 --- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h +++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h @@ -40,6 +40,7 @@ class PreparedModel final : public nn::IPreparedModel, struct PrivateConstructorTag {}; public: + // featureLevel is for testing purposes. static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create( std::shared_ptr<aidl_hal::IPreparedModel> preparedModel, nn::Version featureLevel); @@ -49,18 +50,23 @@ class PreparedModel final : public nn::IPreparedModel, nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced( const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const override; + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override; @@ -68,6 +74,8 @@ class PreparedModel final : public nn::IPreparedModel, nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal( const Request& request, bool measure, int64_t deadline, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, const hal::utils::RequestRelocation& relocation) const; nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> @@ -75,6 +83,8 @@ class PreparedModel final : public nn::IPreparedModel, const std::vector<ndk::ScopedFileDescriptor>& waitFor, bool measure, int64_t deadline, int64_t loopTimeoutDuration, int64_t timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, const hal::utils::RequestRelocation& relocation) const; private: diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp index fb00b264e3..6c7aa882e8 100644 --- a/neuralnetworks/aidl/utils/src/Burst.cpp +++ b/neuralnetworks/aidl/utils/src/Burst.cpp @@ -43,12 +43,16 @@ class BurstExecution final : public nn::IExecution, static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create( std::shared_ptr<const Burst> burst, Request request, std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, hal::utils::RequestRelocation relocation, std::vector<Burst::OptionalCacheHold> cacheHolds); BurstExecution(PrivateConstructorTag tag, std::shared_ptr<const Burst> burst, Request request, std::vector<int64_t> memoryIdentifierTokens, bool measure, - int64_t loopTimeoutDuration, hal::utils::RequestRelocation relocation, + int64_t loopTimeoutDuration, const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, + hal::utils::RequestRelocation relocation, std::vector<Burst::OptionalCacheHold> cacheHolds); nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute( @@ -64,6 +68,8 @@ class BurstExecution final : public nn::IExecution, const std::vector<int64_t> kMemoryIdentifierTokens; const bool kMeasure; const int64_t kLoopTimeoutDuration; + const std::vector<nn::TokenValuePair> kHints; + const std::vector<nn::ExtensionNameAndPrefix> kExtensionNameToPrefix; const hal::utils::RequestRelocation kRelocation; const std::vector<Burst::OptionalCacheHold> kCacheHolds; }; @@ -149,17 +155,20 @@ void Burst::MemoryCache::tryFreeMemory(const nn::SharedMemory& memory, int64_t i } nn::GeneralResult<std::shared_ptr<const Burst>> Burst::create( - std::shared_ptr<aidl_hal::IBurst> burst) { + std::shared_ptr<aidl_hal::IBurst> burst, nn::Version featureLevel) { if (burst == nullptr) { return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "aidl_hal::utils::Burst::create must have non-null burst"; } - return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(burst)); + return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(burst), featureLevel); } -Burst::Burst(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBurst> burst) - : kBurst(std::move(burst)), kMemoryCache(std::make_shared<MemoryCache>(kBurst)) { +Burst::Burst(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBurst> burst, + nn::Version featureLevel) + : kBurst(std::move(burst)), + kMemoryCache(std::make_shared<MemoryCache>(kBurst)), + kFeatureLevel(featureLevel) { CHECK(kBurst != nullptr); } @@ -170,8 +179,9 @@ Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& memory) cons nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -200,14 +210,14 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: memoryIdentifierTokens.push_back(-1); } CHECK_EQ(requestInShared.pools.size(), memoryIdentifierTokens.size()); - return executeInternal(aidlRequest, memoryIdentifierTokens, aidlMeasure, aidlDeadline, - aidlLoopTimeoutDuration, relocation); + aidlLoopTimeoutDuration, hints, extensionNameToPrefix, relocation); } nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::executeInternal( const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, bool measure, - int64_t deadline, int64_t loopTimeoutDuration, + int64_t deadline, int64_t loopTimeoutDuration, const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, const hal::utils::RequestRelocation& relocation) const { // Ensure that at most one execution is in flight at any given time. const bool alreadyInFlight = mExecutionInFlight.test_and_set(); @@ -221,9 +231,21 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: } ExecutionResult executionResult; - const auto ret = kBurst->executeSynchronously(request, memoryIdentifierTokens, measure, - deadline, loopTimeoutDuration, &executionResult); - HANDLE_ASTATUS(ret) << "execute failed"; + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kBurst->executeSynchronouslyWithConfig( + request, memoryIdentifierTokens, + {measure, loopTimeoutDuration, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + deadline, &executionResult); + HANDLE_ASTATUS(ret) << "execute failed"; + } else { + const auto ret = + kBurst->executeSynchronously(request, memoryIdentifierTokens, measure, deadline, + loopTimeoutDuration, &executionResult); + HANDLE_ASTATUS(ret) << "execute failed"; + } if (!executionResult.outputSufficientSize) { auto canonicalOutputShapes = nn::convert(executionResult.outputShapes).value_or(std::vector<nn::OutputShape>{}); @@ -241,7 +263,9 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -272,12 +296,15 @@ nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution( return BurstExecution::create(shared_from_this(), std::move(aidlRequest), std::move(memoryIdentifierTokens), aidlMeasure, - aidlLoopTimeoutDuration, std::move(relocation), std::move(holds)); + aidlLoopTimeoutDuration, hints, extensionNameToPrefix, + std::move(relocation), std::move(holds)); } nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create( std::shared_ptr<const Burst> burst, Request request, std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, hal::utils::RequestRelocation relocation, std::vector<Burst::OptionalCacheHold> cacheHolds) { if (burst == nullptr) { @@ -286,13 +313,15 @@ nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create( return std::make_shared<const BurstExecution>( PrivateConstructorTag{}, std::move(burst), std::move(request), - std::move(memoryIdentifierTokens), measure, loopTimeoutDuration, std::move(relocation), - std::move(cacheHolds)); + std::move(memoryIdentifierTokens), measure, loopTimeoutDuration, hints, + extensionNameToPrefix, std::move(relocation), std::move(cacheHolds)); } BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/, std::shared_ptr<const Burst> burst, Request request, std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, hal::utils::RequestRelocation relocation, std::vector<Burst::OptionalCacheHold> cacheHolds) : kBurst(std::move(burst)), @@ -300,6 +329,8 @@ BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/, std::shared_ptr<co kMemoryIdentifierTokens(std::move(memoryIdentifierTokens)), kMeasure(measure), kLoopTimeoutDuration(loopTimeoutDuration), + kHints(hints), + kExtensionNameToPrefix(extensionNameToPrefix), kRelocation(std::move(relocation)), kCacheHolds(std::move(cacheHolds)) {} @@ -307,7 +338,8 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstEx const nn::OptionalTimePoint& deadline) const { const auto aidlDeadline = NN_TRY(convert(deadline)); return kBurst->executeInternal(kRequest, kMemoryIdentifierTokens, kMeasure, aidlDeadline, - kLoopTimeoutDuration, kRelocation); + kLoopTimeoutDuration, kHints, kExtensionNameToPrefix, + kRelocation); } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp index 113d2da955..eb28db7587 100644 --- a/neuralnetworks/aidl/utils/src/Conversions.cpp +++ b/neuralnetworks/aidl/utils/src/Conversions.cpp @@ -302,9 +302,9 @@ GeneralResult<Model::Subgraph> unvalidatedConvert(const aidl_hal::Subgraph& subg }; } -GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert( +GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( const aidl_hal::ExtensionNameAndPrefix& extensionNameAndPrefix) { - return Model::ExtensionNameAndPrefix{ + return ExtensionNameAndPrefix{ .name = extensionNameAndPrefix.name, .prefix = extensionNameAndPrefix.prefix, }; @@ -506,6 +506,12 @@ GeneralResult<SharedHandle> unvalidatedConvert(const ndk::ScopedFileDescriptor& return std::make_shared<const Handle>(std::move(duplicatedFd)); } +#ifdef NN_AIDL_V4_OR_ABOVE +GeneralResult<TokenValuePair> unvalidatedConvert(const aidl_hal::TokenValuePair& tokenValuePair) { + return TokenValuePair{.token = tokenValuePair.token, .value = tokenValuePair.value}; +} +#endif // NN_AIDL_V4_OR_ABOVE + GeneralResult<Capabilities> convert(const aidl_hal::Capabilities& capabilities) { return validatedConvert(capabilities); } @@ -562,6 +568,17 @@ GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extens GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories) { return validatedConvert(memories); } +GeneralResult<std::vector<ExtensionNameAndPrefix>> convert( + const std::vector<aidl_hal::ExtensionNameAndPrefix>& extensionNameAndPrefix) { + return unvalidatedConvert(extensionNameAndPrefix); +} + +#ifdef NN_AIDL_V4_OR_ABOVE +GeneralResult<std::vector<TokenValuePair>> convert( + const std::vector<aidl_hal::TokenValuePair>& metaData) { + return validatedConvert(metaData); +} +#endif // NN_AIDL_V4_OR_ABOVE GeneralResult<std::vector<OutputShape>> convert( const std::vector<aidl_hal::OutputShape>& outputShapes) { @@ -942,7 +959,7 @@ nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert( } nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( - const nn::Model::ExtensionNameAndPrefix& extensionNameToPrefix) { + const nn::ExtensionNameAndPrefix& extensionNameToPrefix) { return ExtensionNameAndPrefix{ .name = extensionNameToPrefix.name, .prefix = extensionNameToPrefix.prefix, @@ -1055,6 +1072,11 @@ nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension) return Extension{.name = extension.name, .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes))}; } +#ifdef NN_AIDL_V4_OR_ABOVE +nn::GeneralResult<TokenValuePair> unvalidatedConvert(const nn::TokenValuePair& tokenValuePair) { + return TokenValuePair{.token = tokenValuePair.token, .value = tokenValuePair.value}; +} +#endif // NN_AIDL_V4_OR_ABOVE nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken) { return validatedConvert(cacheToken); @@ -1134,6 +1156,17 @@ nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert( const std::vector<nn::SyncFence>& syncFences) { return validatedConvert(syncFences); } +nn::GeneralResult<std::vector<ExtensionNameAndPrefix>> convert( + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) { + return unvalidatedConvert(extensionNameToPrefix); +} + +#ifdef NN_AIDL_V4_OR_ABOVE +nn::GeneralResult<std::vector<TokenValuePair>> convert( + const std::vector<nn::TokenValuePair>& metaData) { + return validatedConvert(metaData); +} +#endif // NN_AIDL_V4_OR_ABOVE nn::GeneralResult<std::vector<Extension>> convert(const std::vector<nn::Extension>& extensions) { return validatedConvert(extensions); diff --git a/neuralnetworks/aidl/utils/src/Device.cpp b/neuralnetworks/aidl/utils/src/Device.cpp index bad10ed347..f3f4fdbba1 100644 --- a/neuralnetworks/aidl/utils/src/Device.cpp +++ b/neuralnetworks/aidl/utils/src/Device.cpp @@ -215,7 +215,9 @@ nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Mo nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const { + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that model is ready for IPC. std::optional<nn::Model> maybeModelInShared; const nn::Model& modelInShared = @@ -225,17 +227,28 @@ nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel( const auto aidlPreference = NN_TRY(convert(preference)); const auto aidlPriority = NN_TRY(convert(priority)); const auto aidlDeadline = NN_TRY(convert(deadline)); - const auto aidlModelCache = NN_TRY(convert(modelCache)); - const auto aidlDataCache = NN_TRY(convert(dataCache)); + auto aidlModelCache = NN_TRY(convert(modelCache)); + auto aidlDataCache = NN_TRY(convert(dataCache)); const auto aidlToken = NN_TRY(convert(token)); const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>(kFeatureLevel); const auto scoped = kDeathHandler.protectCallback(cb.get()); + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kDevice->prepareModelWithConfig( + aidlModel, + {aidlPreference, aidlPriority, aidlDeadline, std::move(aidlModelCache), + std::move(aidlDataCache), aidlToken, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + cb); + HANDLE_ASTATUS(ret) << "prepareModel failed"; + return cb->get(); + } const auto ret = kDevice->prepareModel(aidlModel, aidlPreference, aidlPriority, aidlDeadline, aidlModelCache, aidlDataCache, aidlToken, cb); HANDLE_ASTATUS(ret) << "prepareModel failed"; - return cb->get(); } diff --git a/neuralnetworks/aidl/utils/src/Execution.cpp b/neuralnetworks/aidl/utils/src/Execution.cpp index c4add636e5..2fd88aff36 100644 --- a/neuralnetworks/aidl/utils/src/Execution.cpp +++ b/neuralnetworks/aidl/utils/src/Execution.cpp @@ -63,7 +63,7 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> ExecutionWithCachedRequest::compute(const nn::OptionalTimePoint& deadline) const { const auto aidlDeadline = NN_TRY(convert(deadline)); return kPreparedModel->executeInternal(kRequest, kMeasure, aidlDeadline, kLoopTimeoutDuration, - kRelocation); + {}, {}, kRelocation); } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> @@ -73,9 +73,9 @@ ExecutionWithCachedRequest::computeFenced( const auto aidlWaitFor = NN_TRY(convert(waitFor)); const auto aidlDeadline = NN_TRY(convert(deadline)); const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence)); - return kPreparedModel->executeFencedInternal(kRequest, aidlWaitFor, kMeasure, aidlDeadline, - kLoopTimeoutDuration, - aidlTimeoutDurationAfterFence, kRelocation); + return kPreparedModel->executeFencedInternal( + kRequest, aidlWaitFor, kMeasure, aidlDeadline, kLoopTimeoutDuration, + aidlTimeoutDurationAfterFence, {}, {}, kRelocation); } nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create( diff --git a/neuralnetworks/aidl/utils/src/InvalidDevice.cpp b/neuralnetworks/aidl/utils/src/InvalidDevice.cpp index c9d995590f..33270ff381 100644 --- a/neuralnetworks/aidl/utils/src/InvalidDevice.cpp +++ b/neuralnetworks/aidl/utils/src/InvalidDevice.cpp @@ -167,6 +167,31 @@ ndk::ScopedAStatus InvalidDevice::prepareModel( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus InvalidDevice::prepareModelWithConfig( + const Model& model, const PrepareModelConfig& config, + const std::shared_ptr<IPreparedModelCallback>& callback) { + if (!utils::valid(config.extensionNameToPrefix)) { + callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); + return toAStatus(ErrorStatus::INVALID_ARGUMENT, "Invalid extensionNameToPrefix"); + } + for (const auto& hint : config.compilationHints) { + auto result = std::find_if(config.extensionNameToPrefix.begin(), + config.extensionNameToPrefix.end(), + [&hint](const ExtensionNameAndPrefix& extension) { + uint16_t prefix = static_cast<uint32_t>(hint.token) >> + IDevice::EXTENSION_TYPE_LOW_BITS_TYPE; + return prefix == extension.prefix; + }); + if (result == config.extensionNameToPrefix.end()) { + callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); + return toAStatus(ErrorStatus::INVALID_ARGUMENT, + "Invalid token for compilation hints: " + std::to_string(hint.token)); + } + } + return prepareModel(model, config.preference, config.priority, config.deadlineNs, + config.modelCache, config.dataCache, config.cacheToken, callback); +} + ndk::ScopedAStatus InvalidDevice::prepareModelFromCache( int64_t /*deadline*/, const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/, const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/, diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp index 6d1de569d0..7e3a31cac1 100644 --- a/neuralnetworks/aidl/utils/src/PreparedModel.cpp +++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp @@ -128,8 +128,9 @@ PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -141,30 +142,46 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Prepare const auto aidlMeasure = NN_TRY(convert(measure)); const auto aidlDeadline = NN_TRY(convert(deadline)); const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration)); - return executeInternal(aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration, - relocation); + return executeInternal(aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration, hints, + extensionNameToPrefix, relocation); } nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::executeInternal(const Request& request, bool measure, int64_t deadline, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, const hal::utils::RequestRelocation& relocation) const { if (relocation.input) { relocation.input->flush(); } ExecutionResult executionResult; - const auto ret = kPreparedModel->executeSynchronously(request, measure, deadline, - loopTimeoutDuration, &executionResult); - HANDLE_ASTATUS(ret) << "executeSynchronously failed"; + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kPreparedModel->executeSynchronouslyWithConfig( + request, + {measure, loopTimeoutDuration, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + deadline, &executionResult); + HANDLE_ASTATUS(ret) << "executeSynchronouslyWithConfig failed"; + } else { + const auto ret = kPreparedModel->executeSynchronously( + request, measure, deadline, loopTimeoutDuration, &executionResult); + HANDLE_ASTATUS(ret) << "executeSynchronously failed"; + } return handleExecutionResult(executionResult, relocation); } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> -PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, - nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const { +PreparedModel::executeFenced( + const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, + nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, + const nn::OptionalDuration& loopTimeoutDuration, + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -179,31 +196,45 @@ PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::S const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration)); const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence)); return executeFencedInternal(aidlRequest, aidlWaitFor, aidlMeasure, aidlDeadline, - aidlLoopTimeoutDuration, aidlTimeoutDurationAfterFence, - relocation); + aidlLoopTimeoutDuration, aidlTimeoutDurationAfterFence, hints, + extensionNameToPrefix, relocation); } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> -PreparedModel::executeFencedInternal(const Request& request, - const std::vector<ndk::ScopedFileDescriptor>& waitFor, - bool measure, int64_t deadline, int64_t loopTimeoutDuration, - int64_t timeoutDurationAfterFence, - const hal::utils::RequestRelocation& relocation) const { +PreparedModel::executeFencedInternal( + const Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor, bool measure, + int64_t deadline, int64_t loopTimeoutDuration, int64_t timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, + const hal::utils::RequestRelocation& relocation) const { if (relocation.input) { relocation.input->flush(); } FencedExecutionResult result; - const auto ret = - kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration, - timeoutDurationAfterFence, &result); - HANDLE_ASTATUS(ret) << "executeFenced failed"; + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kPreparedModel->executeFencedWithConfig( + request, waitFor, + {measure, loopTimeoutDuration, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + deadline, timeoutDurationAfterFence, &result); + HANDLE_ASTATUS(ret) << "executeFencedWithConfig failed"; + } else { + const auto ret = kPreparedModel->executeFenced(request, waitFor, measure, deadline, + loopTimeoutDuration, + timeoutDurationAfterFence, &result); + HANDLE_ASTATUS(ret) << "executeFenced failed"; + } return handleFencedExecutionResult(result, relocation); } nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -217,8 +248,14 @@ nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution( if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { std::shared_ptr<IExecution> execution; + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kPreparedModel->createReusableExecution( - aidlRequest, aidlMeasure, aidlLoopTimeoutDuration, &execution); + aidlRequest, + {aidlMeasure, aidlLoopTimeoutDuration, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + &execution); HANDLE_ASTATUS(ret) << "createReusableExecution failed"; return Execution::create(std::move(execution), std::move(relocation)); } @@ -232,7 +269,7 @@ nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() cons std::shared_ptr<IBurst> burst; const auto ret = kPreparedModel->configureExecutionBurst(&burst); HANDLE_ASTATUS(ret) << "configureExecutionBurst failed"; - return Burst::create(std::move(burst)); + return Burst::create(std::move(burst), kFeatureLevel); } std::any PreparedModel::getUnderlyingResource() const { diff --git a/neuralnetworks/aidl/utils/test/DeviceTest.cpp b/neuralnetworks/aidl/utils/test/DeviceTest.cpp index fb13af8d9f..73727b3974 100644 --- a/neuralnetworks/aidl/utils/test/DeviceTest.cpp +++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp @@ -61,7 +61,6 @@ constexpr PerformanceInfo kNoPerformanceInfo = {.execTime = std::numeric_limits< .powerUsage = std::numeric_limits<float>::max()}; constexpr NumberOfCacheFiles kNumberOfCacheFiles = {.numModelCache = nn::kMaxNumberOfCacheFiles - 1, .numDataCache = nn::kMaxNumberOfCacheFiles}; - constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); }; std::shared_ptr<MockDevice> createMockDevice() { @@ -124,6 +123,18 @@ auto makePreparedModelReturn(ErrorStatus launchStatus, ErrorStatus returnStatus, }; } +const std::vector<nn::TokenValuePair> kHints = {nn::TokenValuePair{.token = 0, .value = {1}}}; +const std::vector<nn::ExtensionNameAndPrefix> kExtensionNameToPrefix = { + nn::ExtensionNameAndPrefix{.name = "com.android.nn_test", .prefix = 1}}; +auto makePreparedModelWithConfigReturn(ErrorStatus launchStatus, ErrorStatus returnStatus, + const std::shared_ptr<MockPreparedModel>& preparedModel) { + return [launchStatus, returnStatus, preparedModel]( + const Model& /*model*/, const PrepareModelConfig& /*config*/, + const std::shared_ptr<IPreparedModelCallback>& cb) -> ndk::ScopedAStatus { + return makePreparedModelReturnImpl(launchStatus, returnStatus, preparedModel, cb); + }; +} + auto makePreparedModelFromCacheReturn(ErrorStatus launchStatus, ErrorStatus returnStatus, const std::shared_ptr<MockPreparedModel>& preparedModel) { return [launchStatus, returnStatus, preparedModel]( @@ -560,6 +571,8 @@ TEST_P(DeviceTest, getSupportedOperationsDeadObject) { } TEST_P(DeviceTest, prepareModel) { + if (kVersion.level > nn::Version::Level::FEATURE_LEVEL_7) return; + // setup call const auto mockDevice = createMockDevice(); const auto device = Device::create(kName, mockDevice, kVersion).value(); @@ -571,7 +584,7 @@ TEST_P(DeviceTest, prepareModel) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -580,6 +593,8 @@ TEST_P(DeviceTest, prepareModel) { } TEST_P(DeviceTest, prepareModelLaunchError) { + if (kVersion.level > nn::Version::Level::FEATURE_LEVEL_7) return; + // setup call const auto mockDevice = createMockDevice(); const auto device = Device::create(kName, mockDevice, kVersion).value(); @@ -590,7 +605,7 @@ TEST_P(DeviceTest, prepareModelLaunchError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -598,6 +613,8 @@ TEST_P(DeviceTest, prepareModelLaunchError) { } TEST_P(DeviceTest, prepareModelReturnError) { + if (kVersion.level > nn::Version::Level::FEATURE_LEVEL_7) return; + // setup call const auto mockDevice = createMockDevice(); const auto device = Device::create(kName, mockDevice, kVersion).value(); @@ -608,7 +625,7 @@ TEST_P(DeviceTest, prepareModelReturnError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -616,6 +633,8 @@ TEST_P(DeviceTest, prepareModelReturnError) { } TEST_P(DeviceTest, prepareModelNullptrError) { + if (kVersion.level > nn::Version::Level::FEATURE_LEVEL_7) return; + // setup call const auto mockDevice = createMockDevice(); const auto device = Device::create(kName, mockDevice, kVersion).value(); @@ -626,7 +645,7 @@ TEST_P(DeviceTest, prepareModelNullptrError) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -634,6 +653,8 @@ TEST_P(DeviceTest, prepareModelNullptrError) { } TEST_P(DeviceTest, prepareModelTransportFailure) { + if (kVersion.level > nn::Version::Level::FEATURE_LEVEL_7) return; + // setup call const auto mockDevice = createMockDevice(); const auto device = Device::create(kName, mockDevice, kVersion).value(); @@ -643,7 +664,7 @@ TEST_P(DeviceTest, prepareModelTransportFailure) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -651,6 +672,8 @@ TEST_P(DeviceTest, prepareModelTransportFailure) { } TEST_P(DeviceTest, prepareModelDeadObject) { + if (kVersion.level > nn::Version::Level::FEATURE_LEVEL_7) return; + // setup call const auto mockDevice = createMockDevice(); const auto device = Device::create(kName, mockDevice, kVersion).value(); @@ -660,7 +683,7 @@ TEST_P(DeviceTest, prepareModelDeadObject) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -668,6 +691,8 @@ TEST_P(DeviceTest, prepareModelDeadObject) { } TEST_P(DeviceTest, prepareModelAsyncCrash) { + if (kVersion.level > nn::Version::Level::FEATURE_LEVEL_7) return; + // setup test const auto mockDevice = createMockDevice(); const auto device = Device::create(kName, mockDevice, kVersion).value(); @@ -681,7 +706,157 @@ TEST_P(DeviceTest, prepareModelAsyncCrash) { // run test const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT); +} + +TEST_P(DeviceTest, prepareModelWithConfig) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockDevice = createMockDevice(); + const auto device = Device::create(kName, mockDevice, kVersion).value(); + const auto mockPreparedModel = MockPreparedModel::create(); + EXPECT_CALL(*mockDevice, prepareModelWithConfig(_, _, _)) + .Times(1) + .WillOnce(Invoke(makePreparedModelWithConfigReturn(ErrorStatus::NONE, ErrorStatus::NONE, + mockPreparedModel))); + + // run test + const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, + nn::Priority::DEFAULT, {}, {}, {}, {}, kHints, + kExtensionNameToPrefix); + + // verify result + ASSERT_TRUE(result.has_value()) + << "Failed with " << result.error().code << ": " << result.error().message; + EXPECT_NE(result.value(), nullptr); +} + +TEST_P(DeviceTest, prepareModelWithConfigLaunchError) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockDevice = createMockDevice(); + const auto device = Device::create(kName, mockDevice, kVersion).value(); + EXPECT_CALL(*mockDevice, prepareModelWithConfig(_, _, _)) + .Times(1) + .WillOnce(Invoke(makePreparedModelWithConfigReturn( + ErrorStatus::GENERAL_FAILURE, ErrorStatus::GENERAL_FAILURE, nullptr))); + + // run test + const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, + nn::Priority::DEFAULT, {}, {}, {}, {}, kHints, + kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(DeviceTest, prepareModelWithConfigReturnError) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockDevice = createMockDevice(); + const auto device = Device::create(kName, mockDevice, kVersion).value(); + EXPECT_CALL(*mockDevice, prepareModelWithConfig(_, _, _)) + .Times(1) + .WillOnce(Invoke(makePreparedModelWithConfigReturn( + ErrorStatus::NONE, ErrorStatus::GENERAL_FAILURE, nullptr))); + + // run test + const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, + nn::Priority::DEFAULT, {}, {}, {}, {}, kHints, + kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(DeviceTest, prepareModelWithConfigNullptrError) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockDevice = createMockDevice(); + const auto device = Device::create(kName, mockDevice, kVersion).value(); + EXPECT_CALL(*mockDevice, prepareModelWithConfig(_, _, _)) + .Times(1) + .WillOnce(Invoke(makePreparedModelWithConfigReturn(ErrorStatus::NONE, ErrorStatus::NONE, + nullptr))); + + // run test + const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, + nn::Priority::DEFAULT, {}, {}, {}, {}, kHints, + kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(DeviceTest, prepareModelWithConfigTransportFailure) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockDevice = createMockDevice(); + const auto device = Device::create(kName, mockDevice, kVersion).value(); + EXPECT_CALL(*mockDevice, prepareModelWithConfig(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, + nn::Priority::DEFAULT, {}, {}, {}, {}, kHints, + kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(DeviceTest, prepareModelWithConfigDeadObject) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockDevice = createMockDevice(); + const auto device = Device::create(kName, mockDevice, kVersion).value(); + EXPECT_CALL(*mockDevice, prepareModelWithConfig(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, + nn::Priority::DEFAULT, {}, {}, {}, {}, kHints, + kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT); +} + +TEST_P(DeviceTest, prepareModelWithConfigAsyncCrash) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup test + const auto mockDevice = createMockDevice(); + const auto device = Device::create(kName, mockDevice, kVersion).value(); + const auto ret = [&device]() { + DeathMonitor::serviceDied(device->getDeathMonitor()); + return ndk::ScopedAStatus::ok(); + }; + EXPECT_CALL(*mockDevice, prepareModelWithConfig(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(ret)); + + // run test + const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT, + nn::Priority::DEFAULT, {}, {}, {}, {}, kHints, + kExtensionNameToPrefix); // verify result ASSERT_FALSE(result.has_value()); diff --git a/neuralnetworks/aidl/utils/test/MockBuffer.h b/neuralnetworks/aidl/utils/test/MockBuffer.h index f77fa86953..7a05a0f33f 100644 --- a/neuralnetworks/aidl/utils/test/MockBuffer.h +++ b/neuralnetworks/aidl/utils/test/MockBuffer.h @@ -21,7 +21,6 @@ #include <android/binder_interface_utils.h> #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <hidl/Status.h> namespace aidl::android::hardware::neuralnetworks::utils { diff --git a/neuralnetworks/aidl/utils/test/MockBurst.h b/neuralnetworks/aidl/utils/test/MockBurst.h index 5083bbdc86..609bd305b3 100644 --- a/neuralnetworks/aidl/utils/test/MockBurst.h +++ b/neuralnetworks/aidl/utils/test/MockBurst.h @@ -21,7 +21,6 @@ #include <android/binder_interface_utils.h> #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <hidl/Status.h> namespace aidl::android::hardware::neuralnetworks::utils { @@ -32,6 +31,10 @@ class MockBurst final : public BnBurst { bool measureTiming, int64_t deadline, int64_t loopTimeoutDuration, ExecutionResult* executionResult), (override)); + MOCK_METHOD(ndk::ScopedAStatus, executeSynchronouslyWithConfig, + (const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, + const ExecutionConfig& config, int64_t deadline, ExecutionResult* executionResult), + (override)); MOCK_METHOD(ndk::ScopedAStatus, releaseMemoryResource, (int64_t memoryIdentifierToken), (override)); }; diff --git a/neuralnetworks/aidl/utils/test/MockDevice.h b/neuralnetworks/aidl/utils/test/MockDevice.h index 3a28d55580..47b83460a1 100644 --- a/neuralnetworks/aidl/utils/test/MockDevice.h +++ b/neuralnetworks/aidl/utils/test/MockDevice.h @@ -50,6 +50,10 @@ class MockDevice final : public BnDevice { const std::vector<uint8_t>& token, const std::shared_ptr<IPreparedModelCallback>& callback), (override)); + MOCK_METHOD(ndk::ScopedAStatus, prepareModelWithConfig, + (const Model& model, const PrepareModelConfig& config, + const std::shared_ptr<IPreparedModelCallback>& callback), + (override)); MOCK_METHOD(ndk::ScopedAStatus, prepareModelFromCache, (int64_t deadline, const std::vector<ndk::ScopedFileDescriptor>& modelCache, const std::vector<ndk::ScopedFileDescriptor>& dataCache, diff --git a/neuralnetworks/aidl/utils/test/MockExecution.h b/neuralnetworks/aidl/utils/test/MockExecution.h index 216f569abc..782e54f874 100644 --- a/neuralnetworks/aidl/utils/test/MockExecution.h +++ b/neuralnetworks/aidl/utils/test/MockExecution.h @@ -21,8 +21,6 @@ #include <android/binder_interface_utils.h> #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <hidl/HidlSupport.h> -#include <hidl/Status.h> namespace aidl::android::hardware::neuralnetworks::utils { diff --git a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h index 06f9ea2e41..29449bb88b 100644 --- a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h +++ b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h @@ -22,7 +22,6 @@ #include <android/binder_interface_utils.h> #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <hidl/Status.h> namespace aidl::android::hardware::neuralnetworks::utils { diff --git a/neuralnetworks/aidl/utils/test/MockPreparedModel.h b/neuralnetworks/aidl/utils/test/MockPreparedModel.h index 0ed9af9929..a5b3b66802 100644 --- a/neuralnetworks/aidl/utils/test/MockPreparedModel.h +++ b/neuralnetworks/aidl/utils/test/MockPreparedModel.h @@ -22,8 +22,6 @@ #include <android/binder_interface_utils.h> #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <hidl/HidlSupport.h> -#include <hidl/Status.h> namespace aidl::android::hardware::neuralnetworks::utils { @@ -40,10 +38,19 @@ class MockPreparedModel final : public BnPreparedModel { bool measureTiming, int64_t deadline, int64_t loopTimeoutDuration, int64_t duration, FencedExecutionResult* fencedExecutionResult), (override)); + MOCK_METHOD(ndk::ScopedAStatus, executeSynchronouslyWithConfig, + (const Request& request, const ExecutionConfig& config, int64_t deadline, + ExecutionResult* executionResult), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, executeFencedWithConfig, + (const Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor, + const ExecutionConfig& config, int64_t deadline, int64_t duration, + FencedExecutionResult* fencedExecutionResult), + (override)); MOCK_METHOD(ndk::ScopedAStatus, configureExecutionBurst, (std::shared_ptr<IBurst> * burst), (override)); MOCK_METHOD(ndk::ScopedAStatus, createReusableExecution, - (const Request& request, bool measureTiming, int64_t loopTimeoutDuration, + (const Request& request, const ExecutionConfig& config, std::shared_ptr<IExecution>* execution), (override)); }; diff --git a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp index 8cfb7c123a..bf6136dabb 100644 --- a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp +++ b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp @@ -70,6 +70,21 @@ auto makeFencedExecutionResult(const std::shared_ptr<MockFencedExecutionCallback class PreparedModelTest : public VersionedAidlUtilsTestBase {}; +const std::vector<nn::TokenValuePair> kHints = {nn::TokenValuePair{.token = 0, .value = {1}}}; +const std::vector<nn::ExtensionNameAndPrefix> kExtensionNameToPrefix = { + nn::ExtensionNameAndPrefix{.name = "com.android.nn_test", .prefix = 1}}; +auto makeFencedExecutionWithConfigResult( + const std::shared_ptr<MockFencedExecutionCallback>& callback) { + return [callback](const Request& /*request*/, + const std::vector<ndk::ScopedFileDescriptor>& /*waitFor*/, + const ExecutionConfig& /*config*/, int64_t /*deadline*/, int64_t /*duration*/, + FencedExecutionResult* fencedExecutionResult) { + *fencedExecutionResult = FencedExecutionResult{.callback = callback, + .syncFence = ndk::ScopedFileDescriptor(-1)}; + return ndk::ScopedAStatus::ok(); + }; +} + } // namespace TEST_P(PreparedModelTest, invalidPreparedModel) { @@ -82,6 +97,8 @@ TEST_P(PreparedModelTest, invalidPreparedModel) { } TEST_P(PreparedModelTest, executeSync) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup call const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -96,7 +113,7 @@ TEST_P(PreparedModelTest, executeSync) { DoAll(SetArgPointee<4>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk))); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result EXPECT_TRUE(result.has_value()) @@ -104,6 +121,8 @@ TEST_P(PreparedModelTest, executeSync) { } TEST_P(PreparedModelTest, executeSyncError) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup test const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -112,7 +131,7 @@ TEST_P(PreparedModelTest, executeSyncError) { .WillOnce(Invoke(makeGeneralFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -120,6 +139,8 @@ TEST_P(PreparedModelTest, executeSyncError) { } TEST_P(PreparedModelTest, executeSyncTransportFailure) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup test const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -128,7 +149,7 @@ TEST_P(PreparedModelTest, executeSyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -136,6 +157,8 @@ TEST_P(PreparedModelTest, executeSyncTransportFailure) { } TEST_P(PreparedModelTest, executeSyncDeadObject) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup test const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -144,7 +167,7 @@ TEST_P(PreparedModelTest, executeSyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -152,6 +175,8 @@ TEST_P(PreparedModelTest, executeSyncDeadObject) { } TEST_P(PreparedModelTest, executeFenced) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup call const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -165,7 +190,7 @@ TEST_P(PreparedModelTest, executeFenced) { .WillOnce(Invoke(makeFencedExecutionResult(mockCallback))); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -181,6 +206,8 @@ TEST_P(PreparedModelTest, executeFenced) { } TEST_P(PreparedModelTest, executeFencedCallbackError) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup call const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -195,7 +222,7 @@ TEST_P(PreparedModelTest, executeFencedCallbackError) { .WillOnce(Invoke(makeFencedExecutionResult(mockCallback))); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -211,6 +238,8 @@ TEST_P(PreparedModelTest, executeFencedCallbackError) { } TEST_P(PreparedModelTest, executeFencedError) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup test const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -219,7 +248,7 @@ TEST_P(PreparedModelTest, executeFencedError) { .WillOnce(InvokeWithoutArgs(makeGeneralFailure)); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -227,6 +256,8 @@ TEST_P(PreparedModelTest, executeFencedError) { } TEST_P(PreparedModelTest, executeFencedTransportFailure) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup test const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -235,7 +266,7 @@ TEST_P(PreparedModelTest, executeFencedTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -243,6 +274,8 @@ TEST_P(PreparedModelTest, executeFencedTransportFailure) { } TEST_P(PreparedModelTest, executeFencedDeadObject) { + if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return; + // setup test const auto mockPreparedModel = MockPreparedModel::create(); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); @@ -251,7 +284,7 @@ TEST_P(PreparedModelTest, executeFencedDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -276,7 +309,7 @@ TEST_P(PreparedModelTest, reusableExecuteSync) { DoAll(SetArgPointee<4>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -300,7 +333,7 @@ TEST_P(PreparedModelTest, reusableExecuteSyncError) { .WillOnce(Invoke(makeGeneralFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -322,7 +355,7 @@ TEST_P(PreparedModelTest, reusableExecuteSyncTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -344,7 +377,7 @@ TEST_P(PreparedModelTest, reusableExecuteSyncDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -372,7 +405,7 @@ TEST_P(PreparedModelTest, reusableExecuteFenced) { .WillRepeatedly(Invoke(makeFencedExecutionResult(mockCallback))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -410,7 +443,7 @@ TEST_P(PreparedModelTest, reusableExecuteFencedCallbackError) { .WillOnce(Invoke(makeFencedExecutionResult(mockCallback))); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -440,7 +473,7 @@ TEST_P(PreparedModelTest, reusableExecuteFencedError) { .WillOnce(InvokeWithoutArgs(makeGeneralFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -462,7 +495,7 @@ TEST_P(PreparedModelTest, reusableExecuteFencedTransportFailure) { .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -484,7 +517,7 @@ TEST_P(PreparedModelTest, reusableExecuteFencedDeadObject) { .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); // create execution - const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + const auto createResult = preparedModel->createReusableExecution({}, {}, {}, {}, {}); ASSERT_TRUE(createResult.has_value()) << "Failed with " << createResult.error().code << ": " << createResult.error().message; ASSERT_NE(createResult.value(), nullptr); @@ -495,6 +528,206 @@ TEST_P(PreparedModelTest, reusableExecuteFencedDeadObject) { EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT); } +TEST_P(PreparedModelTest, executeSyncWithConfig) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + const auto mockExecutionResult = ExecutionResult{ + .outputSufficientSize = true, + .outputShapes = {}, + .timing = kNoTiming, + }; + EXPECT_CALL(*mockPreparedModel, executeSynchronouslyWithConfig(_, _, _, _)) + .Times(1) + .WillOnce( + DoAll(SetArgPointee<3>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk))); + + // run test + const auto result = preparedModel->execute({}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + EXPECT_TRUE(result.has_value()) + << "Failed with " << result.error().code << ": " << result.error().message; +} + +TEST_P(PreparedModelTest, executeSyncWithConfigError) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup test + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + EXPECT_CALL(*mockPreparedModel, executeSynchronouslyWithConfig(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(makeGeneralFailure)); + + // run test + const auto result = preparedModel->execute({}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(PreparedModelTest, executeSyncWithConfigTransportFailure) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup test + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + EXPECT_CALL(*mockPreparedModel, executeSynchronouslyWithConfig(_, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto result = preparedModel->execute({}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(PreparedModelTest, executeSyncWithConfigDeadObject) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup test + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + EXPECT_CALL(*mockPreparedModel, executeSynchronouslyWithConfig(_, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto result = preparedModel->execute({}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT); +} + +TEST_P(PreparedModelTest, executeFencedWithConfig) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + const auto mockCallback = MockFencedExecutionCallback::create(); + EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _)) + .Times(1) + .WillOnce(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming), + SetArgPointee<2>(ErrorStatus::NONE), Invoke(makeStatusOk))); + EXPECT_CALL(*mockPreparedModel, executeFencedWithConfig(_, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(makeFencedExecutionWithConfigResult(mockCallback))); + + // run test + const auto result = + preparedModel->executeFenced({}, {}, {}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + ASSERT_TRUE(result.has_value()) + << "Failed with " << result.error().code << ": " << result.error().message; + const auto& [syncFence, callback] = result.value(); + EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED); + ASSERT_NE(callback, nullptr); + + // get results from callback + const auto callbackResult = callback(); + ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code << ": " + << callbackResult.error().message; +} + +TEST_P(PreparedModelTest, executeFencedWithConfigCallbackError) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup call + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + const auto mockCallback = MockFencedExecutionCallback::create(); + EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _)) + .Times(1) + .WillOnce(Invoke(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming), + SetArgPointee<2>(ErrorStatus::GENERAL_FAILURE), + Invoke(makeStatusOk)))); + EXPECT_CALL(*mockPreparedModel, executeFencedWithConfig(_, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(makeFencedExecutionWithConfigResult(mockCallback))); + + // run test + const auto result = + preparedModel->executeFenced({}, {}, {}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + ASSERT_TRUE(result.has_value()) + << "Failed with " << result.error().code << ": " << result.error().message; + const auto& [syncFence, callback] = result.value(); + EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE); + ASSERT_NE(callback, nullptr); + + // verify callback failure + const auto callbackResult = callback(); + ASSERT_FALSE(callbackResult.has_value()); + EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(PreparedModelTest, executeFencedWithConfigError) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup test + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + EXPECT_CALL(*mockPreparedModel, executeFencedWithConfig(_, _, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralFailure)); + + // run test + const auto result = + preparedModel->executeFenced({}, {}, {}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(PreparedModelTest, executeFencedWithConfigTransportFailure) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup test + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + EXPECT_CALL(*mockPreparedModel, executeFencedWithConfig(_, _, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto result = + preparedModel->executeFenced({}, {}, {}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST_P(PreparedModelTest, executeFencedWithConfigDeadObject) { + if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return; + + // setup test + const auto mockPreparedModel = MockPreparedModel::create(); + const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); + EXPECT_CALL(*mockPreparedModel, executeFencedWithConfig(_, _, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto result = + preparedModel->executeFenced({}, {}, {}, {}, {}, {}, kHints, kExtensionNameToPrefix); + + // verify result + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT); +} + TEST_P(PreparedModelTest, configureExecutionBurst) { // setup test const auto mockPreparedModel = MockPreparedModel::create(); @@ -567,13 +800,13 @@ TEST_P(PreparedModelTest, createReusableExecution) { // setup test const auto mockPreparedModel = MockPreparedModel::create(); const auto mockExecution = ndk::SharedRefBase::make<MockExecution>(); - EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _)) + EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _)) .Times(1) - .WillOnce(DoAll(SetArgPointee<3>(mockExecution), Invoke(makeStatusOk))); + .WillOnce(DoAll(SetArgPointee<2>(mockExecution), Invoke(makeStatusOk))); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); // run test - const auto result = preparedModel->createReusableExecution({}, {}, {}); + const auto result = preparedModel->createReusableExecution({}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -586,13 +819,13 @@ TEST_P(PreparedModelTest, createReusableExecutionError) { // setup test const auto mockPreparedModel = MockPreparedModel::create(); - EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _)) + EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _)) .Times(1) .WillOnce(InvokeWithoutArgs(makeGeneralFailure)); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); // run test - const auto result = preparedModel->createReusableExecution({}, {}, {}); + const auto result = preparedModel->createReusableExecution({}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -604,13 +837,13 @@ TEST_P(PreparedModelTest, createReusableExecutionTransportFailure) { // setup test const auto mockPreparedModel = MockPreparedModel::create(); - EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _)) + EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _)) .Times(1) .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); // run test - const auto result = preparedModel->createReusableExecution({}, {}, {}); + const auto result = preparedModel->createReusableExecution({}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -622,13 +855,13 @@ TEST_P(PreparedModelTest, createReusableExecutionDeadObject) { // setup test const auto mockPreparedModel = MockPreparedModel::create(); - EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _)) + EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _)) .Times(1) .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value(); // run test - const auto result = preparedModel->createReusableExecution({}, {}, {}); + const auto result = preparedModel->createReusableExecution({}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); diff --git a/neuralnetworks/aidl/vts/functional/Android.bp b/neuralnetworks/aidl/vts/functional/Android.bp index 1ed15b82a7..356cdb0956 100644 --- a/neuralnetworks/aidl/vts/functional/Android.bp +++ b/neuralnetworks/aidl/vts/functional/Android.bp @@ -30,6 +30,7 @@ cc_test { "neuralnetworks_vts_functional_defaults", "use_libaidlvintf_gtest_helper_static", ], + host_supported: true, srcs: [ "BasicTests.cpp", "Callbacks.cpp", @@ -46,18 +47,11 @@ cc_test { ], shared_libs: [ "libbinder_ndk", - "libnativewindow", - "libvndksupport", ], static_libs: [ - "android.hidl.allocator@1.0", - "android.hidl.memory@1.0", "libaidlcommonsupport", - "libgmock", - "libhidlmemory", "libneuralnetworks_common", "libneuralnetworks_generated_test_harness", - "libsync", ], whole_static_libs: [ "neuralnetworks_generated_AIDL_V3_example", @@ -73,6 +67,34 @@ cc_test { ], test_suites: [ "general-tests", - "vts", ], + target: { + android: { + shared_libs: [ + "libnativewindow", + "libvndksupport", + ], + static_libs: [ + "libsync", + ], + test_suites: [ + "vts", + ], + test_config: "AndroidTestDevice.xml", + }, + host: { + shared_libs: [ + "libtextclassifier_hash", + ], + static_libs: [ + "neuralnetworks_canonical_sample_driver", + "neuralnetworks_utils_hal_adapter_aidl", + ], + exclude_static_libs: [ + "VtsHalHidlTestUtils", + "libaidlvintf_gtest_helper", + ], + test_config: "AndroidTestHost.xml", + }, + }, } diff --git a/neuralnetworks/aidl/vts/functional/AndroidTest.xml b/neuralnetworks/aidl/vts/functional/AndroidTestDevice.xml index 384d42078f..384d42078f 100644 --- a/neuralnetworks/aidl/vts/functional/AndroidTest.xml +++ b/neuralnetworks/aidl/vts/functional/AndroidTestDevice.xml diff --git a/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml b/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml new file mode 100644 index 0000000000..7372a3148c --- /dev/null +++ b/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs VtsHalNeuralnetworksTargetTest."> + <test class="com.android.tradefed.testtype.HostGTest" > + <option name="module-name" value="VtsHalNeuralnetworksTargetTest" /> + <option name="native-test-timeout" value="15m" /> + </test> +</configuration> + diff --git a/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp index 77208aaf87..7451f7eec3 100644 --- a/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp +++ b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp @@ -23,7 +23,6 @@ #include <fcntl.h> #include <ftw.h> #include <gtest/gtest.h> -#include <hidlmemory/mapping.h> #include <unistd.h> #include <cstdio> @@ -34,7 +33,6 @@ #include "Callbacks.h" #include "GeneratedTestHarness.h" -#include "MemoryUtils.h" #include "TestHarness.h" #include "Utils.h" #include "VtsHalNeuralnetworks.h" @@ -229,7 +227,11 @@ class CompilationCachingTestBase : public testing::Test { // Create cache directory. The cache directory and a temporary cache file is always created // to test the behavior of prepareModelFromCache, even when caching is not supported. +#ifdef __ANDROID__ char cacheDirTemp[] = "/data/local/tmp/TestCompilationCachingXXXXXX"; +#else // __ANDROID__ + char cacheDirTemp[] = "/tmp/TestCompilationCachingXXXXXX"; +#endif // __ANDROID__ char* cacheDir = mkdtemp(cacheDirTemp); ASSERT_NE(cacheDir, nullptr); mCacheDir = cacheDir; diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp index 2460fbad86..40f6cd1573 100644 --- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp @@ -20,7 +20,6 @@ #include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h> #include <android-base/logging.h> #include <android/binder_auto_utils.h> -#include <android/sync.h> #include <gtest/gtest.h> #include <algorithm> @@ -30,7 +29,6 @@ #include <numeric> #include <vector> -#include <MemoryUtils.h> #include <android/binder_status.h> #include <nnapi/Result.h> #include <nnapi/SharedMemory.h> @@ -43,6 +41,10 @@ #include "Utils.h" #include "VtsHalNeuralnetworks.h" +#ifdef __ANDROID__ +#include <android/sync.h> +#endif // __ANDROID__ + namespace aidl::android::hardware::neuralnetworks::vts::functional { namespace nn = ::android::nn; @@ -63,6 +65,8 @@ struct TestConfig { // it is skipped. The field is set to true by default and is set to false in // quantization coupling tests to suppress skipping a test bool reportSkipping; + // `useConfig` indicates if a test should use execute*WithConfig functions for the execution. + bool useConfig; TestConfig(Executor executor, bool measureTiming, OutputType outputType, MemoryType memoryType, bool reusable) : executor(executor), @@ -70,7 +74,8 @@ struct TestConfig { outputType(outputType), memoryType(memoryType), reusable(reusable), - reportSkipping(true) {} + reportSkipping(true), + useConfig(false) {} TestConfig(Executor executor, bool measureTiming, OutputType outputType, MemoryType memoryType, bool reusable, bool reportSkipping) : executor(executor), @@ -78,7 +83,17 @@ struct TestConfig { outputType(outputType), memoryType(memoryType), reusable(reusable), - reportSkipping(reportSkipping) {} + reportSkipping(reportSkipping), + useConfig(false) {} + TestConfig(Executor executor, bool measureTiming, OutputType outputType, MemoryType memoryType, + bool reusable, bool reportSkipping, bool useConfig) + : executor(executor), + measureTiming(measureTiming), + outputType(outputType), + memoryType(memoryType), + reusable(reusable), + reportSkipping(reportSkipping), + useConfig(useConfig) {} }; std::string toString(OutputType type) { @@ -100,7 +115,8 @@ std::string toString(const TestConfig& config) { << ", .measureTiming=" << (config.measureTiming ? "true" : "false") << ", .outputType=" << toString(config.outputType) << ", .memoryType=" << toString(config.memoryType) - << ", .reusable=" << (config.reusable ? "true" : "false") << "}"; + << ", .reusable=" << (config.reusable ? "true" : "false") + << ", .useConfig=" << (config.useConfig ? "true" : "false") << "}"; return ss.str(); } @@ -267,10 +283,14 @@ void copyTestBuffers(const std::vector<const TestBuffer*>& buffers, uint8_t* out } // namespace void waitForSyncFence(int syncFd) { - constexpr int kInfiniteTimeout = -1; ASSERT_GT(syncFd, 0); +#ifdef __ANDROID__ + constexpr int kInfiniteTimeout = -1; int r = sync_wait(syncFd, kInfiniteTimeout); ASSERT_GE(r, 0); +#else // __ANDROID__ + LOG(FATAL) << "waitForSyncFence not supported on host"; +#endif // __ANDROID__ } Model createModel(const TestModel& testModel) { @@ -587,8 +607,8 @@ void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device, std::shared_ptr<IExecution> execution; if (testConfig.reusable) { - const auto ret = preparedModel->createReusableExecution(request, testConfig.measureTiming, - loopTimeoutDurationNs, &execution); + const auto ret = preparedModel->createReusableExecution( + request, {testConfig.measureTiming, loopTimeoutDurationNs, {}, {}}, &execution); ASSERT_TRUE(ret.isOk()) << static_cast<nn::ErrorStatus>(ret.getServiceSpecificError()); ASSERT_NE(nullptr, execution.get()); } @@ -607,6 +627,10 @@ void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device, ::ndk::ScopedAStatus ret; if (testConfig.reusable) { ret = execution->executeSynchronously(kNoDeadline, &executionResult); + } else if (testConfig.useConfig) { + ret = preparedModel->executeSynchronouslyWithConfig( + request, {testConfig.measureTiming, loopTimeoutDurationNs, {}, {}}, + kNoDeadline, &executionResult); } else { ret = preparedModel->executeSynchronously(request, testConfig.measureTiming, kNoDeadline, loopTimeoutDurationNs, @@ -649,9 +673,16 @@ void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device, ExecutionResult executionResult; // execute - ret = burst->executeSynchronously(request, slots, testConfig.measureTiming, - kNoDeadline, loopTimeoutDurationNs, - &executionResult); + if (testConfig.useConfig) { + ret = burst->executeSynchronouslyWithConfig( + request, slots, + {testConfig.measureTiming, loopTimeoutDurationNs, {}, {}}, kNoDeadline, + &executionResult); + } else { + ret = burst->executeSynchronously(request, slots, testConfig.measureTiming, + kNoDeadline, loopTimeoutDurationNs, + &executionResult); + } ASSERT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC) << ret.getDescription(); if (ret.isOk()) { @@ -680,6 +711,10 @@ void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device, ::ndk::ScopedAStatus ret; if (testConfig.reusable) { ret = execution->executeFenced({}, kNoDeadline, kNoDuration, &executionResult); + } else if (testConfig.useConfig) { + ret = preparedModel->executeFencedWithConfig( + request, {}, {testConfig.measureTiming, loopTimeoutDurationNs, {}, {}}, + kNoDeadline, kNoDuration, &executionResult); } else { ret = preparedModel->executeFenced(request, {}, testConfig.measureTiming, kNoDeadline, loopTimeoutDurationNs, @@ -697,9 +732,19 @@ void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device, waitFor.emplace_back(dupFd); // If a sync fence is returned, try start another run waiting for the sync // fence. - ret = preparedModel->executeFenced(request, waitFor, testConfig.measureTiming, - kNoDeadline, loopTimeoutDurationNs, - kNoDuration, &executionResult); + if (testConfig.reusable) { + ret = execution->executeFenced(waitFor, kNoDeadline, kNoDuration, + &executionResult); + } else if (testConfig.useConfig) { + ret = preparedModel->executeFencedWithConfig( + request, waitFor, + {testConfig.measureTiming, loopTimeoutDurationNs, {}, {}}, + kNoDeadline, kNoDuration, &executionResult); + } else { + ret = preparedModel->executeFenced( + request, waitFor, testConfig.measureTiming, kNoDeadline, + loopTimeoutDurationNs, kNoDuration, &executionResult); + } ASSERT_TRUE(ret.isOk()); waitForSyncFence(executionResult.syncFence.get()); } @@ -830,11 +875,13 @@ void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device, std::vector<Executor> executorList; std::vector<MemoryType> memoryTypeList; std::vector<bool> reusableList = {false}; + std::vector<bool> useConfigList = {false}; int deviceVersion; ASSERT_TRUE(device->getInterfaceVersion(&deviceVersion).isOk()); if (deviceVersion >= kMinAidlLevelForFL8) { reusableList.push_back(true); + useConfigList.push_back(true); } switch (testKind) { @@ -854,7 +901,11 @@ void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device, outputTypesList = {OutputType::FULLY_SPECIFIED}; measureTimingList = {false}; executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED}; +#ifdef __ANDROID__ memoryTypeList = {MemoryType::BLOB_AHWB, MemoryType::DEVICE}; +#else // __ANDROID__ + memoryTypeList = {MemoryType::DEVICE}; // BLOB_AHWB is not supported on the host. +#endif // __ANDROID__ } break; case TestKind::FENCED_COMPUTE: { outputTypesList = {OutputType::FULLY_SPECIFIED}; @@ -879,11 +930,14 @@ void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device, for (const Executor executor : executorList) { for (const MemoryType memoryType : memoryTypeList) { for (const bool reusable : reusableList) { - if (executor == Executor::BURST && reusable) continue; - const TestConfig testConfig(executor, measureTiming, outputType, memoryType, - reusable); - SCOPED_TRACE(toString(testConfig)); - EvaluatePreparedModel(device, preparedModel, testModel, testConfig); + for (const bool useConfig : useConfigList) { + if ((useConfig || executor == Executor::BURST) && reusable) continue; + const TestConfig testConfig(executor, measureTiming, outputType, + memoryType, reusable, + /*reportSkipping=*/true, useConfig); + SCOPED_TRACE(toString(testConfig)); + EvaluatePreparedModel(device, preparedModel, testModel, testConfig); + } } } } @@ -942,6 +996,13 @@ void Execute(const std::shared_ptr<IDevice>& device, const TestModel& testModel, createPreparedModel(device, model, &preparedModel); if (preparedModel == nullptr) return; EvaluatePreparedModel(device, preparedModel, testModel, testKind); + int32_t deviceVersion; + ASSERT_TRUE(device->getInterfaceVersion(&deviceVersion).isOk()); + if (deviceVersion >= kMinAidlLevelForFL8) { + createPreparedModel(device, model, &preparedModel, /*reportSkipping*/ true, + /*useConfig*/ true); + EvaluatePreparedModel(device, preparedModel, testModel, testKind); + } } break; case TestKind::QUANTIZATION_COUPLING: { ASSERT_TRUE(testModel.hasQuant8CoupledOperands()); diff --git a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp index b3e9c633e3..f8341b15b4 100644 --- a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp +++ b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "neuralnetworks_aidl_hal_test" #include <aidl/android/hardware/graphics/common/PixelFormat.h> +#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h> #include <android-base/logging.h> #include <android/binder_auto_utils.h> #include <android/binder_interface_utils.h> @@ -33,7 +34,6 @@ #include "Callbacks.h" #include "GeneratedTestHarness.h" -#include "MemoryUtils.h" #include "Utils.h" #include "VtsHalNeuralnetworks.h" @@ -191,7 +191,7 @@ TestModel createSingleAddModel(const TestOperand& operand) { } // A placeholder invalid IPreparedModel class for MemoryDomainAllocateTest.InvalidPreparedModel -class InvalidPreparedModel : public BnPreparedModel { +class InvalidPreparedModel final : public IPreparedModel { public: ndk::ScopedAStatus executeSynchronously(const Request&, bool, int64_t, int64_t, ExecutionResult*) override { @@ -204,15 +204,37 @@ class InvalidPreparedModel : public BnPreparedModel { return ndk::ScopedAStatus::fromServiceSpecificError( static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE)); } + ndk::ScopedAStatus executeSynchronouslyWithConfig(const Request&, const ExecutionConfig&, + int64_t, ExecutionResult*) override { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE)); + } + ndk::ScopedAStatus executeFencedWithConfig(const Request&, + const std::vector<ndk::ScopedFileDescriptor>&, + const ExecutionConfig&, int64_t, int64_t, + FencedExecutionResult*) override { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE)); + } ndk::ScopedAStatus configureExecutionBurst(std::shared_ptr<IBurst>*) override { return ndk::ScopedAStatus::fromServiceSpecificError( static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE)); } - ndk::ScopedAStatus createReusableExecution(const aidl_hal::Request&, bool, int64_t, + ndk::ScopedAStatus createReusableExecution(const aidl_hal::Request&, const ExecutionConfig&, std::shared_ptr<aidl_hal::IExecution>*) override { return ndk::ScopedAStatus::fromServiceSpecificError( static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE)); } + ndk::ScopedAStatus getInterfaceVersion(int32_t* /*interfaceVersion*/) { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE)); + } + ndk::ScopedAStatus getInterfaceHash(std::string* /*interfaceHash*/) { + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE)); + } + ndk::SpAIBinder asBinder() override { return ::ndk::SpAIBinder{}; } + bool isRemote() override { return true; } }; template <typename... Args> diff --git a/neuralnetworks/aidl/vts/functional/Utils.cpp b/neuralnetworks/aidl/vts/functional/Utils.cpp index efd5bca517..1bc76f2cb4 100644 --- a/neuralnetworks/aidl/vts/functional/Utils.cpp +++ b/neuralnetworks/aidl/vts/functional/Utils.cpp @@ -21,18 +21,20 @@ #include <aidl/android/hardware/neuralnetworks/OperandType.h> #include <android-base/logging.h> #include <android/binder_status.h> -#include <android/hardware_buffer.h> #include <sys/mman.h> #include <iostream> #include <limits> #include <numeric> -#include <MemoryUtils.h> #include <nnapi/SharedMemory.h> #include <nnapi/hal/aidl/Conversions.h> #include <nnapi/hal/aidl/Utils.h> +#ifdef __ANDROID__ +#include <android/hardware_buffer.h> +#endif // __ANDROID__ + namespace aidl::android::hardware::neuralnetworks { using test_helper::TestBuffer; @@ -140,7 +142,8 @@ std::unique_ptr<TestBlobAHWB> TestBlobAHWB::create(uint32_t size) { return ahwb->mIsValid ? std::move(ahwb) : nullptr; } -void TestBlobAHWB::initialize(uint32_t size) { +void TestBlobAHWB::initialize([[maybe_unused]] uint32_t size) { +#ifdef __ANDROID__ mIsValid = false; ASSERT_GT(size, 0); const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; @@ -164,6 +167,9 @@ void TestBlobAHWB::initialize(uint32_t size) { mAidlMemory = utils::convert(mMemory).value(); mIsValid = true; +#else // __ANDROID__ + LOG(FATAL) << "TestBlobAHWB::initialize not supported on host"; +#endif // __ANDROID__ } std::string gtestCompliantName(std::string name) { diff --git a/neuralnetworks/aidl/vts/functional/Utils.h b/neuralnetworks/aidl/vts/functional/Utils.h index 0db3f8c7f8..4e0a4aafa3 100644 --- a/neuralnetworks/aidl/vts/functional/Utils.h +++ b/neuralnetworks/aidl/vts/functional/Utils.h @@ -18,7 +18,6 @@ #define ANDROID_HARDWARE_NEURALNETWORKS_AIDL_UTILS_H #include <android-base/logging.h> -#include <android/hardware_buffer.h> #include <gtest/gtest.h> #include <algorithm> diff --git a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp index fdc7eff96f..931ba258b3 100644 --- a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp +++ b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp @@ -77,6 +77,28 @@ static void validatePrepareModel(const std::shared_ptr<IDevice>& device, const s ASSERT_EQ(nullptr, preparedModel.get()); } +static void validatePrepareModelWithConfig(const std::shared_ptr<IDevice>& device, + const std::string& message, const Model& model, + ExecutionPreference preference, Priority priority) { + SCOPED_TRACE(message + " [prepareModelWithConfig]"); + + std::shared_ptr<PreparedModelCallback> preparedModelCallback = + ndk::SharedRefBase::make<PreparedModelCallback>(); + const auto prepareLaunchStatus = device->prepareModelWithConfig( + model, {preference, priority, kNoDeadline, {}, {}, kEmptyCacheToken, {}, {}}, + preparedModelCallback); + ASSERT_FALSE(prepareLaunchStatus.isOk()); + ASSERT_EQ(prepareLaunchStatus.getExceptionCode(), EX_SERVICE_SPECIFIC); + ASSERT_EQ(static_cast<ErrorStatus>(prepareLaunchStatus.getServiceSpecificError()), + ErrorStatus::INVALID_ARGUMENT); + + preparedModelCallback->wait(); + ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus(); + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, prepareReturnStatus); + std::shared_ptr<IPreparedModel> preparedModel = preparedModelCallback->getPreparedModel(); + ASSERT_EQ(nullptr, preparedModel.get()); +} + static bool validExecutionPreference(ExecutionPreference preference) { return preference == ExecutionPreference::LOW_POWER || preference == ExecutionPreference::FAST_SINGLE_ANSWER || @@ -103,6 +125,13 @@ static void validate(const std::shared_ptr<IDevice>& device, const std::string& } validatePrepareModel(device, message, model, preference, priority); + + int32_t aidlVersion; + ASSERT_TRUE(device->getInterfaceVersion(&aidlVersion).isOk()); + if (aidlVersion >= kMinAidlLevelForFL8) { + // prepareModelWithConfig must satisfy all requirements enforced by prepareModel. + validatePrepareModelWithConfig(device, message, model, preference, priority); + } } static uint32_t addOperand(Model* model) { diff --git a/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp b/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp index e8debf704c..d7498419a1 100644 --- a/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp +++ b/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp @@ -45,7 +45,7 @@ static void validateReusableExecution(const std::shared_ptr<IPreparedModel>& pre { SCOPED_TRACE(message + " [createReusableExecution]"); const auto createStatus = preparedModel->createReusableExecution( - request, measure, kOmittedTimeoutDuration, &execution); + request, {measure, kOmittedTimeoutDuration, {}, {}}, &execution); if (!createStatus.isOk()) { ASSERT_EQ(createStatus.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorStatus>(createStatus.getServiceSpecificError()), @@ -149,10 +149,59 @@ static void validate(const std::shared_ptr<IPreparedModel>& preparedModel, int32_t aidlVersion; ASSERT_TRUE(preparedModel->getInterfaceVersion(&aidlVersion).isOk()); + if (aidlVersion < kMinAidlLevelForFL8) { + return; + } // validate reusable execution - if (aidlVersion >= kMinAidlLevelForFL8) { - validateReusableExecution(preparedModel, message, request, measure); + validateReusableExecution(preparedModel, message, request, measure); + + // synchronous with empty hints + { + SCOPED_TRACE(message + " [executeSynchronouslyWithConfig]"); + ExecutionResult executionResult; + const auto executeStatus = preparedModel->executeSynchronouslyWithConfig( + request, {measure, kOmittedTimeoutDuration, {}, {}}, kNoDeadline, &executionResult); + ASSERT_FALSE(executeStatus.isOk()); + ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC); + ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()), + ErrorStatus::INVALID_ARGUMENT); + } + + // fenced with empty hints + { + SCOPED_TRACE(message + " [executeFencedWithConfig]"); + FencedExecutionResult executionResult; + const auto executeStatus = preparedModel->executeFencedWithConfig( + request, {}, {false, kOmittedTimeoutDuration, {}, {}}, kNoDeadline, kNoDuration, + &executionResult); + ASSERT_FALSE(executeStatus.isOk()); + ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC); + ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()), + ErrorStatus::INVALID_ARGUMENT); + } + + // burst with empty hints + { + SCOPED_TRACE(message + " [burst executeSynchronouslyWithConfig]"); + + // create burst + std::shared_ptr<IBurst> burst; + auto ret = preparedModel->configureExecutionBurst(&burst); + ASSERT_TRUE(ret.isOk()) << ret.getDescription(); + ASSERT_NE(nullptr, burst.get()); + + // use -1 for all memory identifier tokens + const std::vector<int64_t> slots(request.pools.size(), -1); + + ExecutionResult executionResult; + const auto executeStatus = burst->executeSynchronouslyWithConfig( + request, slots, {measure, kOmittedTimeoutDuration, {}, {}}, kNoDeadline, + &executionResult); + ASSERT_FALSE(executeStatus.isOk()); + ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC); + ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()), + ErrorStatus::INVALID_ARGUMENT); } } diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp index c417356005..51b4805134 100644 --- a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp +++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "neuralnetworks_aidl_hal_test" + #include "VtsHalNeuralnetworks.h" #include <android-base/logging.h> @@ -28,20 +29,27 @@ #include <utility> #include <TestHarness.h> -#include <aidl/Vintf.h> #include <nnapi/hal/aidl/Conversions.h> #include "Callbacks.h" #include "GeneratedTestHarness.h" #include "Utils.h" +#ifdef __ANDROID__ +#include <aidl/Vintf.h> +#else // __ANDROID__ +#include <CanonicalDevice.h> +#include <nnapi/hal/aidl/Adapter.h> +#endif // __ANDROID__ + namespace aidl::android::hardware::neuralnetworks::vts::functional { using implementation::PreparedModelCallback; // internal helper function void createPreparedModel(const std::shared_ptr<IDevice>& device, const Model& model, - std::shared_ptr<IPreparedModel>* preparedModel, bool reportSkipping) { + std::shared_ptr<IPreparedModel>* preparedModel, bool reportSkipping, + bool useConfig) { ASSERT_NE(nullptr, preparedModel); *preparedModel = nullptr; @@ -56,11 +64,25 @@ void createPreparedModel(const std::shared_ptr<IDevice>& device, const Model& mo // launch prepare model const std::shared_ptr<PreparedModelCallback> preparedModelCallback = ndk::SharedRefBase::make<PreparedModelCallback>(); - const auto prepareLaunchStatus = - device->prepareModel(model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, - kNoDeadline, {}, {}, kEmptyCacheToken, preparedModelCallback); - ASSERT_TRUE(prepareLaunchStatus.isOk()) << prepareLaunchStatus.getDescription(); - + if (useConfig) { + const auto prepareLaunchStatus = + device->prepareModelWithConfig(model, + {ExecutionPreference::FAST_SINGLE_ANSWER, + kDefaultPriority, + kNoDeadline, + {}, + {}, + kEmptyCacheToken, + {}, + {}}, + preparedModelCallback); + ASSERT_TRUE(prepareLaunchStatus.isOk()) << prepareLaunchStatus.getDescription(); + } else { + const auto prepareLaunchStatus = device->prepareModel( + model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, kNoDeadline, {}, + {}, kEmptyCacheToken, preparedModelCallback); + ASSERT_TRUE(prepareLaunchStatus.isOk()) << prepareLaunchStatus.getDescription(); + } // retrieve prepared model preparedModelCallback->wait(); const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus(); @@ -96,6 +118,7 @@ void NeuralNetworksAidlTest::SetUp() { ASSERT_TRUE(deviceIsResponsive); } +#ifdef __ANDROID__ static NamedDevice makeNamedDevice(const std::string& name) { ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str())); return {name, IDevice::fromBinder(binder)}; @@ -112,6 +135,14 @@ static std::vector<NamedDevice> getNamedDevicesImpl() { std::transform(names.begin(), names.end(), std::back_inserter(namedDevices), makeNamedDevice); return namedDevices; } +#else // __ANDROID__ +static std::vector<NamedDevice> getNamedDevicesImpl() { + const std::string name = "nnapi-sample"; + auto device = std::make_shared<const ::android::nn::sample::Device>(name); + auto aidlDevice = adapter::adapt(device); + return {{name, aidlDevice}}; +} +#endif // __ANDROID__ const std::vector<NamedDevice>& getNamedDevices() { const static std::vector<NamedDevice> devices = getNamedDevicesImpl(); diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h index a900590791..00d705c521 100644 --- a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h +++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h @@ -51,8 +51,8 @@ std::string printNeuralNetworksAidlTest( // Create an IPreparedModel object. If the model cannot be prepared, // "preparedModel" will be nullptr instead. void createPreparedModel(const std::shared_ptr<IDevice>& device, const Model& model, - std::shared_ptr<IPreparedModel>* preparedModel, - bool reportSkipping = true); + std::shared_ptr<IPreparedModel>* preparedModel, bool reportSkipping = true, + bool useConfig = false); enum class Executor { SYNC, BURST, FENCED }; diff --git a/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/Burst.h b/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/Burst.h index f2687c4a69..8d42e2fc7f 100644 --- a/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/Burst.h +++ b/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/Burst.h @@ -46,6 +46,11 @@ class Burst : public BnBurst { bool measureTiming, int64_t deadlineNs, int64_t loopTimeoutDurationNs, ExecutionResult* executionResult) override; + ndk::ScopedAStatus executeSynchronouslyWithConfig( + const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, + const ExecutionConfig& config, int64_t deadlineNs, + ExecutionResult* executionResult) override; + ndk::ScopedAStatus releaseMemoryResource(int64_t memoryIdentifierToken) override; class ThreadSafeMemoryCache { diff --git a/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/Device.h b/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/Device.h index aa29d63b6b..c94f27085f 100644 --- a/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/Device.h +++ b/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/Device.h @@ -31,6 +31,7 @@ #include <aidl/android/hardware/neuralnetworks/IPreparedModelParcel.h> #include <aidl/android/hardware/neuralnetworks/Model.h> #include <aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.h> +#include <aidl/android/hardware/neuralnetworks/PrepareModelConfig.h> #include <aidl/android/hardware/neuralnetworks/Priority.h> #include <android/binder_auto_utils.h> #include <nnapi/IDevice.h> @@ -72,6 +73,9 @@ class Device : public BnDevice { const std::vector<ndk::ScopedFileDescriptor>& dataCache, const std::vector<uint8_t>& token, const std::shared_ptr<IPreparedModelCallback>& callback) override; + ndk::ScopedAStatus prepareModelWithConfig( + const Model& model, const PrepareModelConfig& config, + const std::shared_ptr<IPreparedModelCallback>& callback) override; protected: const ::android::nn::SharedDevice kDevice; diff --git a/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/PreparedModel.h b/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/PreparedModel.h index f92b0bc783..d1359d6ac1 100644 --- a/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/PreparedModel.h +++ b/neuralnetworks/utils/adapter/aidl/include/nnapi/hal/aidl/PreparedModel.h @@ -51,9 +51,17 @@ class PreparedModel : public BnPreparedModel { int64_t loopTimeoutDurationNs, int64_t durationNs, FencedExecutionResult* executionResult) override; ndk::ScopedAStatus configureExecutionBurst(std::shared_ptr<IBurst>* burst) override; - ndk::ScopedAStatus createReusableExecution(const Request& request, bool measureTiming, - int64_t loopTimeoutDurationNs, + ndk::ScopedAStatus createReusableExecution(const Request& request, + const ExecutionConfig& config, std::shared_ptr<IExecution>* execution) override; + ndk::ScopedAStatus executeSynchronouslyWithConfig(const Request& request, + const ExecutionConfig& config, + int64_t deadlineNs, + ExecutionResult* executionResult) override; + ndk::ScopedAStatus executeFencedWithConfig( + const Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor, + const ExecutionConfig& config, int64_t deadlineNs, int64_t durationNs, + FencedExecutionResult* executionResult) override; ::android::nn::SharedPreparedModel getUnderlyingPreparedModel() const; diff --git a/neuralnetworks/utils/adapter/aidl/src/Burst.cpp b/neuralnetworks/utils/adapter/aidl/src/Burst.cpp index 4fabb20635..a4a80faf2a 100644 --- a/neuralnetworks/utils/adapter/aidl/src/Burst.cpp +++ b/neuralnetworks/utils/adapter/aidl/src/Burst.cpp @@ -93,7 +93,8 @@ std::vector<nn::IBurst::OptionalCacheHold> ensureAllMemoriesAreCached( nn::ExecutionResult<ExecutionResult> executeSynchronously( const nn::IBurst& burst, const Burst::ThreadSafeMemoryCache& cache, const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, bool measureTiming, int64_t deadlineNs, - int64_t loopTimeoutDurationNs) { + int64_t loopTimeoutDurationNs, const std::vector<TokenValuePair>& hints, + const std::vector<ExtensionNameAndPrefix>& extensionNameToPrefix) { if (request.pools.size() != memoryIdentifierTokens.size()) { return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "request.pools.size() != memoryIdentifierTokens.size()"; @@ -107,11 +108,13 @@ nn::ExecutionResult<ExecutionResult> executeSynchronously( const auto nnMeasureTiming = measureTiming ? nn::MeasureTiming::YES : nn::MeasureTiming::NO; const auto nnDeadline = NN_TRY(makeOptionalTimePoint(deadlineNs)); const auto nnLoopTimeoutDuration = NN_TRY(makeOptionalDuration(loopTimeoutDurationNs)); + auto nnHints = NN_TRY(convertInput(hints)); + auto nnExtensionNameToPrefix = NN_TRY(convertInput(extensionNameToPrefix)); const auto hold = ensureAllMemoriesAreCached(&nnRequest, memoryIdentifierTokens, burst, cache); - const auto result = - burst.execute(nnRequest, nnMeasureTiming, nnDeadline, nnLoopTimeoutDuration); + const auto result = burst.execute(nnRequest, nnMeasureTiming, nnDeadline, nnLoopTimeoutDuration, + nnHints, nnExtensionNameToPrefix); if (!result.ok() && result.error().code == nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) { const auto& [message, code, outputShapes] = result.error(); @@ -155,7 +158,24 @@ ndk::ScopedAStatus Burst::executeSynchronously(const Request& request, ExecutionResult* executionResult) { auto result = adapter::executeSynchronously(*kBurst, kMemoryCache, request, memoryIdentifierTokens, - measureTiming, deadlineNs, loopTimeoutDurationNs); + measureTiming, deadlineNs, loopTimeoutDurationNs, {}, {}); + if (!result.has_value()) { + auto [message, code, _] = std::move(result).error(); + const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + static_cast<int32_t>(aidlCode), message.c_str()); + } + *executionResult = std::move(result).value(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Burst::executeSynchronouslyWithConfig( + const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, + const ExecutionConfig& config, int64_t deadlineNs, ExecutionResult* executionResult) { + auto result = adapter::executeSynchronously( + *kBurst, kMemoryCache, request, memoryIdentifierTokens, config.measureTiming, + deadlineNs, config.loopTimeoutDurationNs, config.executionHints, + config.extensionNameToPrefix); if (!result.has_value()) { auto [message, code, _] = std::move(result).error(); const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); diff --git a/neuralnetworks/utils/adapter/aidl/src/Device.cpp b/neuralnetworks/utils/adapter/aidl/src/Device.cpp index 763be7f3fa..84aaddbe9d 100644 --- a/neuralnetworks/utils/adapter/aidl/src/Device.cpp +++ b/neuralnetworks/utils/adapter/aidl/src/Device.cpp @@ -148,13 +148,14 @@ void notify(IPreparedModelCallback* callback, PrepareModelResult result) { } } -nn::GeneralResult<void> prepareModel(const nn::SharedDevice& device, const Executor& executor, - const Model& model, ExecutionPreference preference, - Priority priority, int64_t deadlineNs, - const std::vector<ndk::ScopedFileDescriptor>& modelCache, - const std::vector<ndk::ScopedFileDescriptor>& dataCache, - const std::vector<uint8_t>& token, - const std::shared_ptr<IPreparedModelCallback>& callback) { +nn::GeneralResult<void> prepareModel( + const nn::SharedDevice& device, const Executor& executor, const Model& model, + ExecutionPreference preference, Priority priority, int64_t deadlineNs, + const std::vector<ndk::ScopedFileDescriptor>& modelCache, + const std::vector<ndk::ScopedFileDescriptor>& dataCache, const std::vector<uint8_t>& token, + const std::vector<TokenValuePair>& hints, + const std::vector<ExtensionNameAndPrefix>& extensionNameToPrefix, + const std::shared_ptr<IPreparedModelCallback>& callback) { if (callback.get() == nullptr) { return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback"; } @@ -166,12 +167,16 @@ nn::GeneralResult<void> prepareModel(const nn::SharedDevice& device, const Execu auto nnModelCache = NN_TRY(convertInput(modelCache)); auto nnDataCache = NN_TRY(convertInput(dataCache)); const auto nnToken = NN_TRY(convertCacheToken(token)); + auto nnHints = NN_TRY(convertInput(hints)); + auto nnExtensionNameToPrefix = NN_TRY(convertInput(extensionNameToPrefix)); Task task = [device, nnModel = std::move(nnModel), nnPreference, nnPriority, nnDeadline, nnModelCache = std::move(nnModelCache), nnDataCache = std::move(nnDataCache), - nnToken, callback] { - auto result = device->prepareModel(nnModel, nnPreference, nnPriority, nnDeadline, - nnModelCache, nnDataCache, nnToken); + nnToken, nnHints = std::move(nnHints), + nnExtensionNameToPrefix = std::move(nnExtensionNameToPrefix), callback] { + auto result = + device->prepareModel(nnModel, nnPreference, nnPriority, nnDeadline, nnModelCache, + nnDataCache, nnToken, nnHints, nnExtensionNameToPrefix); notify(callback.get(), std::move(result)); }; executor(std::move(task), nnDeadline); @@ -273,8 +278,9 @@ ndk::ScopedAStatus Device::prepareModel(const Model& model, ExecutionPreference const std::vector<ndk::ScopedFileDescriptor>& dataCache, const std::vector<uint8_t>& token, const std::shared_ptr<IPreparedModelCallback>& callback) { - const auto result = adapter::prepareModel(kDevice, kExecutor, model, preference, priority, - deadlineNs, modelCache, dataCache, token, callback); + const auto result = + adapter::prepareModel(kDevice, kExecutor, model, preference, priority, deadlineNs, + modelCache, dataCache, token, {}, {}, callback); if (!result.has_value()) { const auto& [message, code] = result.error(); const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); @@ -301,4 +307,21 @@ ndk::ScopedAStatus Device::prepareModelFromCache( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Device::prepareModelWithConfig( + const Model& model, const PrepareModelConfig& config, + const std::shared_ptr<IPreparedModelCallback>& callback) { + const auto result = adapter::prepareModel( + kDevice, kExecutor, model, config.preference, config.priority, config.deadlineNs, + config.modelCache, config.dataCache, config.cacheToken, config.compilationHints, + config.extensionNameToPrefix, callback); + if (!result.has_value()) { + const auto& [message, code] = result.error(); + const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); + callback->notify(aidlCode, nullptr); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + static_cast<int32_t>(aidlCode), message.c_str()); + } + return ndk::ScopedAStatus::ok(); +} + } // namespace aidl::android::hardware::neuralnetworks::adapter diff --git a/neuralnetworks/utils/adapter/aidl/src/PreparedModel.cpp b/neuralnetworks/utils/adapter/aidl/src/PreparedModel.cpp index 5cab62c625..790558fde4 100644 --- a/neuralnetworks/utils/adapter/aidl/src/PreparedModel.cpp +++ b/neuralnetworks/utils/adapter/aidl/src/PreparedModel.cpp @@ -118,17 +118,20 @@ nn::GeneralResult<nn::OptionalTimePoint> makeOptionalTimePoint(int64_t durationN return durationNs < 0 ? nn::OptionalTimePoint{} : nn::TimePoint(makeDuration(durationNs)); } -nn::ExecutionResult<ExecutionResult> executeSynchronously(const nn::IPreparedModel& preparedModel, - const Request& request, - bool measureTiming, int64_t deadlineNs, - int64_t loopTimeoutDurationNs) { +nn::ExecutionResult<ExecutionResult> executeSynchronously( + const nn::IPreparedModel& preparedModel, const Request& request, bool measureTiming, + int64_t deadlineNs, int64_t loopTimeoutDurationNs, const std::vector<TokenValuePair>& hints, + const std::vector<ExtensionNameAndPrefix>& extensionNameToPrefix) { const auto nnRequest = NN_TRY(convertInput(request)); const auto nnMeasureTiming = measureTiming ? nn::MeasureTiming::YES : nn::MeasureTiming::NO; const auto nnDeadline = NN_TRY(makeOptionalTimePoint(deadlineNs)); const auto nnLoopTimeoutDuration = NN_TRY(makeOptionalDuration(loopTimeoutDurationNs)); + auto nnHints = NN_TRY(convertInput(hints)); + auto nnExtensionNameToPrefix = NN_TRY(convertInput(extensionNameToPrefix)); const auto result = - preparedModel.execute(nnRequest, nnMeasureTiming, nnDeadline, nnLoopTimeoutDuration); + preparedModel.execute(nnRequest, nnMeasureTiming, nnDeadline, nnLoopTimeoutDuration, + nnHints, nnExtensionNameToPrefix); if (!result.ok() && result.error().code == nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) { const auto& [message, code, outputShapes] = result.error(); @@ -147,16 +150,21 @@ nn::ExecutionResult<ExecutionResult> executeSynchronously(const nn::IPreparedMod nn::GeneralResult<FencedExecutionResult> executeFenced( const nn::IPreparedModel& preparedModel, const Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor, bool measureTiming, - int64_t deadlineNs, int64_t loopTimeoutDurationNs, int64_t durationNs) { + int64_t deadlineNs, int64_t loopTimeoutDurationNs, int64_t durationNs, + const std::vector<TokenValuePair>& hints, + const std::vector<ExtensionNameAndPrefix>& extensionNameToPrefix) { const auto nnRequest = NN_TRY(convertInput(request)); const auto nnWaitFor = NN_TRY(convertSyncFences(waitFor)); const auto nnMeasureTiming = measureTiming ? nn::MeasureTiming::YES : nn::MeasureTiming::NO; const auto nnDeadline = NN_TRY(makeOptionalTimePoint(deadlineNs)); const auto nnLoopTimeoutDuration = NN_TRY(makeOptionalDuration(loopTimeoutDurationNs)); const auto nnDuration = NN_TRY(makeOptionalDuration(durationNs)); + auto nnHints = NN_TRY(convertInput(hints)); + auto nnExtensionNameToPrefix = NN_TRY(convertInput(extensionNameToPrefix)); auto [syncFence, executeFencedInfoCallback] = NN_TRY(preparedModel.executeFenced( - nnRequest, nnWaitFor, nnMeasureTiming, nnDeadline, nnLoopTimeoutDuration, nnDuration)); + nnRequest, nnWaitFor, nnMeasureTiming, nnDeadline, nnLoopTimeoutDuration, nnDuration, + nnHints, nnExtensionNameToPrefix)); ndk::ScopedFileDescriptor fileDescriptor; if (syncFence.hasFd()) { @@ -171,11 +179,16 @@ nn::GeneralResult<FencedExecutionResult> executeFenced( nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::IPreparedModel& preparedModel, const Request& request, bool measureTiming, - int64_t loopTimeoutDurationNs) { + int64_t loopTimeoutDurationNs, const std::vector<TokenValuePair>& hints, + const std::vector<ExtensionNameAndPrefix>& extensionNameToPrefix) { const auto nnRequest = NN_TRY(convertInput(request)); const auto nnMeasureTiming = measureTiming ? nn::MeasureTiming::YES : nn::MeasureTiming::NO; const auto nnLoopTimeoutDuration = NN_TRY(makeOptionalDuration(loopTimeoutDurationNs)); - return preparedModel.createReusableExecution(nnRequest, nnMeasureTiming, nnLoopTimeoutDuration); + auto nnHints = NN_TRY(convertInput(hints)); + auto nnExtensionNameToPrefix = NN_TRY(convertInput(extensionNameToPrefix)); + + return preparedModel.createReusableExecution(nnRequest, nnMeasureTiming, nnLoopTimeoutDuration, + nnHints, nnExtensionNameToPrefix); } nn::ExecutionResult<ExecutionResult> executeSynchronously(const nn::IExecution& execution, @@ -231,7 +244,7 @@ ndk::ScopedAStatus PreparedModel::executeSynchronously(const Request& request, b int64_t loopTimeoutDurationNs, ExecutionResult* executionResult) { auto result = adapter::executeSynchronously(*kPreparedModel, request, measureTiming, deadlineNs, - loopTimeoutDurationNs); + loopTimeoutDurationNs, {}, {}); if (!result.has_value()) { const auto& [message, code, _] = result.error(); const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); @@ -247,7 +260,41 @@ ndk::ScopedAStatus PreparedModel::executeFenced( bool measureTiming, int64_t deadlineNs, int64_t loopTimeoutDurationNs, int64_t durationNs, FencedExecutionResult* executionResult) { auto result = adapter::executeFenced(*kPreparedModel, request, waitFor, measureTiming, - deadlineNs, loopTimeoutDurationNs, durationNs); + deadlineNs, loopTimeoutDurationNs, durationNs, {}, {}); + if (!result.has_value()) { + const auto& [message, code] = result.error(); + const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + static_cast<int32_t>(aidlCode), message.c_str()); + } + *executionResult = std::move(result).value(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PreparedModel::executeSynchronouslyWithConfig(const Request& request, + const ExecutionConfig& config, + int64_t deadlineNs, + ExecutionResult* executionResult) { + auto result = adapter::executeSynchronously( + *kPreparedModel, request, config.measureTiming, deadlineNs, + config.loopTimeoutDurationNs, config.executionHints, config.extensionNameToPrefix); + if (!result.has_value()) { + const auto& [message, code, _] = result.error(); + const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + static_cast<int32_t>(aidlCode), message.c_str()); + } + *executionResult = std::move(result).value(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PreparedModel::executeFencedWithConfig( + const Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor, + const ExecutionConfig& config, int64_t deadlineNs, int64_t durationNs, + FencedExecutionResult* executionResult) { + auto result = adapter::executeFenced(*kPreparedModel, request, waitFor, config.measureTiming, + deadlineNs, config.loopTimeoutDurationNs, durationNs, + config.executionHints, config.extensionNameToPrefix); if (!result.has_value()) { const auto& [message, code] = result.error(); const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); @@ -275,11 +322,11 @@ nn::SharedPreparedModel PreparedModel::getUnderlyingPreparedModel() const { } ndk::ScopedAStatus PreparedModel::createReusableExecution(const Request& request, - bool measureTiming, - int64_t loopTimeoutDurationNs, + const ExecutionConfig& config, std::shared_ptr<IExecution>* execution) { - auto result = adapter::createReusableExecution(*kPreparedModel, request, measureTiming, - loopTimeoutDurationNs); + auto result = adapter::createReusableExecution( + *kPreparedModel, request, config.measureTiming, config.loopTimeoutDurationNs, + config.executionHints, config.extensionNameToPrefix); if (!result.has_value()) { const auto& [message, code] = result.error(); const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE); diff --git a/neuralnetworks/utils/adapter/hidl/Android.bp b/neuralnetworks/utils/adapter/hidl/Android.bp index d073106a5f..6875daa4ed 100644 --- a/neuralnetworks/utils/adapter/hidl/Android.bp +++ b/neuralnetworks/utils/adapter/hidl/Android.bp @@ -30,17 +30,16 @@ cc_library_static { local_include_dirs: ["include/nnapi/hal"], export_include_dirs: ["include"], static_libs: [ - "neuralnetworks_types", - "neuralnetworks_utils_hal_1_0", - "neuralnetworks_utils_hal_1_1", - "neuralnetworks_utils_hal_1_2", - "neuralnetworks_utils_hal_1_3", - ], - shared_libs: [ "android.hardware.neuralnetworks@1.0", "android.hardware.neuralnetworks@1.1", "android.hardware.neuralnetworks@1.2", "android.hardware.neuralnetworks@1.3", "libfmq", + "neuralnetworks_types", + "neuralnetworks_utils_hal_1_0", + "neuralnetworks_utils_hal_1_1", + "neuralnetworks_utils_hal_1_2", + "neuralnetworks_utils_hal_1_3", + "neuralnetworks_utils_hal_common", ], } diff --git a/neuralnetworks/utils/adapter/hidl/src/Burst.cpp b/neuralnetworks/utils/adapter/hidl/src/Burst.cpp index 8b2e1dd465..e3b165b1a4 100644 --- a/neuralnetworks/utils/adapter/hidl/src/Burst.cpp +++ b/neuralnetworks/utils/adapter/hidl/src/Burst.cpp @@ -250,7 +250,7 @@ nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> Burst: nn::MeasureTiming canonicalMeasure = NN_TRY(nn::convert(measure)); const auto [outputShapes, timing] = - NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure, {}, {})); + NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure, {}, {}, {}, {})); return std::make_pair(NN_TRY(V1_2::utils::convert(outputShapes)), NN_TRY(V1_2::utils::convert(timing))); diff --git a/neuralnetworks/utils/adapter/hidl/src/Device.cpp b/neuralnetworks/utils/adapter/hidl/src/Device.cpp index 4993a80a93..0f44638722 100644 --- a/neuralnetworks/utils/adapter/hidl/src/Device.cpp +++ b/neuralnetworks/utils/adapter/hidl/src/Device.cpp @@ -135,7 +135,7 @@ nn::GeneralResult<void> prepareModel(const nn::SharedDevice& device, const Execu Task task = [device, nnModel = std::move(nnModel), executor, callback] { auto result = device->prepareModel(nnModel, nn::ExecutionPreference::DEFAULT, - nn::Priority::DEFAULT, {}, {}, {}, {}); + nn::Priority::DEFAULT, {}, {}, {}, {}, {}, {}); notify(callback.get(), std::move(result), executor); }; executor(std::move(task), {}); @@ -155,8 +155,8 @@ nn::GeneralResult<void> prepareModel_1_1(const nn::SharedDevice& device, const E const auto nnPreference = NN_TRY(convertInput(preference)); Task task = [device, nnModel = std::move(nnModel), nnPreference, executor, callback] { - auto result = - device->prepareModel(nnModel, nnPreference, nn::Priority::DEFAULT, {}, {}, {}, {}); + auto result = device->prepareModel(nnModel, nnPreference, nn::Priority::DEFAULT, {}, {}, {}, + {}, {}, {}); notify(callback.get(), std::move(result), executor); }; executor(std::move(task), {}); @@ -185,7 +185,7 @@ nn::GeneralResult<void> prepareModel_1_2(const nn::SharedDevice& device, const E nnModelCache = std::move(nnModelCache), nnDataCache = std::move(nnDataCache), nnToken, executor, callback] { auto result = device->prepareModel(nnModel, nnPreference, nn::Priority::DEFAULT, {}, - nnModelCache, nnDataCache, nnToken); + nnModelCache, nnDataCache, nnToken, {}, {}); notify(callback.get(), std::move(result), executor); }; executor(std::move(task), {}); @@ -215,7 +215,7 @@ nn::GeneralResult<void> prepareModel_1_3( nnModelCache = std::move(nnModelCache), nnDataCache = std::move(nnDataCache), nnToken, executor, callback] { auto result = device->prepareModel(nnModel, nnPreference, nnPriority, nnDeadline, - nnModelCache, nnDataCache, nnToken); + nnModelCache, nnDataCache, nnToken, {}, {}); notify(callback.get(), std::move(result), executor); }; executor(std::move(task), nnDeadline); diff --git a/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp b/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp index 71060d5ca7..c6055a6747 100644 --- a/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp +++ b/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp @@ -159,7 +159,7 @@ nn::GeneralResult<void> execute(const nn::SharedPreparedModel& preparedModel, } Task task = [preparedModel, nnRequest = std::move(nnRequest), callback] { - auto result = preparedModel->execute(nnRequest, nn::MeasureTiming::NO, {}, {}); + auto result = preparedModel->execute(nnRequest, nn::MeasureTiming::NO, {}, {}, {}, {}); notify(callback.get(), std::move(result)); }; executor(std::move(task), {}); @@ -185,7 +185,7 @@ nn::GeneralResult<void> execute_1_2(const nn::SharedPreparedModel& preparedModel } Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, callback] { - auto result = preparedModel->execute(nnRequest, nnMeasure, {}, {}); + auto result = preparedModel->execute(nnRequest, nnMeasure, {}, {}, {}, {}); notify(callback.get(), std::move(result)); }; executor(std::move(task), {}); @@ -216,8 +216,8 @@ nn::GeneralResult<void> execute_1_3(const nn::SharedPreparedModel& preparedModel Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, nnDeadline, nnLoopTimeoutDuration, callback] { - auto result = - preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration); + auto result = preparedModel->execute(nnRequest, nnMeasure, nnDeadline, + nnLoopTimeoutDuration, {}, {}); notify(callback.get(), std::move(result)); }; executor(std::move(task), nnDeadline); @@ -232,7 +232,7 @@ nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> execut const auto nnMeasure = NN_TRY(convertInput(measure)); const auto [outputShapes, timing] = - NN_TRY(preparedModel->execute(nnRequest, nnMeasure, {}, {})); + NN_TRY(preparedModel->execute(nnRequest, nnMeasure, {}, {}, {}, {})); auto hidlOutputShapes = NN_TRY(V1_2::utils::convert(outputShapes)); const auto hidlTiming = NN_TRY(V1_2::utils::convert(timing)); @@ -248,8 +248,8 @@ nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> execut const auto nnDeadline = NN_TRY(convertInput(deadline)); const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration)); - const auto [outputShapes, timing] = - NN_TRY(preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration)); + const auto [outputShapes, timing] = NN_TRY(preparedModel->execute( + nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration, {}, {})); auto hidlOutputShapes = NN_TRY(V1_3::utils::convert(outputShapes)); const auto hidlTiming = NN_TRY(V1_3::utils::convert(timing)); @@ -293,8 +293,9 @@ nn::GeneralResult<std::pair<hidl_handle, sp<V1_3::IFencedExecutionCallback>>> ex const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration)); const auto nnDuration = NN_TRY(convertInput(duration)); - auto [syncFence, executeFencedCallback] = NN_TRY(preparedModel->executeFenced( - nnRequest, nnWaitFor, nnMeasure, nnDeadline, nnLoopTimeoutDuration, nnDuration)); + auto [syncFence, executeFencedCallback] = + NN_TRY(preparedModel->executeFenced(nnRequest, nnWaitFor, nnMeasure, nnDeadline, + nnLoopTimeoutDuration, nnDuration, {}, {})); auto hidlSyncFence = NN_TRY(V1_3::utils::convert(syncFence.getSharedHandle())); auto hidlExecuteFencedCallback = sp<FencedExecutionCallback>::make(executeFencedCallback); diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp index 39927a363c..bfba24fc1f 100644 --- a/neuralnetworks/utils/common/Android.bp +++ b/neuralnetworks/utils/common/Android.bp @@ -39,20 +39,12 @@ cc_test { srcs: ["test/*.cpp"], static_libs: [ "libgmock", - "libneuralnetworks_common", "neuralnetworks_types", "neuralnetworks_utils_hal_common", ], shared_libs: [ - "android.hidl.allocator@1.0", - "android.hidl.memory@1.0", "libbase", "libcutils", - "libfmq", - "libhidlbase", - "libhidlmemory", - "liblog", - "libutils", ], target: { android: { diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h index e86eddab88..1f1245f1bd 100644 --- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h +++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h @@ -33,12 +33,15 @@ class InvalidBurst final : public nn::IBurst { nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; }; } // namespace android::hardware::neuralnetworks::utils diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h index 5e62b9ae0b..9582873009 100644 --- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h +++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h @@ -52,8 +52,9 @@ class InvalidDevice final : public nn::IDevice { nn::GeneralResult<nn::SharedPreparedModel> prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, - const nn::CacheToken& token) const override; + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache( nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h index de30aaefc9..3f1f2904d8 100644 --- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h +++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h @@ -31,18 +31,23 @@ class InvalidPreparedModel final : public nn::IPreparedModel { public: nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced( const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const override; + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override; diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h index fde2486a53..129431f3cf 100644 --- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h +++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h @@ -48,18 +48,23 @@ class ResilientBurst final : public nn::IBurst, nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; private: bool isValidInternal() const EXCLUDES(mMutex); nn::GeneralResult<nn::SharedExecution> createReusableExecutionInternal( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const; const Factory kMakeBurst; mutable std::mutex mMutex; diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h index 84ae799aad..267d6346cf 100644 --- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h +++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h @@ -65,8 +65,9 @@ class ResilientDevice final : public nn::IDevice, nn::GeneralResult<nn::SharedPreparedModel> prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, - const nn::CacheToken& token) const override; + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache( nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, @@ -83,7 +84,9 @@ class ResilientDevice final : public nn::IDevice, nn::GeneralResult<nn::SharedPreparedModel> prepareModelInternal( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const; + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const; nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCacheInternal( nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const; diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h index 86533edd12..bbfc220a3b 100644 --- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h +++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h @@ -49,18 +49,23 @@ class ResilientPreparedModel final : public nn::IPreparedModel, nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced( const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const override; + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedExecution> createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const override; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const override; nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override; @@ -70,7 +75,9 @@ class ResilientPreparedModel final : public nn::IPreparedModel, bool isValidInternal() const EXCLUDES(mMutex); nn::GeneralResult<nn::SharedExecution> createReusableExecutionInternal( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const; + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& metaData, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const; nn::GeneralResult<nn::SharedBurst> configureExecutionBurstInternal() const; const Factory kMakePreparedModel; diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp index 01915337c4..3fdfb5cec3 100644 --- a/neuralnetworks/utils/common/src/InvalidBurst.cpp +++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp @@ -34,13 +34,17 @@ InvalidBurst::OptionalCacheHold InvalidBurst::cacheMemory( nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> InvalidBurst::execute( const nn::Request& /*request*/, nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/) const { + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { return NN_ERROR() << "InvalidBurst"; } nn::GeneralResult<nn::SharedExecution> InvalidBurst::createReusableExecution( const nn::Request& /*request*/, nn::MeasureTiming /*measure*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/) const { + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { return NN_ERROR() << "InvalidBurst"; } diff --git a/neuralnetworks/utils/common/src/InvalidDevice.cpp b/neuralnetworks/utils/common/src/InvalidDevice.cpp index 535ccb41c7..c8cc287573 100644 --- a/neuralnetworks/utils/common/src/InvalidDevice.cpp +++ b/neuralnetworks/utils/common/src/InvalidDevice.cpp @@ -84,7 +84,9 @@ nn::GeneralResult<nn::SharedPreparedModel> InvalidDevice::prepareModel( const nn::Model& /*model*/, nn::ExecutionPreference /*preference*/, nn::Priority /*priority*/, nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/, - const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const { + const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { return NN_ERROR() << "InvalidDevice"; } diff --git a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp index 8195462ba8..f6f978d8c8 100644 --- a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp +++ b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp @@ -27,9 +27,12 @@ namespace android::hardware::neuralnetworks::utils { nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> -InvalidPreparedModel::execute(const nn::Request& /*request*/, nn::MeasureTiming /*measure*/, - const nn::OptionalTimePoint& /*deadline*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/) const { +InvalidPreparedModel::execute( + const nn::Request& /*request*/, nn::MeasureTiming /*measure*/, + const nn::OptionalTimePoint& /*deadline*/, + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { return NN_ERROR() << "InvalidPreparedModel"; } @@ -38,13 +41,17 @@ InvalidPreparedModel::executeFenced( const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/, nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/, const nn::OptionalDuration& /*loopTimeoutDuration*/, - const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const { + const nn::OptionalDuration& /*timeoutDurationAfterFence*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { return NN_ERROR() << "InvalidPreparedModel"; } nn::GeneralResult<nn::SharedExecution> InvalidPreparedModel::createReusableExecution( const nn::Request& /*request*/, nn::MeasureTiming /*measure*/, - const nn::OptionalDuration& /*loopTimeoutDuration*/) const { + const nn::OptionalDuration& /*loopTimeoutDuration*/, + const std::vector<nn::TokenValuePair>& /*hints*/, + const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const { return NN_ERROR() << "InvalidPreparedModel"; } diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp index 79cbe3991f..bf7a8ea130 100644 --- a/neuralnetworks/utils/common/src/ResilientBurst.cpp +++ b/neuralnetworks/utils/common/src/ResilientBurst.cpp @@ -105,37 +105,49 @@ ResilientBurst::OptionalCacheHold ResilientBurst::cacheMemory( nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> ResilientBurst::execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { - const auto fn = [&request, measure, deadline, loopTimeoutDuration](const nn::IBurst& burst) { - return burst.execute(request, measure, deadline, loopTimeoutDuration); + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { + const auto fn = [&request, measure, deadline, loopTimeoutDuration, &hints, + &extensionNameToPrefix](const nn::IBurst& burst) { + return burst.execute(request, measure, deadline, loopTimeoutDuration, hints, + extensionNameToPrefix); }; return protect(*this, fn); } nn::GeneralResult<nn::SharedExecution> ResilientBurst::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { #if 0 auto self = shared_from_this(); - ResilientExecution::Factory makeExecution = - [burst = std::move(self), request, measure, loopTimeoutDuration] { - return burst->createReusableExecutionInternal(request, measure, loopTimeoutDuration); + ResilientExecution::Factory makeExecution = [burst = std::move(self), request, measure, + loopTimeoutDuration, &hints, + &extensionNameToPrefix] { + return burst->createReusableExecutionInternal(request, measure, loopTimeoutDuration, hints, + extensionNameToPrefix); }; return ResilientExecution::create(std::move(makeExecution)); #else - return createReusableExecutionInternal(request, measure, loopTimeoutDuration); + return createReusableExecutionInternal(request, measure, loopTimeoutDuration, hints, + extensionNameToPrefix); #endif } nn::GeneralResult<nn::SharedExecution> ResilientBurst::createReusableExecutionInternal( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { if (!isValidInternal()) { return std::make_shared<const InvalidExecution>(); } - const auto fn = [&request, measure, &loopTimeoutDuration](const nn::IBurst& burst) { - return burst.createReusableExecution(request, measure, loopTimeoutDuration); + const auto fn = [&request, measure, &loopTimeoutDuration, &hints, + &extensionNameToPrefix](const nn::IBurst& burst) { + return burst.createReusableExecution(request, measure, loopTimeoutDuration, hints, + extensionNameToPrefix); }; return protect(*this, fn); } diff --git a/neuralnetworks/utils/common/src/ResilientDevice.cpp b/neuralnetworks/utils/common/src/ResilientDevice.cpp index 2023c9af30..a5c2640b76 100644 --- a/neuralnetworks/utils/common/src/ResilientDevice.cpp +++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp @@ -179,19 +179,21 @@ nn::GeneralResult<std::vector<bool>> ResilientDevice::getSupportedOperations( nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const { + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { #if 0 auto self = shared_from_this(); ResilientPreparedModel::Factory makePreparedModel = [device = std::move(self), model, preference, priority, deadline, modelCache, - dataCache, token] { + dataCache, token, hints, extensionNameToPrefix] { return device->prepareModelInternal(model, preference, priority, deadline, modelCache, - dataCache, token); + dataCache, token, hints, extensionNameToPrefix); }; return ResilientPreparedModel::create(std::move(makePreparedModel)); #else - return prepareModelInternal(model, preference, priority, deadline, modelCache, dataCache, - token); + return prepareModelInternal(model, preference, priority, deadline, modelCache, dataCache, token, + hints, extensionNameToPrefix); #endif } @@ -234,14 +236,16 @@ bool ResilientDevice::isValidInternal() const { nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelInternal( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const { + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { if (!isValidInternal()) { return std::make_shared<const InvalidPreparedModel>(); } - const auto fn = [&model, preference, priority, &deadline, &modelCache, &dataCache, - &token](const nn::IDevice& device) { + const auto fn = [&model, preference, priority, &deadline, &modelCache, &dataCache, &token, + &hints, &extensionNameToPrefix](const nn::IDevice& device) { return device.prepareModel(model, preference, priority, deadline, modelCache, dataCache, - token); + token, hints, extensionNameToPrefix); }; return protect(*this, fn, /*blocking=*/false); } diff --git a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp index 1ae19bc6ca..b5843c0fd4 100644 --- a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp +++ b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp @@ -104,43 +104,53 @@ nn::GeneralResult<nn::SharedPreparedModel> ResilientPreparedModel::recover( } nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> -ResilientPreparedModel::execute(const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { - const auto fn = [&request, measure, &deadline, - &loopTimeoutDuration](const nn::IPreparedModel& preparedModel) { - return preparedModel.execute(request, measure, deadline, loopTimeoutDuration); +ResilientPreparedModel::execute( + const nn::Request& request, nn::MeasureTiming measure, + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { + const auto fn = [&request, measure, &deadline, &loopTimeoutDuration, &hints, + &extensionNameToPrefix](const nn::IPreparedModel& preparedModel) { + return preparedModel.execute(request, measure, deadline, loopTimeoutDuration, hints, + extensionNameToPrefix); }; return protect(*this, fn); } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> -ResilientPreparedModel::executeFenced(const nn::Request& request, - const std::vector<nn::SyncFence>& waitFor, - nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const { +ResilientPreparedModel::executeFenced( + const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, + nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, + const nn::OptionalDuration& loopTimeoutDuration, + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { const auto fn = [&request, &waitFor, measure, &deadline, &loopTimeoutDuration, - &timeoutDurationAfterFence](const nn::IPreparedModel& preparedModel) { + &timeoutDurationAfterFence, &hints, + &extensionNameToPrefix](const nn::IPreparedModel& preparedModel) { return preparedModel.executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration, - timeoutDurationAfterFence); + timeoutDurationAfterFence, hints, extensionNameToPrefix); }; return protect(*this, fn); } nn::GeneralResult<nn::SharedExecution> ResilientPreparedModel::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { #if 0 auto self = shared_from_this(); - ResilientExecution::Factory makeExecution = - [preparedModel = std::move(self), request, measure, loopTimeoutDuration] { - return preparedModel->createReusableExecutionInternal(request, measure, loopTimeoutDuration); + ResilientExecution::Factory makeExecution = [preparedModel = std::move(self), request, measure, + loopTimeoutDuration, hints, + extensionNameToPrefix] { + return preparedModel->createReusableExecutionInternal(request, measure, loopTimeoutDuration, + hints, extensionNameToPrefix); }; return ResilientExecution::create(std::move(makeExecution)); #else - return createReusableExecutionInternal(request, measure, loopTimeoutDuration); + return createReusableExecutionInternal(request, measure, loopTimeoutDuration, hints, + extensionNameToPrefix); #endif } @@ -159,13 +169,16 @@ nn::GeneralResult<nn::SharedBurst> ResilientPreparedModel::configureExecutionBur nn::GeneralResult<nn::SharedExecution> ResilientPreparedModel::createReusableExecutionInternal( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { if (!isValidInternal()) { return std::make_shared<const InvalidExecution>(); } - const auto fn = [&request, measure, - &loopTimeoutDuration](const nn::IPreparedModel& preparedModel) { - return preparedModel.createReusableExecution(request, measure, loopTimeoutDuration); + const auto fn = [&request, measure, &loopTimeoutDuration, &hints, + &extensionNameToPrefix](const nn::IPreparedModel& preparedModel) { + return preparedModel.createReusableExecution(request, measure, loopTimeoutDuration, hints, + extensionNameToPrefix); }; return protect(*this, fn); } diff --git a/neuralnetworks/utils/common/test/MockDevice.h b/neuralnetworks/utils/common/test/MockDevice.h index a9428bccfc..a0fc5c3de1 100644 --- a/neuralnetworks/utils/common/test/MockDevice.h +++ b/neuralnetworks/utils/common/test/MockDevice.h @@ -39,7 +39,9 @@ class MockDevice final : public IDevice { MOCK_METHOD(GeneralResult<SharedPreparedModel>, prepareModel, (const Model& model, ExecutionPreference preference, Priority priority, OptionalTimePoint deadline, const std::vector<SharedHandle>& modelCache, - const std::vector<SharedHandle>& dataCache, const CacheToken& token), + const std::vector<SharedHandle>& dataCache, const CacheToken& token, + const std::vector<TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix), (const, override)); MOCK_METHOD(GeneralResult<SharedPreparedModel>, prepareModelFromCache, (OptionalTimePoint deadline, const std::vector<SharedHandle>& modelCache, diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h index c8ce006171..b8613b226c 100644 --- a/neuralnetworks/utils/common/test/MockPreparedModel.h +++ b/neuralnetworks/utils/common/test/MockPreparedModel.h @@ -27,17 +27,23 @@ class MockPreparedModel final : public IPreparedModel { public: MOCK_METHOD((ExecutionResult<std::pair<std::vector<OutputShape>, Timing>>), execute, (const Request& request, MeasureTiming measure, const OptionalTimePoint& deadline, - const OptionalDuration& loopTimeoutDuration), + const OptionalDuration& loopTimeoutDuration, + const std::vector<TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix), (const, override)); MOCK_METHOD((GeneralResult<std::pair<SyncFence, ExecuteFencedInfoCallback>>), executeFenced, (const Request& request, const std::vector<SyncFence>& waitFor, MeasureTiming measure, const OptionalTimePoint& deadline, const OptionalDuration& loopTimeoutDuration, - const OptionalDuration& timeoutDurationAfterFence), + const OptionalDuration& timeoutDurationAfterFence, + const std::vector<TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix), (const, override)); MOCK_METHOD((GeneralResult<SharedExecution>), createReusableExecution, - (const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration), + (const Request& request, MeasureTiming measure, + const OptionalDuration& loopTimeoutDuration, + const std::vector<TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix), (const, override)); MOCK_METHOD(GeneralResult<SharedBurst>, configureExecutionBurst, (), (const, override)); MOCK_METHOD(std::any, getUnderlyingResource, (), (const, override)); diff --git a/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp b/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp index 0488b6359b..d9b8505c0e 100644 --- a/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp +++ b/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp @@ -309,12 +309,12 @@ TEST(ResilientDeviceTest, prepareModel) { // setup call const auto [mockDevice, mockDeviceFactory, device] = setup(); const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>(); - EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _)) + EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _, _)) .Times(1) .WillOnce(Return(mockPreparedModel)); // run test - const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}); + const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -324,12 +324,12 @@ TEST(ResilientDeviceTest, prepareModel) { TEST(ResilientDeviceTest, prepareModelError) { // setup call const auto [mockDevice, mockDeviceFactory, device] = setup(); - EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _)) + EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _, _)) .Times(1) .WillOnce(kReturnGeneralFailure); // run test - const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}); + const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -339,13 +339,13 @@ TEST(ResilientDeviceTest, prepareModelError) { TEST(ResilientDeviceTest, prepareModelDeadObjectFailedRecovery) { // setup call const auto [mockDevice, mockDeviceFactory, device] = setup(); - EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _)) + EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _, _)) .Times(1) .WillOnce(kReturnDeadObject); EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure); // run test - const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}); + const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -355,18 +355,18 @@ TEST(ResilientDeviceTest, prepareModelDeadObjectFailedRecovery) { TEST(ResilientDeviceTest, prepareModelDeadObjectSuccessfulRecovery) { // setup call const auto [mockDevice, mockDeviceFactory, device] = setup(); - EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _)) + EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _, _)) .Times(1) .WillOnce(kReturnDeadObject); const auto recoveredMockDevice = createConfiguredMockDevice(); const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>(); - EXPECT_CALL(*recoveredMockDevice, prepareModel(_, _, _, _, _, _, _)) + EXPECT_CALL(*recoveredMockDevice, prepareModel(_, _, _, _, _, _, _, _, _)) .Times(1) .WillOnce(Return(mockPreparedModel)); EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice)); // run test - const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}); + const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -679,7 +679,7 @@ TEST(ResilientDeviceTest, recoverCacheMismatchInvalidPrepareModel) { device->recover(mockDevice.get(), /*blocking=*/false); // run test - auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}); + auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) diff --git a/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp index d396ca88df..276bfba4ef 100644 --- a/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp +++ b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp @@ -104,12 +104,12 @@ TEST(ResilientPreparedModelTest, getPreparedModel) { TEST(ResilientPreparedModelTest, execute) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)) + EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(kNoExecutionError)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -119,10 +119,12 @@ TEST(ResilientPreparedModelTest, execute) { TEST(ResilientPreparedModelTest, executeError) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnGeneralFailure); + EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _, _, _)) + .Times(1) + .WillOnce(kReturnGeneralFailure); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -132,12 +134,12 @@ TEST(ResilientPreparedModelTest, executeError) { TEST(ResilientPreparedModelTest, executeDeadObjectFailedRecovery) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject); + EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _, _, _)).Times(1).WillOnce(kReturnDeadObject); constexpr auto ret = [] { return nn::error(nn::ErrorStatus::GENERAL_FAILURE); }; EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(ret); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -147,9 +149,9 @@ TEST(ResilientPreparedModelTest, executeDeadObjectFailedRecovery) { TEST(ResilientPreparedModelTest, executeDeadObjectSuccessfulRecovery) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject); + EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _, _, _)).Times(1).WillOnce(kReturnDeadObject); const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel(); - EXPECT_CALL(*recoveredMockPreparedModel, execute(_, _, _, _)) + EXPECT_CALL(*recoveredMockPreparedModel, execute(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(kNoExecutionError)); EXPECT_CALL(*mockPreparedModelFactory, Call()) @@ -157,7 +159,7 @@ TEST(ResilientPreparedModelTest, executeDeadObjectSuccessfulRecovery) { .WillOnce(Return(recoveredMockPreparedModel)); // run test - const auto result = preparedModel->execute({}, {}, {}, {}); + const auto result = preparedModel->execute({}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -167,12 +169,12 @@ TEST(ResilientPreparedModelTest, executeDeadObjectSuccessfulRecovery) { TEST(ResilientPreparedModelTest, executeFenced) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _)) + EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _, _)) .Times(1) .WillOnce(Return(kNoFencedExecutionError)); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -182,12 +184,12 @@ TEST(ResilientPreparedModelTest, executeFenced) { TEST(ResilientPreparedModelTest, executeFencedError) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _)) + EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _, _)) .Times(1) .WillOnce(kReturnGeneralFailure); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -197,13 +199,13 @@ TEST(ResilientPreparedModelTest, executeFencedError) { TEST(ResilientPreparedModelTest, executeFencedDeadObjectFailedRecovery) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _)) + EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _, _)) .Times(1) .WillOnce(kReturnDeadObject); EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); @@ -213,11 +215,11 @@ TEST(ResilientPreparedModelTest, executeFencedDeadObjectFailedRecovery) { TEST(ResilientPreparedModelTest, executeFencedDeadObjectSuccessfulRecovery) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _)) + EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _, _)) .Times(1) .WillOnce(kReturnDeadObject); const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel(); - EXPECT_CALL(*recoveredMockPreparedModel, executeFenced(_, _, _, _, _, _)) + EXPECT_CALL(*recoveredMockPreparedModel, executeFenced(_, _, _, _, _, _, _, _)) .Times(1) .WillOnce(Return(kNoFencedExecutionError)); EXPECT_CALL(*mockPreparedModelFactory, Call()) @@ -225,7 +227,7 @@ TEST(ResilientPreparedModelTest, executeFencedDeadObjectSuccessfulRecovery) { .WillOnce(Return(recoveredMockPreparedModel)); // run test - const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}); + const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -235,12 +237,12 @@ TEST(ResilientPreparedModelTest, executeFencedDeadObjectSuccessfulRecovery) { TEST(ResilientPreparedModelTest, createReusableExecution) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _)) + EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _, _)) .Times(1) .WillOnce(Return(kNoCreateReusableExecutionError)); // run test - const auto result = preparedModel->createReusableExecution({}, {}, {}); + const auto result = preparedModel->createReusableExecution({}, {}, {}, {}, {}); // verify result ASSERT_TRUE(result.has_value()) @@ -250,12 +252,12 @@ TEST(ResilientPreparedModelTest, createReusableExecution) { TEST(ResilientPreparedModelTest, createReusableExecutionError) { // setup call const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup(); - EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _)) + EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _, _)) .Times(1) .WillOnce(kReturnGeneralFailure); // run test - const auto result = preparedModel->createReusableExecution({}, {}, {}); + const auto result = preparedModel->createReusableExecution({}, {}, {}, {}, {}); // verify result ASSERT_FALSE(result.has_value()); diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp index c3272ae430..452078b7d4 100644 --- a/neuralnetworks/utils/service/Android.bp +++ b/neuralnetworks/utils/service/Android.bp @@ -33,6 +33,10 @@ cc_library_static { local_include_dirs: ["include/nnapi/hal"], export_include_dirs: ["include"], static_libs: [ + "android.hardware.neuralnetworks@1.0", + "android.hardware.neuralnetworks@1.1", + "android.hardware.neuralnetworks@1.2", + "android.hardware.neuralnetworks@1.3", "neuralnetworks_types", "neuralnetworks_utils_hal_1_0", "neuralnetworks_utils_hal_1_1", @@ -40,10 +44,4 @@ cc_library_static { "neuralnetworks_utils_hal_1_3", "neuralnetworks_utils_hal_common", ], - shared_libs: [ - "android.hardware.neuralnetworks@1.0", - "android.hardware.neuralnetworks@1.1", - "android.hardware.neuralnetworks@1.2", - "android.hardware.neuralnetworks@1.3", - ], } diff --git a/nfc/OWNERS b/nfc/OWNERS new file mode 100644 index 0000000000..78672046e4 --- /dev/null +++ b/nfc/OWNERS @@ -0,0 +1,6 @@ +# Bug component: 48448 +alisher@google.com +georgekgchang@google.com +jackcwyu@google.com + + diff --git a/nfc/aidl/Android.bp b/nfc/aidl/Android.bp new file mode 100644 index 0000000000..d390c7ea05 --- /dev/null +++ b/nfc/aidl/Android.bp @@ -0,0 +1,34 @@ +// Copyright (C) 2021 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. + +aidl_interface { + name: "android.hardware.nfc", + vendor_available: true, + srcs: ["android/hardware/nfc/*.aidl"], + stability: "vintf", + backend: { + cpp: { + enabled: false, + }, + java: { + sdk_version: "module_current", + enabled: false, + }, + ndk: { + vndk: { + enabled: true, + }, + }, + }, +} diff --git a/nfc/aidl/TEST_MAPPING b/nfc/aidl/TEST_MAPPING new file mode 100644 index 0000000000..3a1008487b --- /dev/null +++ b/nfc/aidl/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "VtsAidlHalNfcTargetTest" + } + ] +} diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl new file mode 100644 index 0000000000..7a0ae54963 --- /dev/null +++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 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 <name>-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.nfc; +@VintfStability +interface INfc { + void open(in android.hardware.nfc.INfcClientCallback clientCallback); + void close(in android.hardware.nfc.NfcCloseType type); + void coreInitialized(); + void factoryReset(); + android.hardware.nfc.NfcConfig getConfig(); + void powerCycle(); + void preDiscover(); + int write(in byte[] data); + void setEnableVerboseLogging(in boolean enable); + boolean isVerboseLoggingEnabled(); +} diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl new file mode 100644 index 0000000000..8150e818d7 --- /dev/null +++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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 <name>-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.nfc; +@VintfStability +interface INfcClientCallback { + void sendData(in byte[] data); + void sendEvent(in android.hardware.nfc.NfcEvent event, in android.hardware.nfc.NfcStatus status); +} diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl new file mode 100644 index 0000000000..7d44d48e3e --- /dev/null +++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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 <name>-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.nfc; +@Backing(type="int") @VintfStability +enum NfcCloseType { + DISABLE = 0, + HOST_SWITCHED_OFF = 1, +} diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl new file mode 100644 index 0000000000..92e0a9a82c --- /dev/null +++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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 <name>-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.nfc; +@VintfStability +parcelable NfcConfig { + boolean nfaPollBailOutMode; + android.hardware.nfc.PresenceCheckAlgorithm presenceCheckAlgorithm; + android.hardware.nfc.ProtocolDiscoveryConfig nfaProprietaryCfg; + byte defaultOffHostRoute; + byte defaultOffHostRouteFelica; + byte defaultSystemCodeRoute; + byte defaultSystemCodePowerState; + byte defaultRoute; + byte offHostESEPipeId; + byte offHostSIMPipeId; + int maxIsoDepTransceiveLength; + byte[] hostAllowlist; + byte[] offHostRouteUicc; + byte[] offHostRouteEse; + byte defaultIsoDepRoute; +} diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl new file mode 100644 index 0000000000..dda258eb08 --- /dev/null +++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 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 <name>-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.nfc; +@Backing(type="int") @VintfStability +enum NfcEvent { + OPEN_CPLT = 0, + CLOSE_CPLT = 1, + POST_INIT_CPLT = 2, + PRE_DISCOVER_CPLT = 3, + HCI_NETWORK_RESET = 4, + ERROR = 5, +} diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.aidl new file mode 100644 index 0000000000..2632480bdb --- /dev/null +++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 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 <name>-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.nfc; +@Backing(type="int") @VintfStability +enum NfcStatus { + OK = 0, + FAILED = 1, + ERR_TRANSPORT = 2, + ERR_CMD_TIMEOUT = 3, + REFUSED = 4, +} diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl new file mode 100644 index 0000000000..9a9be215a6 --- /dev/null +++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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 <name>-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.nfc; +@Backing(type="byte") @VintfStability +enum PresenceCheckAlgorithm { + DEFAULT = 0, + I_BLOCK = 1, + ISO_DEP_NAK = 2, +} diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl new file mode 100644 index 0000000000..021dfe22d4 --- /dev/null +++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 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 <name>-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.nfc; +@VintfStability +parcelable ProtocolDiscoveryConfig { + byte protocol18092Active; + byte protocolBPrime; + byte protocolDual; + byte protocol15693; + byte protocolKovio; + byte protocolMifare; + byte discoveryPollKovio; + byte discoveryPollBPrime; + byte discoveryListenBPrime; +} diff --git a/nfc/aidl/android/hardware/nfc/INfc.aidl b/nfc/aidl/android/hardware/nfc/INfc.aidl new file mode 100644 index 0000000000..662f8d4c8f --- /dev/null +++ b/nfc/aidl/android/hardware/nfc/INfc.aidl @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2021 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.nfc; + +import android.hardware.nfc.INfcClientCallback; +import android.hardware.nfc.NfcCloseType; +import android.hardware.nfc.NfcConfig; +import android.hardware.nfc.NfcStatus; + +@VintfStability +interface INfc { + /** + * Opens the NFC controller device and performs initialization. + * This may include patch download and other vendor-specific initialization. + * + * If open completes successfully, the controller must be ready to perform NCI + * initialization - ie accept CORE_RESET and subsequent commands through the write() + * call. + * + * Returns ok() if initialization starts successfully. + * If open() returns ok(), the NCI stack must wait for a NfcEvent.OPEN_CPLT + * callback before continuing. + * If a NfcEvent.OPEN_CPLT callback received with status NfcStatus::OK, the controller + * must be ready to perform NCI initialization - ie accept CORE_RESET and subsequent + * commands through the write() call. + */ + void open(in INfcClientCallback clientCallback); + + /** + * Close the NFC HAL and setup NFC controller close type. + * Associated resources such as clientCallback must be released. + * The clientCallback reference from open() must be invalid after close(). + * If close() returns ok(), the NCI stack must wait for a NfcEvent.CLOSE_CPLT + * callback before continuing. + * Returns an error if close may be called more than once. + * Calls to any other method which expect a callback after this must return + * a service-specific error NfcStatus::FAILED. + * HAL close automatically if the client drops the reference to the HAL or + * crashes. + */ + void close(in NfcCloseType type); + + /** + * coreInitialized() is called after the CORE_INIT_RSP is received from the + * NFCC. At this time, the HAL can do any chip-specific configuration. + * + * If coreInitialized() returns ok(), the NCI stack must wait for a + * NfcEvent.POST_INIT_CPLT before continuing. + * If coreInitialized() returns an error, the NCI stack must continue immediately. + * + * coreInitialized() must be called after open() registers the clientCallback + * or return a service-specific error NfcStatus::FAILED directly. + * + */ + void coreInitialized(); + + /** + * Clears the NFC chip. + * + * Must be called during factory reset and/or before the first time the HAL is + * initialized after a factory reset. + */ + void factoryReset(); + + /** + * Fetches vendor specific configurations. + * @return NfcConfig indicates support for certain features and + * populates the vendor specific configs. + */ + NfcConfig getConfig(); + + /** + * Restart controller by power cyle; + * It's similar to open but just reset the controller without initialize all the + * resources. + * + * If powerCycle() returns ok(), the NCI stack must wait for a NfcEvent.OPEN_CPLT + * before continuing. + * + * powerCycle() must be called after open() registers the clientCallback + * or return a service-specific error NfcStatus::FAILED directly. + */ + void powerCycle(); + + /** + * preDiscover is called every time before starting RF discovery. + * It is a good place to do vendor-specific configuration that must be + * performed every time RF discovery is about to be started. + * + * If preDiscover() returns ok(), the NCI stack must wait for a + * NfcEvent.PREDISCOVER_CPLT before continuing. + * + * preDiscover() must be called after open() registers the clientCallback + * or return a service-specific error NfcStatus::FAILED directly. + * + * If preDiscover() reports an error, the NCI stack must start RF discovery immediately. + */ + void preDiscover(); + + /** + * Performs an NCI write. + * + * This method may queue writes and return immediately. The only + * requirement is that the writes are executed in order. + * + * @param data + * Data packet to transmit NCI Commands and Data Messages over write. + * Detailed format is defined in NFC Controller Interface (NCI) Technical Specification. + * https://nfc-forum.org/our-work/specification-releases/ + * + * @return number of bytes written to the NFCC. + */ + int write(in byte[] data); + + /** + * Set the logging flag for NFC HAL to enable it's verbose logging. + * If verbose logging is not supported, the call must not have any effect on logging verbosity. + * However, isVerboseLoggingEnabled() must still return the value set by the last call to + * setEnableVerboseLogging(). + * @param enable for setting the verbose logging flag to HAL + */ + void setEnableVerboseLogging(in boolean enable); + + /** + * Get the verbose logging flag value from NFC HAL. + * @return true if verbose logging flag value is enabled, false if disabled. + */ + boolean isVerboseLoggingEnabled(); +} diff --git a/nfc/aidl/android/hardware/nfc/INfcClientCallback.aidl b/nfc/aidl/android/hardware/nfc/INfcClientCallback.aidl new file mode 100644 index 0000000000..43d4f5cc8b --- /dev/null +++ b/nfc/aidl/android/hardware/nfc/INfcClientCallback.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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.nfc; + +import android.hardware.nfc.NfcEvent; +import android.hardware.nfc.NfcStatus; + +@VintfStability +interface INfcClientCallback { + /** + * The callback passed in from the NFC stack that the HAL + * can use to pass incomming response data to the stack. + * + * @param data + * Data packet to transmit NCI Responses, Notifications, and Data + * Messages over sendData. + * Detailed format is defined in NFC Controller Interface (NCI) Technical Specification. + * https://nfc-forum.org/our-work/specification-releases/ + */ + void sendData(in byte[] data); + + /** + * The callback passed in from the NFC stack that the HAL + * can use to pass events back to the stack. + */ + void sendEvent(in NfcEvent event, in NfcStatus status); +} diff --git a/nfc/aidl/android/hardware/nfc/NfcCloseType.aidl b/nfc/aidl/android/hardware/nfc/NfcCloseType.aidl new file mode 100644 index 0000000000..5160532dc8 --- /dev/null +++ b/nfc/aidl/android/hardware/nfc/NfcCloseType.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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.nfc; + +/** + * Different closing types for NFC HAL. + */ +@VintfStability +@Backing(type="int") +enum NfcCloseType { + /** + * Close the NFC controller and free NFC HAL resources. + */ + DISABLE = 0, + + /** + * Switch the NFC controller operation mode and free NFC HAL resources. + * Enable NFC functionality for off host card emulation usecases in + * device(host) power off(switched off) state, if the device + * supports power off use cases. If the device doesn't support power + * off use cases, this call should be same as DISABLE. + */ + HOST_SWITCHED_OFF = 1, +} diff --git a/nfc/aidl/android/hardware/nfc/NfcConfig.aidl b/nfc/aidl/android/hardware/nfc/NfcConfig.aidl new file mode 100644 index 0000000000..1b4fcfb8e8 --- /dev/null +++ b/nfc/aidl/android/hardware/nfc/NfcConfig.aidl @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 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.nfc; + +import android.hardware.nfc.PresenceCheckAlgorithm; +import android.hardware.nfc.ProtocolDiscoveryConfig; + +/** + * Define Nfc related configurations based on: + * NFC Controller Interface (NCI) Technical Specification + * https://nfc-forum.org/our-work/specification-releases/ + */ +@VintfStability +parcelable NfcConfig { + /** + * If true, NFCC is using bail out mode for either Type A or Type B poll + * based on: Nfc-Forum Activity Technical Specification + * https://nfc-forum.org/our-work/specification-releases/ + */ + boolean nfaPollBailOutMode; + PresenceCheckAlgorithm presenceCheckAlgorithm; + ProtocolDiscoveryConfig nfaProprietaryCfg; + /** + * Default off-host route. 0x00 if there aren't any. Refer to NCI spec. + */ + byte defaultOffHostRoute; + /** + * Default off-host route for Felica. 0x00 if there aren't any. Refer to + * NCI spec. + */ + byte defaultOffHostRouteFelica; + /** + * Default system code route. 0x00 if there aren't any. Refer NCI spec. + */ + byte defaultSystemCodeRoute; + /** + * Default power state for system code route. 0x00 if there aren't any. + * Refer to NCI spec. + */ + byte defaultSystemCodePowerState; + /** + * Default route for all remaining protocols and technology which haven't + * been configured. + * Device Host(0x00) is the default. Refer to NCI spec. + * + */ + byte defaultRoute; + /** + * Pipe ID for eSE. 0x00 if there aren't any. + */ + byte offHostESEPipeId; + /** + * Pipe ID for UICC. 0x00 if there aren't any. + */ + byte offHostSIMPipeId; + /** + * Extended APDU length for ISO_DEP. If not supported default length is 261 + */ + int maxIsoDepTransceiveLength; + /** + * list of allowed host ids, as per ETSI TS 102 622 + * https://www.etsi.org/ + */ + byte[] hostAllowlist; + /** + * NFCEE ID for offhost UICC & eSE secure element. + * 0x00 if there aren't any. Refer to NCI spec. + */ + byte[] offHostRouteUicc; + byte[] offHostRouteEse; + /** + * Default IsoDep route. 0x00 if there aren't any. Refer to NCI spec. + */ + byte defaultIsoDepRoute; +} diff --git a/nfc/aidl/android/hardware/nfc/NfcEvent.aidl b/nfc/aidl/android/hardware/nfc/NfcEvent.aidl new file mode 100644 index 0000000000..a78b1cdb44 --- /dev/null +++ b/nfc/aidl/android/hardware/nfc/NfcEvent.aidl @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 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.nfc; + +/** + * Nfc event to notify nfc status change. + */ +@VintfStability +@Backing(type="int") +enum NfcEvent { + /** + * Open complete event to notify upper layer when NFC HAL and NFCC + * initialization complete. + */ + OPEN_CPLT = 0, + /** + * Close complete event to notify upper layer when HAL close is done. + */ + CLOSE_CPLT = 1, + /** + * Post init complete event to notify upper layer when post init operations + * are done. + */ + POST_INIT_CPLT = 2, + /** + * Pre-discover complete event to notify upper layer when pre-discover + * operations are done. + */ + PRE_DISCOVER_CPLT = 3, + /** + * HCI network reset event to notify upplayer when HCI network needs to + * be re-initialized in case of an error. + */ + HCI_NETWORK_RESET = 4, + /** + * Error event to notify upper layer when there's an unknown error. + */ + ERROR = 5, +} diff --git a/nfc/aidl/android/hardware/nfc/NfcStatus.aidl b/nfc/aidl/android/hardware/nfc/NfcStatus.aidl new file mode 100644 index 0000000000..a38d370f85 --- /dev/null +++ b/nfc/aidl/android/hardware/nfc/NfcStatus.aidl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 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.nfc; + +/** + * Used to specify the status of the NfcEvent + */ +@VintfStability +@Backing(type="int") +enum NfcStatus { + /** + * Default status when NfcEvent with status OK. + */ + OK = 0, + /** + * Generic error. + */ + FAILED = 1, + /** + * Transport error. + */ + ERR_TRANSPORT = 2, + /** + * Command timeout error. + */ + ERR_CMD_TIMEOUT = 3, + /** + * Refused error when command is rejected. + */ + REFUSED = 4, +} diff --git a/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl b/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl new file mode 100644 index 0000000000..20e7bffbfe --- /dev/null +++ b/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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.nfc; + +/* + * Presence Check Algorithm as defined in ISO/IEC 14443-4: + * https://www.iso.org/standard/73599.html + */ +@VintfStability +@Backing(type="byte") +enum PresenceCheckAlgorithm { + /** + * Let the stack select an algorithm + */ + DEFAULT = 0, + /** + * ISO-DEP protocol's empty I-block + */ + I_BLOCK = 1, + /** + * Type - 4 tag protocol iso-dep nak presence check command is sent waiting for + * response and notification. + */ + ISO_DEP_NAK = 2, +} diff --git a/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl b/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl new file mode 100644 index 0000000000..f8e32288e6 --- /dev/null +++ b/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 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.nfc; + +/** + * Vendor Specific Proprietary Protocol & Discovery Configuration. + * Set to 0xFF if not supported. + * discovery* fields map to "RF Technology and Mode" in NCI Spec + * protocol* fields map to "RF protocols" in NCI Spec + */ +@VintfStability +parcelable ProtocolDiscoveryConfig { + byte protocol18092Active; + byte protocolBPrime; + byte protocolDual; + byte protocol15693; + byte protocolKovio; + byte protocolMifare; + byte discoveryPollKovio; + byte discoveryPollBPrime; + byte discoveryListenBPrime; +} diff --git a/nfc/aidl/default/Android.bp b/nfc/aidl/default/Android.bp new file mode 100644 index 0000000000..907d23d222 --- /dev/null +++ b/nfc/aidl/default/Android.bp @@ -0,0 +1,23 @@ +cc_binary { + name: "android.hardware.nfc-service.example", + relative_install_path: "hw", + init_rc: ["nfc-service-example.rc"], + vintf_fragments: ["nfc-service-example.xml"], + vendor: true, + cflags: [ + "-Wall", + "-Wextra", + ], + shared_libs: [ + "libbase", + "liblog", + "libutils", + "libbinder_ndk", + "android.hardware.nfc-V1-ndk", + ], + srcs: [ + "main.cpp", + "Nfc.cpp", + "Vendor_hal_api.cpp", + ], +} diff --git a/nfc/aidl/default/Nfc.cpp b/nfc/aidl/default/Nfc.cpp new file mode 100644 index 0000000000..4685b59236 --- /dev/null +++ b/nfc/aidl/default/Nfc.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2021 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 "Nfc.h" + +#include <android-base/logging.h> + +#include "Vendor_hal_api.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace nfc { + +std::shared_ptr<INfcClientCallback> Nfc::mCallback = nullptr; +AIBinder_DeathRecipient* clientDeathRecipient = nullptr; + +void OnDeath(void* cookie) { + if (Nfc::mCallback != nullptr && !AIBinder_isAlive(Nfc::mCallback->asBinder().get())) { + LOG(INFO) << __func__ << " Nfc service has died"; + Nfc* nfc = static_cast<Nfc*>(cookie); + nfc->close(NfcCloseType::DISABLE); + } +} + +::ndk::ScopedAStatus Nfc::open(const std::shared_ptr<INfcClientCallback>& clientCallback) { + LOG(INFO) << "open"; + if (clientCallback == nullptr) { + LOG(INFO) << "Nfc::open null callback"; + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); + } + Nfc::mCallback = clientCallback; + + clientDeathRecipient = AIBinder_DeathRecipient_new(OnDeath); + auto linkRet = AIBinder_linkToDeath(clientCallback->asBinder().get(), clientDeathRecipient, + this /* cookie */); + if (linkRet != STATUS_OK) { + LOG(ERROR) << __func__ << ": linkToDeath failed: " << linkRet; + // Just ignore the error. + } + + int ret = Vendor_hal_open(eventCallback, dataCallback); + return ret == 0 ? ndk::ScopedAStatus::ok() + : ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); +} + +::ndk::ScopedAStatus Nfc::close(NfcCloseType type) { + LOG(INFO) << "close"; + if (Nfc::mCallback == nullptr) { + LOG(ERROR) << __func__ << "mCallback null"; + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); + } + int ret = 0; + if (type == NfcCloseType::HOST_SWITCHED_OFF) { + ret = Vendor_hal_close_off(); + } else { + ret = Vendor_hal_close(); + } + Nfc::mCallback = nullptr; + AIBinder_DeathRecipient_delete(clientDeathRecipient); + clientDeathRecipient = nullptr; + return ret == 0 ? ndk::ScopedAStatus::ok() + : ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); +} + +::ndk::ScopedAStatus Nfc::coreInitialized() { + LOG(INFO) << "coreInitialized"; + if (Nfc::mCallback == nullptr) { + LOG(ERROR) << __func__ << "mCallback null"; + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); + } + int ret = Vendor_hal_core_initialized(); + + return ret == 0 ? ndk::ScopedAStatus::ok() + : ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); +} + +::ndk::ScopedAStatus Nfc::factoryReset() { + LOG(INFO) << "factoryReset"; + Vendor_hal_factoryReset(); + return ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus Nfc::getConfig(NfcConfig* _aidl_return) { + LOG(INFO) << "getConfig"; + NfcConfig nfcVendorConfig; + Vendor_hal_getConfig(nfcVendorConfig); + + *_aidl_return = nfcVendorConfig; + return ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus Nfc::powerCycle() { + LOG(INFO) << "powerCycle"; + if (Nfc::mCallback == nullptr) { + LOG(ERROR) << __func__ << "mCallback null"; + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); + } + return Vendor_hal_power_cycle() ? ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)) + : ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus Nfc::preDiscover() { + LOG(INFO) << "preDiscover"; + if (Nfc::mCallback == nullptr) { + LOG(ERROR) << __func__ << "mCallback null"; + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); + } + return Vendor_hal_pre_discover() ? ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)) + : ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus Nfc::write(const std::vector<uint8_t>& data, int32_t* _aidl_return) { + LOG(INFO) << "write"; + if (Nfc::mCallback == nullptr) { + LOG(ERROR) << __func__ << "mCallback null"; + return ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(NfcStatus::FAILED)); + } + *_aidl_return = Vendor_hal_write(data.size(), &data[0]); + return ndk::ScopedAStatus::ok(); +} +::ndk::ScopedAStatus Nfc::setEnableVerboseLogging(bool enable) { + LOG(INFO) << "setVerboseLogging"; + Vendor_hal_setVerboseLogging(enable); + return ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus Nfc::isVerboseLoggingEnabled(bool* _aidl_return) { + *_aidl_return = Vendor_hal_getVerboseLogging(); + return ndk::ScopedAStatus::ok(); +} + +} // namespace nfc +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/nfc/aidl/default/Nfc.h b/nfc/aidl/default/Nfc.h new file mode 100644 index 0000000000..1b14534e1a --- /dev/null +++ b/nfc/aidl/default/Nfc.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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 <aidl/android/hardware/nfc/BnNfc.h> +#include <aidl/android/hardware/nfc/INfcClientCallback.h> +#include <android-base/logging.h> +namespace aidl { +namespace android { +namespace hardware { +namespace nfc { + +using ::aidl::android::hardware::nfc::NfcCloseType; +using ::aidl::android::hardware::nfc::NfcConfig; +using ::aidl::android::hardware::nfc::NfcStatus; + +// Default implementation that reports no support NFC. +struct Nfc : public BnNfc { + public: + Nfc() = default; + + ::ndk::ScopedAStatus open(const std::shared_ptr<INfcClientCallback>& clientCallback) override; + ::ndk::ScopedAStatus close(NfcCloseType type) override; + ::ndk::ScopedAStatus coreInitialized() override; + ::ndk::ScopedAStatus factoryReset() override; + ::ndk::ScopedAStatus getConfig(NfcConfig* _aidl_return) override; + ::ndk::ScopedAStatus powerCycle() override; + ::ndk::ScopedAStatus preDiscover() override; + ::ndk::ScopedAStatus write(const std::vector<uint8_t>& data, int32_t* _aidl_return) override; + ::ndk::ScopedAStatus setEnableVerboseLogging(bool enable) override; + ::ndk::ScopedAStatus isVerboseLoggingEnabled(bool* _aidl_return) override; + + static void eventCallback(uint8_t event, uint8_t status) { + if (mCallback != nullptr) { + auto ret = mCallback->sendEvent((NfcEvent)event, (NfcStatus)status); + if (!ret.isOk()) { + LOG(ERROR) << "Failed to send event!"; + } + } + } + + static void dataCallback(uint16_t data_len, uint8_t* p_data) { + std::vector<uint8_t> data(p_data, p_data + data_len); + if (mCallback != nullptr) { + auto ret = mCallback->sendData(data); + if (!ret.isOk()) { + LOG(ERROR) << "Failed to send data!"; + } + } + } + + static std::shared_ptr<INfcClientCallback> mCallback; +}; + +} // namespace nfc +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/nfc/aidl/default/Vendor_hal_api.cpp b/nfc/aidl/default/Vendor_hal_api.cpp new file mode 100644 index 0000000000..66a2ebc5de --- /dev/null +++ b/nfc/aidl/default/Vendor_hal_api.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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 <android-base/properties.h> +#include <dlfcn.h> +#include <errno.h> +#include <string.h> + +#include "Vendor_hal_api.h" + +bool logging = false; + +int Vendor_hal_open(nfc_stack_callback_t* p_cback, nfc_stack_data_callback_t* p_data_cback) { + (void)p_cback; + (void)p_data_cback; + // nothing to open in this example + return -1; +} + +int Vendor_hal_write(uint16_t data_len, const uint8_t* p_data) { + (void)data_len; + (void)p_data; + return -1; +} + +int Vendor_hal_core_initialized() { + return -1; +} + +int Vendor_hal_pre_discover() { + return -1; +} + +int Vendor_hal_close() { + return -1; +} + +int Vendor_hal_close_off() { + return -1; +} + +int Vendor_hal_power_cycle() { + return -1; +} + +void Vendor_hal_factoryReset() {} + +void Vendor_hal_getConfig(NfcConfig& config) { + (void)config; +} + +void Vendor_hal_setVerboseLogging(bool enable) { + logging = enable; +} + +bool Vendor_hal_getVerboseLogging() { + return logging; +} diff --git a/nfc/aidl/default/Vendor_hal_api.h b/nfc/aidl/default/Vendor_hal_api.h new file mode 100644 index 0000000000..595c2dd8aa --- /dev/null +++ b/nfc/aidl/default/Vendor_hal_api.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 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 <aidl/android/hardware/nfc/INfc.h> +#include <aidl/android/hardware/nfc/NfcConfig.h> +#include <aidl/android/hardware/nfc/NfcEvent.h> +#include <aidl/android/hardware/nfc/NfcStatus.h> +#include <aidl/android/hardware/nfc/PresenceCheckAlgorithm.h> +#include <aidl/android/hardware/nfc/ProtocolDiscoveryConfig.h> +#include "hardware_nfc.h" + +using aidl::android::hardware::nfc::NfcConfig; +using aidl::android::hardware::nfc::NfcEvent; +using aidl::android::hardware::nfc::NfcStatus; +using aidl::android::hardware::nfc::PresenceCheckAlgorithm; +using aidl::android::hardware::nfc::ProtocolDiscoveryConfig; + +int Vendor_hal_open(nfc_stack_callback_t* p_cback, nfc_stack_data_callback_t* p_data_cback); +int Vendor_hal_write(uint16_t data_len, const uint8_t* p_data); + +int Vendor_hal_core_initialized(); + +int Vendor_hal_pre_discover(); + +int Vendor_hal_close(); + +int Vendor_hal_close_off(); + +int Vendor_hal_power_cycle(); + +void Vendor_hal_factoryReset(); + +void Vendor_hal_getConfig(NfcConfig& config); + +void Vendor_hal_setVerboseLogging(bool enable); + +bool Vendor_hal_getVerboseLogging(); diff --git a/nfc/aidl/default/hardware_nfc.h b/nfc/aidl/default/hardware_nfc.h new file mode 100644 index 0000000000..0f856c5ac7 --- /dev/null +++ b/nfc/aidl/default/hardware_nfc.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 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 + +typedef uint8_t nfc_event_t; +typedef uint8_t nfc_status_t; + +/* + * The callback passed in from the NFC stack that the HAL + * can use to pass events back to the stack. + */ +typedef void(nfc_stack_callback_t)(nfc_event_t event, nfc_status_t event_status); + +/* + * The callback passed in from the NFC stack that the HAL + * can use to pass incomming data to the stack. + */ +typedef void(nfc_stack_data_callback_t)(uint16_t data_len, uint8_t* p_data); diff --git a/nfc/aidl/default/main.cpp b/nfc/aidl/default/main.cpp new file mode 100644 index 0000000000..0cc51e7207 --- /dev/null +++ b/nfc/aidl/default/main.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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 <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +#include "Nfc.h" +using ::aidl::android::hardware::nfc::Nfc; + +int main() { + LOG(INFO) << "NFC HAL starting up"; + if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) { + LOG(INFO) << "failed to set thread pool max thread count"; + return 1; + } + std::shared_ptr<Nfc> nfc_service = ndk::SharedRefBase::make<Nfc>(); + + const std::string instance = std::string() + Nfc::descriptor + "/default"; + binder_status_t status = + AServiceManager_addService(nfc_service->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + ABinderProcess_joinThreadPool(); + return 0; +} diff --git a/nfc/aidl/default/nfc-service-example.rc b/nfc/aidl/default/nfc-service-example.rc new file mode 100644 index 0000000000..7d7052ef0a --- /dev/null +++ b/nfc/aidl/default/nfc-service-example.rc @@ -0,0 +1,4 @@ +service nfc_hal_service /vendor/bin/hw/android.hardware.nfc-service.st + class hal + user nfc + group nfc diff --git a/nfc/aidl/default/nfc-service-example.xml b/nfc/aidl/default/nfc-service-example.xml new file mode 100644 index 0000000000..70fed205b5 --- /dev/null +++ b/nfc/aidl/default/nfc-service-example.xml @@ -0,0 +1,6 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.nfc</name> + <fqname>INfc/default</fqname> + </hal> +</manifest> diff --git a/nfc/aidl/vts/functional/Android.bp b/nfc/aidl/vts/functional/Android.bp new file mode 100644 index 0000000000..99eecd060d --- /dev/null +++ b/nfc/aidl/vts/functional/Android.bp @@ -0,0 +1,46 @@ +// +// Copyright (C) 2021 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"], +} + +cc_test { + name: "VtsAidlHalNfcTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsAidlHalNfcTargetTest.cpp", + ], + shared_libs: [ + "libbinder", + "libbinder_ndk", + ], + static_libs: [ + "android.hardware.nfc-V1-ndk", + ], + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp b/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp new file mode 100644 index 0000000000..977b25cf92 --- /dev/null +++ b/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "nfc_aidl_hal_test" + +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> +#include <aidl/android/hardware/nfc/BnNfc.h> +#include <aidl/android/hardware/nfc/BnNfcClientCallback.h> +#include <aidl/android/hardware/nfc/INfc.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android/binder_auto_utils.h> +#include <android/binder_enums.h> +#include <android/binder_interface_utils.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +#include <chrono> +#include <future> + +using aidl::android::hardware::nfc::INfc; +using aidl::android::hardware::nfc::INfcClientCallback; +using aidl::android::hardware::nfc::NfcCloseType; +using aidl::android::hardware::nfc::NfcConfig; +using aidl::android::hardware::nfc::NfcEvent; +using aidl::android::hardware::nfc::NfcStatus; +using aidl::android::hardware::nfc::PresenceCheckAlgorithm; + +using android::getAidlHalInstanceNames; +using android::PrintInstanceNameToString; +using android::base::StringPrintf; +using ndk::enum_range; +using ndk::ScopedAStatus; +using ndk::SharedRefBase; +using ndk::SpAIBinder; + +constexpr static int kCallbackTimeoutMs = 10000; + +// 261 bytes is the default and minimum transceive length +constexpr unsigned int MIN_ISO_DEP_TRANSCEIVE_LENGTH = 261; + +// Range of valid off host route ids +constexpr uint8_t MIN_OFFHOST_ROUTE_ID = 0x01; +constexpr uint8_t MAX_OFFHOST_ROUTE_ID = 0xFE; + +class NfcClientCallback : public aidl::android::hardware::nfc::BnNfcClientCallback { + public: + NfcClientCallback(const std::function<void(NfcEvent, NfcStatus)>& on_hal_event_cb, + const std::function<void(const std::vector<uint8_t>&)>& on_nci_data_cb) + : on_nci_data_cb_(on_nci_data_cb), on_hal_event_cb_(on_hal_event_cb) {} + virtual ~NfcClientCallback() = default; + + ::ndk::ScopedAStatus sendEvent(NfcEvent event, NfcStatus event_status) override { + on_hal_event_cb_(event, event_status); + return ::ndk::ScopedAStatus::ok(); + }; + ::ndk::ScopedAStatus sendData(const std::vector<uint8_t>& data) override { + on_nci_data_cb_(data); + return ::ndk::ScopedAStatus::ok(); + }; + + private: + std::function<void(const std::vector<uint8_t>&)> on_nci_data_cb_; + std::function<void(NfcEvent, NfcStatus)> on_hal_event_cb_; +}; + +class NfcAidl : public testing::TestWithParam<std::string> { + public: + void SetUp() override { + SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str())); + infc_ = INfc::fromBinder(binder); + ASSERT_NE(infc_, nullptr); + } + std::shared_ptr<INfc> infc_; +}; + +/* + * OpenAndCloseForDisable: + * Makes an open call, waits for NfcEvent::OPEN_CPLT + * Immediately calls close(NfcCloseType::DISABLE) and + * waits for NfcEvent::CLOSE_CPLT + * + */ +TEST_P(NfcAidl, OpenAndCloseForDisable) { + std::promise<void> open_cb_promise; + std::promise<void> close_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto close_cb_future = close_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &close_cb_promise](auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + LOG(INFO) << StringPrintf("%s,%d ", __func__, event); + if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value(); + if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value(); + }, + [](auto) {}); + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + // Close and wait for CLOSE_CPLT + LOG(INFO) << "close DISABLE"; + EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk()); + LOG(INFO) << "wait for close"; + EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready); +} + +/* + * OpenAndCloseForHostSwitchedOff: + * Makes an open call, waits for NfcEvent::OPEN_CPLT + * Immediately calls close(NfcCloseType::HOST_SWITCHED_OFF) and + * waits for NfcEvent::CLOSE_CPLT + * + */ +TEST_P(NfcAidl, OpenAndCloseForHostSwitchedOff) { + std::promise<void> open_cb_promise; + std::promise<void> close_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto close_cb_future = close_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &close_cb_promise](auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value(); + if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value(); + }, + [](auto) {}); + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + + // Close and wait for CLOSE_CPLT + LOG(INFO) << "close HOST_SWITCHED_OFF"; + EXPECT_TRUE(infc_->close(NfcCloseType::HOST_SWITCHED_OFF).isOk()); + EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready); +} + +/* + * OpenAfterOpen: + * Calls open() multiple times + * Checks status + */ +TEST_P(NfcAidl, OpenAfterOpen) { + int open_count = 0; + std::promise<void> open_cb_promise; + std::promise<void> open2_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto open2_cb_future = open2_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &open2_cb_promise, &open_count](auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + if (event == NfcEvent::OPEN_CPLT) { + open_count == 0 ? open_cb_promise.set_value() : open2_cb_promise.set_value(); + open_count++; + } + }, + [](auto) {}); + + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + + // Open again and wait for OPEN_CPLT + LOG(INFO) << "open again"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open2_cb_future.wait_for(timeout), std::future_status::ready); +} + +/* + * CloseAfterClose: + * Calls close() multiple times + * Checks status + */ +TEST_P(NfcAidl, CloseAfterClose) { + std::promise<void> open_cb_promise; + std::promise<void> close_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto close_cb_future = close_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &close_cb_promise](auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value(); + if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value(); + }, + [](auto) {}); + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + + // Close and wait for CLOSE_CPLT + LOG(INFO) << "close"; + EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk()); + EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready); + // Close again should fail. + LOG(INFO) << "close again"; + EXPECT_TRUE(!(infc_->close(NfcCloseType::DISABLE).isOk())); +} + +/* + * PowerCycleAfterOpen: + * Calls powerCycle() after open + * Waits for NfcEvent.OPEN_CPLT + * Checks status + */ +TEST_P(NfcAidl, PowerCycleAfterOpen) { + int open_cplt_count = 0; + std::promise<void> open_cb_promise; + std::promise<void> power_cycle_cb_promise; + std::promise<void> close_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto power_cycle_cb_future = power_cycle_cb_promise.get_future(); + auto close_cb_future = close_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &close_cb_promise, &power_cycle_cb_promise, &open_cplt_count]( + auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + if (event == NfcEvent::OPEN_CPLT) { + if (open_cplt_count == 0) { + open_cplt_count++; + open_cb_promise.set_value(); + } else { + power_cycle_cb_promise.set_value(); + } + } + if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value(); + }, + [](auto) {}); + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + + // PowerCycle and wait for OPEN_CPLT + LOG(INFO) << "PowerCycle"; + EXPECT_TRUE(infc_->powerCycle().isOk()); + EXPECT_EQ(power_cycle_cb_future.wait_for(timeout), std::future_status::ready); + + // Close and wait for CLOSE_CPLT + LOG(INFO) << "close"; + EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk()); + EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready); +} + +/* + * PowerCycleAfterClose: + * Calls powerCycle() after close + * PowerCycle should fail immediately + */ +TEST_P(NfcAidl, PowerCycleAfterClose) { + std::promise<void> open_cb_promise; + std::promise<void> close_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto close_cb_future = close_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &close_cb_promise](auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value(); + if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value(); + }, + [](auto) {}); + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + + // Close and wait for CLOSE_CPLT + LOG(INFO) << "close"; + EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk()); + EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready); + + // PowerCycle should fail + LOG(INFO) << "PowerCycle"; + EXPECT_TRUE(!(infc_->powerCycle().isOk())); +} + +/* + * CoreInitializedAfterOpen: + * Calls coreInitialized() after open + * Waits for NfcEvent.POST_INIT_CPLT + */ +TEST_P(NfcAidl, CoreInitializedAfterOpen) { + std::promise<void> open_cb_promise; + std::promise<void> core_init_cb_promise; + std::promise<void> close_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto core_init_cb_future = core_init_cb_promise.get_future(); + auto close_cb_future = close_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &close_cb_promise, &core_init_cb_promise](auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value(); + if (event == NfcEvent::POST_INIT_CPLT) core_init_cb_promise.set_value(); + if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value(); + }, + [](auto) {}); + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + + // CoreInitialized and wait for POST_INIT_CPLT + LOG(INFO) << "coreInitialized"; + EXPECT_TRUE(infc_->coreInitialized().isOk()); + EXPECT_EQ(core_init_cb_future.wait_for(timeout), std::future_status::ready); + + // Close and wait for CLOSE_CPLT + LOG(INFO) << "close"; + EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk()); + EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready); +} + +/* + * CoreInitializedAfterClose: + * Calls coreInitialized() after close + * coreInitialized() should fail immediately + */ +TEST_P(NfcAidl, CoreInitializedAfterClose) { + std::promise<void> open_cb_promise; + std::promise<void> close_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto close_cb_future = close_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &close_cb_promise](auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value(); + if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value(); + }, + [](auto) {}); + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + + // Close and wait for CLOSE_CPLT + LOG(INFO) << "close"; + EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk()); + EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready); + + // coreInitialized should fail + LOG(INFO) << "CoreInitialized"; + EXPECT_TRUE(!(infc_->coreInitialized().isOk())); +} + +/* + * PreDiscoverAfterClose: + * Call preDiscover() after close + * preDiscover() should fail immediately + */ +TEST_P(NfcAidl, PreDiscoverAfterClose) { + std::promise<void> open_cb_promise; + std::promise<void> close_cb_promise; + auto open_cb_future = open_cb_promise.get_future(); + auto close_cb_future = close_cb_promise.get_future(); + std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>( + [&open_cb_promise, &close_cb_promise](auto event, auto status) { + EXPECT_EQ(status, NfcStatus::OK); + if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value(); + if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value(); + }, + [](auto) {}); + std::chrono::milliseconds timeout{kCallbackTimeoutMs}; + // Open and wait for OPEN_CPLT + LOG(INFO) << "open"; + EXPECT_TRUE(infc_->open(mCallback).isOk()); + EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready); + + // Close and wait for CLOSE_CPLT + LOG(INFO) << "close"; + EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk()); + EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready); + + // preDiscover should fail + LOG(INFO) << "preDiscover"; + EXPECT_TRUE(!(infc_->preDiscover().isOk())); +} + +/* + * checkGetConfigValues: + * Calls getConfig() + * checks if fields in NfcConfig are populated correctly + */ +TEST_P(NfcAidl, CheckGetConfigValues) { + NfcConfig configValue; + EXPECT_TRUE(infc_->getConfig(&configValue).isOk()); + EXPECT_GE(configValue.maxIsoDepTransceiveLength, MIN_ISO_DEP_TRANSCEIVE_LENGTH); + LOG(INFO) << StringPrintf("configValue.maxIsoDepTransceiveLength = %x", + configValue.maxIsoDepTransceiveLength); + for (auto uicc : configValue.offHostRouteUicc) { + LOG(INFO) << StringPrintf("offHostRouteUicc = %x", uicc); + EXPECT_GE(uicc, MIN_OFFHOST_ROUTE_ID); + EXPECT_LE(uicc, MAX_OFFHOST_ROUTE_ID); + } + for (auto ese : configValue.offHostRouteEse) { + LOG(INFO) << StringPrintf("offHostRouteEse = %x", ese); + EXPECT_GE(ese, MIN_OFFHOST_ROUTE_ID); + EXPECT_LE(ese, MAX_OFFHOST_ROUTE_ID); + } + if (configValue.defaultIsoDepRoute != 0) { + EXPECT_GE((uint8_t)configValue.defaultIsoDepRoute, MIN_OFFHOST_ROUTE_ID); + EXPECT_LE((uint8_t)configValue.defaultIsoDepRoute, MAX_OFFHOST_ROUTE_ID); + } +} + +/* + * CheckisVerboseLoggingEnabledAfterSetEnableVerboseLogging: + * Calls setEnableVerboseLogging() + * checks the return value of isVerboseLoggingEnabled + */ +TEST_P(NfcAidl, CheckisVerboseLoggingEnabledAfterSetEnableVerboseLogging) { + bool enabled = false; + EXPECT_TRUE(infc_->setEnableVerboseLogging(true).isOk()); + EXPECT_TRUE(infc_->isVerboseLoggingEnabled(&enabled).isOk()); + EXPECT_TRUE(enabled); + EXPECT_TRUE(infc_->setEnableVerboseLogging(false).isOk()); + EXPECT_TRUE(infc_->isVerboseLoggingEnabled(&enabled).isOk()); + EXPECT_TRUE(!enabled); +} + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NfcAidl); +INSTANTIATE_TEST_SUITE_P(Nfc, NfcAidl, + testing::ValuesIn(::android::getAidlHalInstanceNames(INfc::descriptor)), + ::android::PrintInstanceNameToString); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_startThreadPool(); + std::system("/system/bin/svc nfc disable"); /* Turn off NFC */ + sleep(5); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + std::system("/system/bin/svc nfc enable"); /* Turn on NFC */ + sleep(5); + return status; +} diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl index ba444a7081..f38426b785 100644 --- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl +++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl @@ -49,5 +49,6 @@ enum Mode { CAMERA_STREAMING_LOW = 12, CAMERA_STREAMING_MID = 13, CAMERA_STREAMING_HIGH = 14, - GAME_LOADING = 15, + GAME = 15, + GAME_LOADING = 16, } diff --git a/power/aidl/android/hardware/power/Mode.aidl b/power/aidl/android/hardware/power/Mode.aidl index 2ebace103f..cc4b1308d2 100644 --- a/power/aidl/android/hardware/power/Mode.aidl +++ b/power/aidl/android/hardware/power/Mode.aidl @@ -164,6 +164,11 @@ enum Mode { CAMERA_STREAMING_HIGH, /** + * This mode indicates that user is playing a game. + */ + GAME, + + /** * This mode indicates that the user is waiting for loading in a game. */ GAME_LOADING, diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl index 06bce19c82..5ff45f8a94 100644 --- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl +++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl @@ -38,6 +38,7 @@ parcelable RpcHardwareInfo { int versionNumber; @utf8InCpp String rpcAuthorName; int supportedEekCurve = 0; + @nullable @utf8InCpp String uniqueId; const int CURVE_NONE = 0; const int CURVE_P256 = 1; const int CURVE_25519 = 2; diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl index 9846ee91ac..4b637993f1 100644 --- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl +++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl @@ -96,7 +96,9 @@ import android.hardware.security.secureclock.TimeStampToken; * - TRUSTED_ENVRIONMENT IKeyMintDevices must support curve 25519 for Purpose::SIGN (Ed25519, * as specified in RFC 8032), Purpose::ATTEST_KEY (Ed25519) or for KeyPurpose::AGREE_KEY * (X25519, as specified in RFC 7748). However, a key must have exactly one of these - * purpose values; the same key cannot be used for multiple purposes. + * purpose values; the same key cannot be used for multiple purposes. Signing operations + * (Purpose::SIGN) have a message size limit of 16 KiB; operations on messages longer than + * this limit must fail with ErrorCode::INVALID_INPUT_LENGTH. * STRONGBOX IKeyMintDevices do not support curve 25519. * * o AES diff --git a/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl index d297f871fb..3a4c233cf5 100644 --- a/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl +++ b/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl @@ -53,4 +53,21 @@ parcelable RpcHardwareInfo { * a passing implementation does not provide CURVE_NONE. */ int supportedEekCurve = CURVE_NONE; + + /** + * uniqueId is an opaque identifier for this IRemotelyProvisionedComponent implementation. The + * client should NOT interpret the content of the identifier in any way. The client can only + * compare identifiers to determine if two IRemotelyProvisionedComponents share the same + * implementation. Each IRemotelyProvisionedComponent implementation must have a distinct + * identifier from all other implementations on the same device. + * + * This identifier must be consistent across reboots, as it is used to store and track + * provisioned keys in a persistent, on-device database. + * + * uniqueId may not be empty, and must not be any longer than 32 characters. + * + * This field was added in API version 2. + * + */ + @nullable @utf8InCpp String uniqueId; } diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp index 374f2da7a8..146a527561 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp @@ -25,6 +25,7 @@ #include <cppbor_parse.h> #include <cutils/properties.h> #include <gmock/gmock.h> +#include <openssl/evp.h> #include <openssl/mem.h> #include <remote_prov/remote_prov_utils.h> @@ -206,6 +207,21 @@ uint32_t KeyMintAidlTestBase::boot_patch_level() { return boot_patch_level(key_characteristics_); } +bool KeyMintAidlTestBase::Curve25519Supported() { + // Strongbox never supports curve 25519. + if (SecLevel() == SecurityLevel::STRONGBOX) { + return false; + } + + // Curve 25519 was included in version 2 of the KeyMint interface. + int32_t version = 0; + auto status = keymint_->getInterfaceVersion(&version); + if (!status.isOk()) { + ADD_FAILURE() << "Failed to determine interface version"; + } + return version >= 2; +} + ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(const Status& result) { if (result.isOk()) return ErrorCode::OK; @@ -543,7 +559,12 @@ ErrorCode KeyMintAidlTestBase::Update(const string& input, string* output) { std::vector<uint8_t> o_put; result = op_->update(vector<uint8_t>(input.begin(), input.end()), {}, {}, &o_put); - if (result.isOk()) output->append(o_put.begin(), o_put.end()); + if (result.isOk()) { + output->append(o_put.begin(), o_put.end()); + } else { + // Failure always terminates the operation. + op_ = {}; + } return GetReturnErrorCode(result); } @@ -740,6 +761,19 @@ void KeyMintAidlTestBase::LocalVerifyMessage(const string& message, const string if (digest == Digest::NONE) { switch (EVP_PKEY_id(pub_key.get())) { + case EVP_PKEY_ED25519: { + ASSERT_EQ(64, signature.size()); + uint8_t pub_keydata[32]; + size_t pub_len = sizeof(pub_keydata); + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(pub_key.get(), pub_keydata, &pub_len)); + ASSERT_EQ(sizeof(pub_keydata), pub_len); + ASSERT_EQ(1, ED25519_verify(reinterpret_cast<const uint8_t*>(message.data()), + message.size(), + reinterpret_cast<const uint8_t*>(signature.data()), + pub_keydata)); + break; + } + case EVP_PKEY_EC: { vector<uint8_t> data((EVP_PKEY_bits(pub_key.get()) + 7) / 8); size_t data_size = std::min(data.size(), message.size()); @@ -1166,16 +1200,31 @@ vector<PaddingMode> KeyMintAidlTestBase::InvalidPaddingModes(Algorithm algorithm vector<EcCurve> KeyMintAidlTestBase::ValidCurves() { if (securityLevel_ == SecurityLevel::STRONGBOX) { return {EcCurve::P_256}; + } else if (Curve25519Supported()) { + return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521, + EcCurve::CURVE_25519}; } else { - return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521}; + return { + EcCurve::P_224, + EcCurve::P_256, + EcCurve::P_384, + EcCurve::P_521, + }; } } vector<EcCurve> KeyMintAidlTestBase::InvalidCurves() { if (SecLevel() == SecurityLevel::STRONGBOX) { - return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521}; + // Curve 25519 is not supported, either because: + // - KeyMint v1: it's an unknown enum value + // - KeyMint v2+: it's not supported by StrongBox. + return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521, EcCurve::CURVE_25519}; } else { - return {}; + if (Curve25519Supported()) { + return {}; + } else { + return {EcCurve::CURVE_25519}; + } } } diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h index 61f9d4d59a..27cb99c06a 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h @@ -80,6 +80,8 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> { uint32_t boot_patch_level(const vector<KeyCharacteristics>& key_characteristics); uint32_t boot_patch_level(); + bool Curve25519Supported(); + ErrorCode GetReturnErrorCode(const Status& result); ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob, diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp index 340010fec3..7357ac7425 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp @@ -22,6 +22,7 @@ #include <algorithm> #include <iostream> +#include <openssl/curve25519.h> #include <openssl/ec.h> #include <openssl/evp.h> #include <openssl/mem.h> @@ -69,6 +70,9 @@ namespace aidl::android::hardware::security::keymint::test { namespace { +// Maximum supported Ed25519 message size. +const size_t MAX_ED25519_MSG_SIZE = 16 * 1024; + // Whether to check that BOOT_PATCHLEVEL is populated. bool check_boot_pl = true; @@ -415,6 +419,126 @@ string ec_256_key_sec1 = hex2str( // } end SEQUENCE (PrivateKeyInfo) ); +/** + * Ed25519 key pair generated as follows: + * ``` + * % openssl req -x509 -newkey ED25519 -days 700 -nodes \ + * -keyout ed25519_priv.key -out ed25519.pem * -subj "/CN=fake.ed25519.com" + * Generating a ED25519 private key writing new private key to + * 'ed25519_priv.key' + * ----- + * % cat ed25519_priv.key + * -----BEGIN PRIVATE KEY----- + * MC4CAQAwBQYDK2VwBCIEIKl3A5quNywcj1P+0XI9SBalFPIvO52NxceMLRH6dVmR + * -----END PRIVATE KEY----- + * % der2ascii -pem -i ed25519_priv.key + * SEQUENCE { + * INTEGER { 0 } + * SEQUENCE { + * # ed25519 + * OBJECT_IDENTIFIER { 1.3.101.112 } + * } + * OCTET_STRING { + * OCTET_STRING { `a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991` } + * } + * } + * % cat ed25519.pem + * -----BEGIN CERTIFICATE----- + * MIIBSjCB/aADAgECAhR0Jron3eKcdgqyecv/eEfGWAzn8DAFBgMrZXAwGzEZMBcG + * A1UEAwwQZmFrZS5lZDI1NTE5LmNvbTAeFw0yMTEwMjAwODI3NDJaFw0yMzA5MjAw + * ODI3NDJaMBsxGTAXBgNVBAMMEGZha2UuZWQyNTUxOS5jb20wKjAFBgMrZXADIQDv + * uwHz+3TaQ69D2digxlz0fFfsZg0rPqgQae3jBPRWkaNTMFEwHQYDVR0OBBYEFN9O + * od30SY4JTs66ZR403UPya+iXMB8GA1UdIwQYMBaAFN9Ood30SY4JTs66ZR403UPy + * a+iXMA8GA1UdEwEB/wQFMAMBAf8wBQYDK2VwA0EAKjVrYQjuE/gEL2j/ABpDbFjV + * Ilg5tJ6MN/P3psAv3Cs7f0X1lFqdlt15nJ/6aj2cmGCwNRXt5wcyYDKNu+v2Dw== + * -----END CERTIFICATE----- + * % openssl x509 -in ed25519.pem -text -noout + * Certificate: + * Data: + * Version: 3 (0x2) + * Serial Number: + * 74:26:ba:27:dd:e2:9c:76:0a:b2:79:cb:ff:78:47:c6:58:0c:e7:f0 + * Signature Algorithm: ED25519 + * Issuer: CN = fake.ed25519.com + * Validity + * Not Before: Oct 20 08:27:42 2021 GMT + * Not After : Sep 20 08:27:42 2023 GMT + * Subject: CN = fake.ed25519.com + * Subject Public Key Info: + * Public Key Algorithm: ED25519 + * ED25519 Public-Key: + * pub: + * ef:bb:01:f3:fb:74:da:43:af:43:d9:d8:a0:c6:5c: + * f4:7c:57:ec:66:0d:2b:3e:a8:10:69:ed:e3:04:f4: + * 56:91 + * X509v3 extensions: + * X509v3 Subject Key Identifier: + * DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97 + * X509v3 Authority Key Identifier: + * keyid:DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97 + * + * X509v3 Basic Constraints: critical + * CA:TRUE + * Signature Algorithm: ED25519 + * 2a:35:6b:61:08:ee:13:f8:04:2f:68:ff:00:1a:43:6c:58:d5: + * 22:58:39:b4:9e:8c:37:f3:f7:a6:c0:2f:dc:2b:3b:7f:45:f5: + * 94:5a:9d:96:dd:79:9c:9f:fa:6a:3d:9c:98:60:b0:35:15:ed: + * e7:07:32:60:32:8d:bb:eb:f6:0f + * ``` + */ +string ed25519_key = hex2str("a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991"); +string ed25519_pkcs8_key = hex2str( + // RFC 5208 s5 + "302e" // SEQUENCE length 0x2e (PrivateKeyInfo) { + "0201" // INTEGER length 1 (Version) + "00" // version 0 + "3005" // SEQUENCE length 05 (AlgorithmIdentifier) { + "0603" // OBJECT IDENTIFIER length 3 (algorithm) + "2b6570" // 1.3.101.112 (id-Ed125519 RFC 8410 s3) + // } end SEQUENCE (AlgorithmIdentifier) + "0422" // OCTET STRING length 0x22 (PrivateKey) + "0420" // OCTET STRING length 0x20 (RFC 8410 s7) + "a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991" + // } end SEQUENCE (PrivateKeyInfo) +); +string ed25519_pubkey = hex2str("efbb01f3fb74da43af43d9d8a0c65cf47c57ec660d2b3ea81069ede304f45691"); + +/** + * X25519 key pair generated as follows: + * ``` + * % openssl genpkey -algorithm X25519 > x25519_priv.key + * % cat x25519_priv.key + * -----BEGIN PRIVATE KEY----- + * MC4CAQAwBQYDK2VuBCIEIGgPwF3NLwQx/Sfwr2nfJvXitwlDNh3Skzh+TISN/y1C + * -----END PRIVATE KEY----- + * % der2ascii -pem -i x25519_priv.key + * SEQUENCE { + * INTEGER { 0 } + * SEQUENCE { + * # x25519 + * OBJECT_IDENTIFIER { 1.3.101.110 } + * } + * OCTET_STRING { + * OCTET_STRING { `680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42` } + * } + * } + * ``` + */ + +string x25519_key = hex2str("680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42"); +string x25519_pkcs8_key = hex2str( + // RFC 5208 s5 + "302e" // SEQUENCE length 0x2e (PrivateKeyInfo) { + "0201" // INTEGER length 1 (Version) + "00" // version 0 + "3005" // SEQUENCE length 05 (AlgorithmIdentifier) { + "0603" // OBJECT IDENTIFIER length 3 (algorithm) + "2b656e" // 1.3.101.110 (id-X125519 RFC 8410 s3) + "0422" // OCTET STRING length 0x22 (PrivateKey) + "0420" // OCTET STRING length 0x20 (RFC 8410 s7) + "680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42"); +string x25519_pubkey = hex2str("be46925a857f17831d6d454b9d3d36a4a30166edf80eb82b684661c3e258f768"); + struct RSA_Delete { void operator()(RSA* p) { RSA_free(p); } }; @@ -1374,7 +1498,7 @@ TEST_P(NewKeyGenerationTest, RsaMissingParams) { /* * NewKeyGenerationTest.Ecdsa * - * Verifies that keymint can generate all required EC key sizes, and that the resulting keys + * Verifies that keymint can generate all required EC curves, and that the resulting keys * have correct characteristics. */ TEST_P(NewKeyGenerationTest, Ecdsa) { @@ -1400,6 +1524,65 @@ TEST_P(NewKeyGenerationTest, Ecdsa) { } /* + * NewKeyGenerationTest.EcdsaCurve25519 + * + * Verifies that keymint can generate a curve25519 key, and that the resulting key + * has correct characteristics. + */ +TEST_P(NewKeyGenerationTest, EcdsaCurve25519) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + ErrorCode result = GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity(), + &key_blob, &key_characteristics); + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + + EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_)); + ASSERT_GT(cert_chain_.size(), 0); + + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); + + AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics); + + EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC)); + EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing"; + + CheckedDeleteKey(&key_blob); +} + +/* + * NewKeyGenerationTest.EcCurve25519MultiPurposeFail + * + * Verifies that KeyMint rejects an attempt to generate a curve 25519 key for both + * SIGN and AGREE_KEY. + */ +TEST_P(NewKeyGenerationTest, EcdsaCurve25519MultiPurposeFail) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + ErrorCode result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity(), + &key_blob, &key_characteristics); + ASSERT_EQ(result, ErrorCode::INCOMPATIBLE_PURPOSE); +} + +/* * NewKeyGenerationTest.EcdsaAttestation * * Verifies that for all Ecdsa key sizes, if challenge and app id is provided, @@ -1453,6 +1636,62 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { } /* + * NewKeyGenerationTest.EcdsaAttestationCurve25519 + * + * Verifies that for a curve 25519 key, if challenge and app id is provided, + * an attestation will be generated. + */ +TEST_P(NewKeyGenerationTest, EcdsaAttestationCurve25519) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + auto challenge = "hello"; + auto app_id = "foo"; + + auto subject = "cert subj 2"; + vector<uint8_t> subject_der(make_name_from_str(subject)); + + uint64_t serial_int = 0xFFFFFFFFFFFFFFFF; + vector<uint8_t> serial_blob(build_serial_blob(serial_int)); + + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + ErrorCode result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), + &key_blob, &key_characteristics); + ASSERT_EQ(ErrorCode::OK, result); + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); + + AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics); + + EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC)); + EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing"; + + EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_)); + ASSERT_GT(cert_chain_.size(), 0); + verify_subject_and_serial(cert_chain_[0], serial_int, subject, false); + + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, // + sw_enforced, hw_enforced, SecLevel(), + cert_chain_[0].encodedCertificate)); + + CheckedDeleteKey(&key_blob); +} + +/* * NewKeyGenerationTest.EcdsaAttestationTags * * Verifies that creation of an attested ECDSA key includes various tags in the @@ -1984,20 +2223,22 @@ TEST_P(NewKeyGenerationTest, EcdsaDefaultSize) { } /* - * NewKeyGenerationTest.EcdsaInvalidSize + * NewKeyGenerationTest.EcdsaInvalidCurve * - * Verifies that specifying an invalid key size for EC key generation returns + * Verifies that specifying an invalid curve for EC key generation returns * UNSUPPORTED_KEY_SIZE. */ -TEST_P(NewKeyGenerationTest, EcdsaInvalidSize) { +TEST_P(NewKeyGenerationTest, EcdsaInvalidCurve) { for (auto curve : InvalidCurves()) { vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; - ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, GenerateKey(AuthorizationSetBuilder() - .EcdsaSigningKey(curve) - .Digest(Digest::NONE) - .SetDefaultValidity(), - &key_blob, &key_characteristics)); + auto result = GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity(), + &key_blob, &key_characteristics); + ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_KEY_SIZE || + result == ErrorCode::UNSUPPORTED_EC_CURVE); } ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, @@ -2808,15 +3049,19 @@ TEST_P(SigningOperationsTest, RsaSignTooLargeMessage) { /* * SigningOperationsTest.EcdsaAllDigestsAndCurves * - * Verifies ECDSA signature/verification for all digests and curves. + * Verifies ECDSA signature/verification for all digests and required curves. */ TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) { - auto digests = ValidDigests(true /* withNone */, false /* withMD5 */); string message = "1234567890"; string corrupt_message = "2234567890"; for (auto curve : ValidCurves()) { SCOPED_TRACE(testing::Message() << "Curve::" << curve); + // Ed25519 only allows Digest::NONE. + auto digests = (curve == EcCurve::CURVE_25519) + ? std::vector<Digest>(1, Digest::NONE) + : ValidDigests(true /* withNone */, false /* withMD5 */); + ErrorCode error = GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) .EcdsaSigningKey(curve) @@ -2841,25 +3086,141 @@ TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) { /* * SigningOperationsTest.EcdsaAllCurves * - * Verifies that ECDSA operations succeed with all possible curves. + * Verifies that ECDSA operations succeed with all required curves. */ TEST_P(SigningOperationsTest, EcdsaAllCurves) { for (auto curve : ValidCurves()) { + Digest digest = (curve == EcCurve::CURVE_25519 ? Digest::NONE : Digest::SHA_2_256); + SCOPED_TRACE(testing::Message() << "Curve::" << curve); ErrorCode error = GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) .EcdsaSigningKey(curve) - .Digest(Digest::SHA_2_256) + .Digest(digest) .SetDefaultValidity()); EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; if (error != ErrorCode::OK) continue; string message(1024, 'a'); - SignMessage(message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)); + SignMessage(message, AuthorizationSetBuilder().Digest(digest)); CheckedDeleteKey(); } } /* + * SigningOperationsTest.EcdsaCurve25519 + * + * Verifies that ECDSA operations succeed with curve25519. + */ +TEST_P(SigningOperationsTest, EcdsaCurve25519) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity()); + ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; + + string message(1024, 'a'); + SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE)); + CheckedDeleteKey(); +} + +/* + * SigningOperationsTest.EcdsaCurve25519MaxSize + * + * Verifies that EDDSA operations with curve25519 under the maximum message size succeed. + */ +TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSize) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity()); + ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; + + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + + for (size_t msg_size : {MAX_ED25519_MSG_SIZE - 1, MAX_ED25519_MSG_SIZE}) { + SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size); + string message(msg_size, 'a'); + + // Attempt to sign via Begin+Finish. + AuthorizationSet out_params; + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params)); + EXPECT_TRUE(out_params.empty()); + string signature; + auto result = Finish(message, &signature); + EXPECT_EQ(result, ErrorCode::OK); + LocalVerifyMessage(message, signature, params); + + // Attempt to sign via Begin+Update+Finish + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params)); + EXPECT_TRUE(out_params.empty()); + string output; + result = Update(message, &output); + EXPECT_EQ(result, ErrorCode::OK); + EXPECT_EQ(output.size(), 0); + string signature2; + EXPECT_EQ(ErrorCode::OK, Finish({}, &signature2)); + LocalVerifyMessage(message, signature2, params); + } + + CheckedDeleteKey(); +} + +/* + * SigningOperationsTest.EcdsaCurve25519MaxSizeFail + * + * Verifies that EDDSA operations with curve25519 fail when message size is too large. + */ +TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSizeFail) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity()); + ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; + + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + + for (size_t msg_size : {MAX_ED25519_MSG_SIZE + 1, MAX_ED25519_MSG_SIZE * 2}) { + SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size); + string message(msg_size, 'a'); + + // Attempt to sign via Begin+Finish. + AuthorizationSet out_params; + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params)); + EXPECT_TRUE(out_params.empty()); + string signature; + auto result = Finish(message, &signature); + EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH); + + // Attempt to sign via Begin+Update (but never get to Finish) + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params)); + EXPECT_TRUE(out_params.empty()); + string output; + result = Update(message, &output); + EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH); + } + + CheckedDeleteKey(); +} + +/* * SigningOperationsTest.EcdsaNoDigestHugeData * * Verifies that ECDSA operations support very large messages, even without digesting. This @@ -3509,6 +3870,255 @@ TEST_P(ImportKeyTest, EcdsaAttestMultiPurposeFail) { } /* + * ImportKeyTest.Ed25519RawSuccess + * + * Verifies that importing and using a raw Ed25519 private key works correctly. + */ +TEST_P(ImportKeyTest, Ed25519RawSuccess) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::RAW, ed25519_key)); + CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519); + CheckOrigin(); + + // The returned cert should hold the correct public key. + ASSERT_GT(cert_chain_.size(), 0); + X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate)); + ASSERT_NE(kmKeyCert, nullptr); + EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get())); + ASSERT_NE(kmPubKey.get(), nullptr); + size_t kmPubKeySize = 32; + uint8_t kmPubKeyData[32]; + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize)); + ASSERT_EQ(kmPubKeySize, 32); + EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey); + + string message(32, 'a'); + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + string signature = SignMessage(message, params); + LocalVerifyMessage(message, signature, params); +} + +/* + * ImportKeyTest.Ed25519Pkcs8Success + * + * Verifies that importing and using a PKCS#8-encoded Ed25519 private key works correctly. + */ +TEST_P(ImportKeyTest, Ed25519Pkcs8Success) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::PKCS8, ed25519_pkcs8_key)); + CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519); + CheckOrigin(); + + // The returned cert should hold the correct public key. + ASSERT_GT(cert_chain_.size(), 0); + X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate)); + ASSERT_NE(kmKeyCert, nullptr); + EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get())); + ASSERT_NE(kmPubKey.get(), nullptr); + size_t kmPubKeySize = 32; + uint8_t kmPubKeyData[32]; + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize)); + ASSERT_EQ(kmPubKeySize, 32); + EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey); + + string message(32, 'a'); + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + string signature = SignMessage(message, params); + LocalVerifyMessage(message, signature, params); +} + +/* + * ImportKeyTest.Ed25519CurveMismatch + * + * Verifies that importing an Ed25519 key pair with a curve that doesn't match the key fails in + * the correct way. + */ +TEST_P(ImportKeyTest, Ed25519CurveMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, + ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::RAW, ed25519_key)); +} + +/* + * ImportKeyTest.Ed25519FormatMismatch + * + * Verifies that importing an Ed25519 key pair with an invalid format fails. + */ +TEST_P(ImportKeyTest, Ed25519FormatMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::PKCS8, ed25519_key)); + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::RAW, ed25519_pkcs8_key)); +} + +/* + * ImportKeyTest.Ed25519PurposeMismatch + * + * Verifies that importing an Ed25519 key pair with an invalid purpose fails. + */ +TEST_P(ImportKeyTest, Ed25519PurposeMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Can't have both SIGN and ATTEST_KEY + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::RAW, ed25519_key)); + // AGREE_KEY is for X25519 (but can only tell the difference if the import key is in + // PKCS#8 format and so includes an OID). + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::PKCS8, ed25519_pkcs8_key)); +} + +/* + * ImportKeyTest.X25519RawSuccess + * + * Verifies that importing and using a raw X25519 private key works correctly. + */ +TEST_P(ImportKeyTest, X25519RawSuccess) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::RAW, x25519_key)); + + CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519); + CheckOrigin(); +} + +/* + * ImportKeyTest.X25519Pkcs8Success + * + * Verifies that importing and using a PKCS#8-encoded X25519 private key works correctly. + */ +TEST_P(ImportKeyTest, X25519Pkcs8Success) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_pkcs8_key)); + + CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519); + CheckOrigin(); +} + +/* + * ImportKeyTest.X25519CurveMismatch + * + * Verifies that importing an X25519 key with a curve that doesn't match the key fails in + * the correct way. + */ +TEST_P(ImportKeyTest, X25519CurveMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_224 /* Doesn't match key */) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::RAW, x25519_key)); +} + +/* + * ImportKeyTest.X25519FormatMismatch + * + * Verifies that importing an X25519 key with an invalid format fails. + */ +TEST_P(ImportKeyTest, X25519FormatMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_key)); + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::RAW, x25519_pkcs8_key)); +} + +/* + * ImportKeyTest.X25519PurposeMismatch + * + * Verifies that importing an X25519 key pair with an invalid format fails. + */ +TEST_P(ImportKeyTest, X25519PurposeMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_pkcs8_key)); + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::CURVE_25519) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_pkcs8_key)); +} + +/* * ImportKeyTest.AesSuccess * * Verifies that importing and using an AES key works. @@ -6703,8 +7313,6 @@ TEST_P(TransportLimitTest, LargeFinishInput) { INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest); -typedef KeyMintAidlTestBase KeyAgreementTest; - static int EcdhCurveToOpenSslCurveName(EcCurve curve) { switch (curve) { case EcCurve::P_224: @@ -6720,10 +7328,108 @@ static int EcdhCurveToOpenSslCurveName(EcCurve curve) { } } +class KeyAgreementTest : public KeyMintAidlTestBase { + protected: + void GenerateLocalEcKey(EcCurve localCurve, EVP_PKEY_Ptr* localPrivKey, + std::vector<uint8_t>* localPublicKey) { + // Generate EC key locally (with access to private key material) + if (localCurve == EcCurve::CURVE_25519) { + uint8_t privKeyData[32]; + uint8_t pubKeyData[32]; + X25519_keypair(pubKeyData, privKeyData); + *localPublicKey = vector<uint8_t>(pubKeyData, pubKeyData + 32); + *localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new_raw_private_key( + EVP_PKEY_X25519, nullptr, privKeyData, sizeof(privKeyData))); + } else { + auto ecKey = EC_KEY_Ptr(EC_KEY_new()); + int curveName = EcdhCurveToOpenSslCurveName(localCurve); + auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName)); + ASSERT_NE(group, nullptr); + ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1); + ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1); + *localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new()); + ASSERT_EQ(EVP_PKEY_set1_EC_KEY(localPrivKey->get(), ecKey.get()), 1); + + // Get encoded form of the public part of the locally generated key... + unsigned char* p = nullptr; + int localPublicKeySize = i2d_PUBKEY(localPrivKey->get(), &p); + ASSERT_GT(localPublicKeySize, 0); + *localPublicKey = + vector<uint8_t>(reinterpret_cast<const uint8_t*>(p), + reinterpret_cast<const uint8_t*>(p + localPublicKeySize)); + OPENSSL_free(p); + } + } + + void GenerateKeyMintEcKey(EcCurve curve, EVP_PKEY_Ptr* kmPubKey) { + vector<uint8_t> challenge = {0x41, 0x42}; + ErrorCode result = + GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_EC_CURVE, curve) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .Authorization(TAG_ALGORITHM, Algorithm::EC) + .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62}) + .Authorization(TAG_ATTESTATION_CHALLENGE, challenge) + .SetDefaultValidity()); + ASSERT_EQ(ErrorCode::OK, result) << "Failed to generate key"; + ASSERT_GT(cert_chain_.size(), 0); + X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate)); + ASSERT_NE(kmKeyCert, nullptr); + // Check that keyAgreement (bit 4) is set in KeyUsage + EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0); + *kmPubKey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get())); + ASSERT_NE(*kmPubKey, nullptr); + if (dump_Attestations) { + for (size_t n = 0; n < cert_chain_.size(); n++) { + std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl; + } + } + } + + void CheckAgreement(EVP_PKEY_Ptr kmPubKey, EVP_PKEY_Ptr localPrivKey, + const std::vector<uint8_t>& localPublicKey) { + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); + string ZabFromKeyMintStr; + ASSERT_EQ(ErrorCode::OK, + Finish(string(localPublicKey.begin(), localPublicKey.end()), &ZabFromKeyMintStr)); + vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end()); + vector<uint8_t> ZabFromTest; + + if (EVP_PKEY_id(kmPubKey.get()) == EVP_PKEY_X25519) { + size_t kmPubKeySize = 32; + uint8_t kmPubKeyData[32]; + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize)); + ASSERT_EQ(kmPubKeySize, 32); + + uint8_t localPrivKeyData[32]; + size_t localPrivKeySize = 32; + ASSERT_EQ(1, EVP_PKEY_get_raw_private_key(localPrivKey.get(), localPrivKeyData, + &localPrivKeySize)); + ASSERT_EQ(localPrivKeySize, 32); + + uint8_t sharedKey[32]; + ASSERT_EQ(1, X25519(sharedKey, localPrivKeyData, kmPubKeyData)); + ZabFromTest = std::vector<uint8_t>(sharedKey, sharedKey + 32); + } else { + // Perform local ECDH between the two keys so we can check if we get the same Zab.. + auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(localPrivKey.get(), nullptr)); + ASSERT_NE(ctx, nullptr); + ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1); + ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPubKey.get()), 1); + size_t ZabFromTestLen = 0; + ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1); + ZabFromTest.resize(ZabFromTestLen); + ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1); + } + EXPECT_EQ(ZabFromKeyMint, ZabFromTest); + } +}; + /* * KeyAgreementTest.Ecdh * - * Verifies that ECDH works for all curves + * Verifies that ECDH works for all required curves */ TEST_P(KeyAgreementTest, Ecdh) { // Because it's possible to use this API with keys on different curves, we @@ -6737,49 +7443,13 @@ TEST_P(KeyAgreementTest, Ecdh) { for (auto curve : ValidCurves()) { for (auto localCurve : ValidCurves()) { // Generate EC key locally (with access to private key material) - auto ecKey = EC_KEY_Ptr(EC_KEY_new()); - int curveName = EcdhCurveToOpenSslCurveName(localCurve); - auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName)); - ASSERT_NE(group, nullptr); - ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1); - ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1); - auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); - ASSERT_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()), 1); - - // Get encoded form of the public part of the locally generated key... - unsigned char* p = nullptr; - int encodedPublicKeySize = i2d_PUBKEY(pkey.get(), &p); - ASSERT_GT(encodedPublicKeySize, 0); - vector<uint8_t> encodedPublicKey( - reinterpret_cast<const uint8_t*>(p), - reinterpret_cast<const uint8_t*>(p + encodedPublicKeySize)); - OPENSSL_free(p); + EVP_PKEY_Ptr localPrivKey; + vector<uint8_t> localPublicKey; + GenerateLocalEcKey(localCurve, &localPrivKey, &localPublicKey); // Generate EC key in KeyMint (only access to public key material) - vector<uint8_t> challenge = {0x41, 0x42}; - EXPECT_EQ( - ErrorCode::OK, - GenerateKey(AuthorizationSetBuilder() - .Authorization(TAG_NO_AUTH_REQUIRED) - .Authorization(TAG_EC_CURVE, curve) - .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) - .Authorization(TAG_ALGORITHM, Algorithm::EC) - .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62}) - .Authorization(TAG_ATTESTATION_CHALLENGE, challenge) - .SetDefaultValidity())) - << "Failed to generate key"; - ASSERT_GT(cert_chain_.size(), 0); - X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate)); - ASSERT_NE(kmKeyCert, nullptr); - // Check that keyAgreement (bit 4) is set in KeyUsage - EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0); - auto kmPkey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get())); - ASSERT_NE(kmPkey, nullptr); - if (dump_Attestations) { - for (size_t n = 0; n < cert_chain_.size(); n++) { - std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl; - } - } + EVP_PKEY_Ptr kmPubKey; + GenerateKeyMintEcKey(curve, &kmPubKey); // Now that we have the two keys, we ask KeyMint to perform ECDH... if (curve != localCurve) { @@ -6788,30 +7458,12 @@ TEST_P(KeyAgreementTest, Ecdh) { EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); string ZabFromKeyMintStr; EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, - Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()), + Finish(string(localPublicKey.begin(), localPublicKey.end()), &ZabFromKeyMintStr)); } else { // Otherwise if the keys are using the same curve, it should work. - EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); - string ZabFromKeyMintStr; - EXPECT_EQ(ErrorCode::OK, - Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()), - &ZabFromKeyMintStr)); - vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end()); - - // Perform local ECDH between the two keys so we can check if we get the same Zab.. - auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(pkey.get(), nullptr)); - ASSERT_NE(ctx, nullptr); - ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1); - ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPkey.get()), 1); - size_t ZabFromTestLen = 0; - ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1); - vector<uint8_t> ZabFromTest; - ZabFromTest.resize(ZabFromTestLen); - ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1); - - EXPECT_EQ(ZabFromKeyMint, ZabFromTest); + CheckAgreement(std::move(kmPubKey), std::move(localPrivKey), localPublicKey); } CheckedDeleteKey(); @@ -6819,6 +7471,140 @@ TEST_P(KeyAgreementTest, Ecdh) { } } +/* + * KeyAgreementTest.EcdhCurve25519 + * + * Verifies that ECDH works for curve25519. This is also covered by the general + * KeyAgreementTest.Ecdh case, but is pulled out separately here because this curve was added after + * KeyMint 1.0. + */ +TEST_P(KeyAgreementTest, EcdhCurve25519) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Generate EC key in KeyMint (only access to public key material) + EcCurve curve = EcCurve::CURVE_25519; + EVP_PKEY_Ptr kmPubKey = nullptr; + GenerateKeyMintEcKey(curve, &kmPubKey); + + // Generate EC key on same curve locally (with access to private key material). + EVP_PKEY_Ptr privKey; + vector<uint8_t> encodedPublicKey; + GenerateLocalEcKey(curve, &privKey, &encodedPublicKey); + + // Agree on a key between local and KeyMint and check it. + CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey); + + CheckedDeleteKey(); +} + +/* + * KeyAgreementTest.EcdhCurve25519Imported + * + * Verifies that ECDH works for an imported curve25519 key. + */ +TEST_P(KeyAgreementTest, EcdhCurve25519Imported) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Import x25519 key into KeyMint. + EcCurve curve = EcCurve::CURVE_25519; + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_pkcs8_key)); + ASSERT_GT(cert_chain_.size(), 0); + X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate)); + ASSERT_NE(kmKeyCert, nullptr); + EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get())); + ASSERT_NE(kmPubKey.get(), nullptr); + + // Expect the import to emit corresponding public key data. + size_t kmPubKeySize = 32; + uint8_t kmPubKeyData[32]; + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize)); + ASSERT_EQ(kmPubKeySize, 32); + EXPECT_EQ(bin2hex(std::vector<uint8_t>(kmPubKeyData, kmPubKeyData + 32)), + bin2hex(std::vector<uint8_t>(x25519_pubkey.begin(), x25519_pubkey.end()))); + + // Generate EC key on same curve locally (with access to private key material). + EVP_PKEY_Ptr privKey; + vector<uint8_t> encodedPublicKey; + GenerateLocalEcKey(curve, &privKey, &encodedPublicKey); + + // Agree on a key between local and KeyMint and check it. + CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey); + + CheckedDeleteKey(); +} + +/* + * KeyAgreementTest.EcdhCurve25519InvalidSize + * + * Verifies that ECDH fails for curve25519 if the wrong size of public key is provided. + */ +TEST_P(KeyAgreementTest, EcdhCurve25519InvalidSize) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Generate EC key in KeyMint (only access to public key material) + EcCurve curve = EcCurve::CURVE_25519; + EVP_PKEY_Ptr kmPubKey = nullptr; + GenerateKeyMintEcKey(curve, &kmPubKey); + + // Generate EC key on same curve locally (with access to private key material). + EVP_PKEY_Ptr privKey; + vector<uint8_t> encodedPublicKey; + GenerateLocalEcKey(curve, &privKey, &encodedPublicKey); + + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); + string ZabFromKeyMintStr; + // Send in an incomplete public key. + ASSERT_NE(ErrorCode::OK, Finish(string(encodedPublicKey.begin(), encodedPublicKey.end() - 1), + &ZabFromKeyMintStr)); + + CheckedDeleteKey(); +} + +/* + * KeyAgreementTest.EcdhCurve25519Mismatch + * + * Verifies that ECDH fails between curve25519 and other curves. + */ +TEST_P(KeyAgreementTest, EcdhCurve25519Mismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Generate EC key in KeyMint (only access to public key material) + EcCurve curve = EcCurve::CURVE_25519; + EVP_PKEY_Ptr kmPubKey = nullptr; + GenerateKeyMintEcKey(curve, &kmPubKey); + + for (auto localCurve : ValidCurves()) { + if (localCurve == curve) { + continue; + } + // Generate EC key on a different curve locally (with access to private key material). + EVP_PKEY_Ptr privKey; + vector<uint8_t> encodedPublicKey; + GenerateLocalEcKey(localCurve, &privKey, &encodedPublicKey); + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); + string ZabFromKeyMintStr; + EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, + Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()), + &ZabFromKeyMintStr)); + } + + CheckedDeleteKey(); +} + INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest); using DestroyAttestationIdsTest = KeyMintAidlTestBase; diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp index c9d506f788..829780d442 100644 --- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp @@ -20,6 +20,7 @@ #include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h> #include <aidl/android/hardware/security/keymint/SecurityLevel.h> #include <android/binder_manager.h> +#include <binder/IServiceManager.h> #include <cppbor_parse.h> #include <gmock/gmock.h> #include <keymaster/cppcose/cppcose.h> @@ -29,6 +30,7 @@ #include <openssl/ec_key.h> #include <openssl/x509.h> #include <remote_prov/remote_prov_utils.h> +#include <set> #include <vector> #include "KeyMintAidlTestBase.h" @@ -40,6 +42,8 @@ using ::std::vector; namespace { +constexpr int32_t VERSION_WITH_UNIQUE_ID_SUPPORT = 2; + #define INSTANTIATE_REM_PROV_AIDL_TEST(name) \ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name); \ INSTANTIATE_TEST_SUITE_P( \ @@ -47,6 +51,7 @@ namespace { testing::ValuesIn(VtsRemotelyProvisionedComponentTests::build_params()), \ ::android::PrintInstanceNameToString) +using ::android::sp; using bytevec = std::vector<uint8_t>; using testing::MatchesRegex; using namespace remote_prov; @@ -175,6 +180,67 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std:: std::shared_ptr<IRemotelyProvisionedComponent> provisionable_; }; +/** + * Verify that every implementation reports a different unique id. + */ +TEST(NonParameterizedTests, eachRpcHasAUniqueId) { + std::set<std::string> uniqueIds; + for (auto hal : ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor)) { + ASSERT_TRUE(AServiceManager_isDeclared(hal.c_str())); + ::ndk::SpAIBinder binder(AServiceManager_waitForService(hal.c_str())); + std::shared_ptr<IRemotelyProvisionedComponent> rpc = + IRemotelyProvisionedComponent::fromBinder(binder); + ASSERT_NE(rpc, nullptr); + + RpcHardwareInfo hwInfo; + ASSERT_TRUE(rpc->getHardwareInfo(&hwInfo).isOk()); + + int32_t version; + ASSERT_TRUE(rpc->getInterfaceVersion(&version).isOk()); + if (version >= VERSION_WITH_UNIQUE_ID_SUPPORT) { + ASSERT_TRUE(hwInfo.uniqueId); + auto [_, wasInserted] = uniqueIds.insert(*hwInfo.uniqueId); + EXPECT_TRUE(wasInserted); + } else { + ASSERT_FALSE(hwInfo.uniqueId); + } + } +} + +using GetHardwareInfoTests = VtsRemotelyProvisionedComponentTests; + +INSTANTIATE_REM_PROV_AIDL_TEST(GetHardwareInfoTests); + +/** + * Verify that a valid curve is reported by the implementation. + */ +TEST_P(GetHardwareInfoTests, supportsValidCurve) { + RpcHardwareInfo hwInfo; + ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk()); + + const std::set<int> validCurves = {RpcHardwareInfo::CURVE_P256, RpcHardwareInfo::CURVE_25519}; + ASSERT_EQ(validCurves.count(hwInfo.supportedEekCurve), 1) + << "Invalid curve: " << hwInfo.supportedEekCurve; +} + +/** + * Verify that the unique id is within the length limits as described in RpcHardwareInfo.aidl. + */ +TEST_P(GetHardwareInfoTests, uniqueId) { + int32_t version; + ASSERT_TRUE(provisionable_->getInterfaceVersion(&version).isOk()); + + if (version < VERSION_WITH_UNIQUE_ID_SUPPORT) { + return; + } + + RpcHardwareInfo hwInfo; + ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk()); + ASSERT_TRUE(hwInfo.uniqueId); + EXPECT_GE(hwInfo.uniqueId->size(), 1); + EXPECT_LE(hwInfo.uniqueId->size(), 32); +} + using GenerateKeyTests = VtsRemotelyProvisionedComponentTests; INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests); diff --git a/sensors/1.0/default/convert.cpp b/sensors/1.0/default/convert.cpp index 53ceb0d92f..43ee3276b9 100644 --- a/sensors/1.0/default/convert.cpp +++ b/sensors/1.0/default/convert.cpp @@ -190,8 +190,6 @@ void convertFromSensorEvent(const sensors_event_t &src, Event *dst) { } default: { - CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); - memcpy(dst->u.data.data(), src.data, 16 * sizeof(float)); break; } @@ -330,9 +328,6 @@ void convertToSensorEvent(const Event &src, sensors_event_t *dst) { } default: { - CHECK_GE((int32_t)src.sensorType, - (int32_t)SensorType::DEVICE_PRIVATE_BASE); - memcpy(dst->data, src.u.data.data(), 16 * sizeof(float)); break; } diff --git a/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/Event.aidl b/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/Event.aidl index c92ab1ab0c..4f49002fce 100644 --- a/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/Event.aidl +++ b/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/Event.aidl @@ -52,6 +52,9 @@ parcelable Event { android.hardware.sensors.AdditionalInfo additional; android.hardware.sensors.Event.EventPayload.Data data; android.hardware.sensors.Event.EventPayload.HeadTracker headTracker; + android.hardware.sensors.Event.EventPayload.LimitedAxesImu limitedAxesImu; + android.hardware.sensors.Event.EventPayload.LimitedAxesImuUncal limitedAxesImuUncal; + android.hardware.sensors.Event.EventPayload.Heading heading; @FixedSize @VintfStability parcelable Vec4 { float x; @@ -86,11 +89,37 @@ parcelable Event { int discontinuityCount; } @FixedSize @VintfStability + parcelable LimitedAxesImu { + float x; + float y; + float z; + float xSupported; + float ySupported; + float zSupported; + } + @FixedSize @VintfStability + parcelable LimitedAxesImuUncal { + float x; + float y; + float z; + float xBias; + float yBias; + float zBias; + float xSupported; + float ySupported; + float zSupported; + } + @FixedSize @VintfStability parcelable HeartRate { float bpm; android.hardware.sensors.SensorStatus status; } @FixedSize @VintfStability + parcelable Heading { + float heading; + float accuracy; + } + @FixedSize @VintfStability parcelable MetaData { android.hardware.sensors.Event.EventPayload.MetaData.MetaDataEventType what; @Backing(type="int") @VintfStability diff --git a/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/SensorType.aidl b/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/SensorType.aidl index 3d7ab45cd8..8c864e90be 100644 --- a/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/SensorType.aidl +++ b/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/SensorType.aidl @@ -71,5 +71,10 @@ enum SensorType { ACCELEROMETER_UNCALIBRATED = 35, HINGE_ANGLE = 36, HEAD_TRACKER = 37, + ACCELEROMETER_LIMITED_AXES = 38, + GYROSCOPE_LIMITED_AXES = 39, + ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40, + GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41, + HEADING = 42, DEVICE_PRIVATE_BASE = 65536, } diff --git a/sensors/aidl/android/hardware/sensors/Event.aidl b/sensors/aidl/android/hardware/sensors/Event.aidl index fd6a8cc4ba..e8550f184b 100644 --- a/sensors/aidl/android/hardware/sensors/Event.aidl +++ b/sensors/aidl/android/hardware/sensors/Event.aidl @@ -132,6 +132,23 @@ parcelable Event { */ HeadTracker headTracker; + /** + * SensorType::ACCELEROMETER_LIMITED_AXES + * SensorType::GYROSCOPE_LIMITED_AXES + */ + LimitedAxesImu limitedAxesImu; + + /** + * SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED + * SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED + */ + LimitedAxesImuUncal limitedAxesImuUncal; + + /** + * SensorType::HEADING + */ + Heading heading; + @FixedSize @VintfStability parcelable Vec4 { @@ -201,6 +218,70 @@ parcelable Event { int discontinuityCount; } + /** + * Payload of the ACCELEROMETER_LIMITED_AXES and GYROSCOPE_LIMITED_AXES + * sensor types. + */ + @FixedSize + @VintfStability + parcelable LimitedAxesImu { + /** + * Acceleration or angular speed values. If certain axes are not + * supported, the associated value must be set to 0. + */ + float x; + float y; + float z; + + /** + * Limited axes sensors must not be supported for all three axes. + * These values indicate which axes are supported with a 1.0 for + * supported, and a 0 for not supported. The supported axes should + * be determined at build time and these values must not change + * during runtime. + */ + float xSupported; + float ySupported; + float zSupported; + } + + /** + * Payload of the ACCELEROMETER_LIMITED_AXES_UNCALIBRATED and + * GYROSCOPE_LIMITED_AXES_UNCALIBRATED sensor types. + */ + @FixedSize + @VintfStability + parcelable LimitedAxesImuUncal { + /** + * Acceleration (without bias compensation) or angular (speed + * (without drift compensation) values. If certain axes are not + * supported, the associated value must be set to 0. + */ + float x; + float y; + float z; + + /** + * Estimated bias values for uncalibrated accelerometer or + * estimated drift values for uncalibrated gyroscope. If certain + * axes are not supported, the associated value must be set to 0. + */ + float xBias; + float yBias; + float zBias; + + /** + * Limited axes sensors must not be supported for all three axes. + * These values indicate which axes are supported with a 1.0 for + * supported, and a 0 for not supported. The supported axes should + * be determined at build time and these values must not change + * during runtime. + */ + float xSupported; + float ySupported; + float zSupported; + } + @FixedSize @VintfStability parcelable HeartRate { @@ -218,6 +299,27 @@ parcelable Event { @FixedSize @VintfStability + parcelable Heading { + /** + * The direction in which the device is pointing relative to true + * north in degrees. The value must be between 0.0 (inclusive) and + * 360.0 (exclusive), with 0 indicating north, 90 east, 180 south, + * and 270 west. + */ + float heading; + /** + * Accuracy is defined at 68% confidence. In the case where the + * underlying distribution is assumed Gaussian normal, this would be + * considered one standard deviation. For example, if the heading + * returns 60 degrees, and accuracy returns 10 degrees, then there + * is a 68 percent probability of the true heading being between 50 + * degrees and 70 degrees. + */ + float accuracy; + } + + @FixedSize + @VintfStability parcelable MetaData { MetaDataEventType what; diff --git a/sensors/aidl/android/hardware/sensors/SensorType.aidl b/sensors/aidl/android/hardware/sensors/SensorType.aidl index 01e6bee1d5..9098894cc7 100644 --- a/sensors/aidl/android/hardware/sensors/SensorType.aidl +++ b/sensors/aidl/android/hardware/sensors/SensorType.aidl @@ -667,6 +667,57 @@ enum SensorType { HEAD_TRACKER = 37, /** + * ACCELEROMETER_LIMITED_AXES + * reporting-mode: continuous + * + * Equivalent to ACCELEROMETER, but supporting cases where one or two axes + * are not supported. + */ + ACCELEROMETER_LIMITED_AXES = 38, + + /** + * GYROSCOPE_LIMITED_AXES + * reporting-mode: continuous + * + * Equivalent to GYROSCOPE, but supporting cases where one or two axes are + * not supported. + */ + GYROSCOPE_LIMITED_AXES = 39, + + /** + * ACCELEROMETER_LIMITED_AXES_UNCALIBRATED + * reporting-mode: continuous + * + * Equivalent to ACCELEROMETER_UNCALIBRATED, but supporting cases where one + * or two axes are not supported. + */ + ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40, + + /** + * GYROSCOPE_LIMITED_AXES_UNCALIBRATED + * reporting-mode: continuous + * + * Equivalent to GYROSCOPE_UNCALIBRATED, but supporting cases where one or + * two axes are not supported. + */ + GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41, + + /** + * HEADING + * reporting-mode: continuous + * + * A sensor of this type measures the direction in which the device is + * pointing relative to true north in degrees. + * + * This sensor was added for automotive form factors. Other devices with a + * clear forward direction might find it useful as well. However, devices + * with a more ambiguous orientation such as phones or wearables might want + * to consider using other sensors such as Sensor.TYPE_ROTATION_VECTOR + * which might be more suitable. + */ + HEADING = 42, + + /** * Base for device manufacturers private sensor types. * These sensor types can't be exposed in the SDK. */ diff --git a/soundtrigger/aidl/Android.bp b/soundtrigger/aidl/Android.bp index fcccc27722..28e809047a 100644 --- a/soundtrigger/aidl/Android.bp +++ b/soundtrigger/aidl/Android.bp @@ -10,6 +10,7 @@ package { aidl_interface { name: "android.hardware.soundtrigger3", vendor_available: true, + host_supported: true, flags: ["-Werror", "-Weverything", ], srcs: [ "android/hardware/soundtrigger3/ISoundTriggerHw.aidl", diff --git a/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl index 2a3fc644ee..618331b3aa 100644 --- a/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl +++ b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl @@ -18,15 +18,12 @@ package android.hardware.soundtrigger3; import android.hardware.soundtrigger3.ISoundTriggerHwCallback; import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback; - -import android.media.soundtrigger.PhraseSoundModel; -import android.media.soundtrigger.Properties; -import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.ModelParameter; import android.media.soundtrigger.ModelParameterRange; +import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; +import android.media.soundtrigger.SoundModel; /** * SoundTrigger HAL interface. Used for hardware recognition of hotwords @@ -196,12 +193,12 @@ interface ISoundTriggerHw { * an audio stream associated with this recognition session. * @param config A RecognitionConfig structure containing attributes of the recognition to * perform. - * @throws ServiceSpecificException(RESOURCE_CONTENTION) if the model cannot be started due + * @throws ServiceSpecificException(RESOURCE_CONTENTION) if the model cannot be started due * to resource constraints. This is typically a temporary condition and the client may * retry after the onResourcesAvailable() global callback is invoked. - */ - void startRecognition(in int modelHandle, in int deviceHandle, - in int ioHandle, in RecognitionConfig config); + */ + void startRecognition( + in int modelHandle, in int deviceHandle, in int ioHandle, in RecognitionConfig config); /** * Stop recognition on a given model. @@ -235,7 +232,8 @@ interface ISoundTriggerHw { * @return This structure indicates supported attributes of the parameter for the given model * handle. If the parameter is not supported, null is returned. */ - @nullable ModelParameterRange queryParameter(in int modelHandle, in ModelParameter modelParam); + @nullable ModelParameterRange queryParameter( + in int modelHandle, in ModelParameter modelParam); /** * Get a model specific parameter. diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatus.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatus.aidl index fc0efc92be..1e0f5f0840 100644 --- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatus.aidl +++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatus.aidl @@ -76,4 +76,5 @@ union FrontendStatus { android.hardware.tv.tuner.FrontendIsdbtPartialReceptionFlag partialReceptionFlag; int[] streamIdList; int[] dvbtCellIds; + android.hardware.tv.tuner.FrontendScanAtsc3PlpInfo[] allPlpInfo; } diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusReadiness.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusReadiness.aidl new file mode 100644 index 0000000000..41944ce440 --- /dev/null +++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusReadiness.aidl @@ -0,0 +1,43 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS 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 <name>-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.tv.tuner; +/* @hide */ +@Backing(type="int") @VintfStability +enum FrontendStatusReadiness { + UNDEFINED = 0, + UNAVAILABLE = 1, + UNSTABLE = 2, + STABLE = 3, + UNSUPPORTED = 4, +} diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusType.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusType.aidl index 2cc62d533e..cd6ccb3dc1 100644 --- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusType.aidl +++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusType.aidl @@ -76,4 +76,5 @@ enum FrontendStatusType { ISDBT_PARTIAL_RECEPTION_FLAG = 38, STREAM_ID_LIST = 39, DVBT_CELL_IDS = 40, + ATSC3_ALL_PLP_INFO = 41, } diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/IFrontend.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/IFrontend.aidl index e7aa070552..3e3ff4fad6 100644 --- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/IFrontend.aidl +++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/IFrontend.aidl @@ -46,4 +46,6 @@ interface IFrontend { int linkCiCam(in int ciCamId); void unlinkCiCam(in int ciCamId); String getHardwareInfo(); + void removeOutputPid(int pid); + android.hardware.tv.tuner.FrontendStatusReadiness[] getFrontendStatusReadiness(in android.hardware.tv.tuner.FrontendStatusType[] statusTypes); } diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatus.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatus.aidl index ae6e46fa29..b5d02016df 100644 --- a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatus.aidl +++ b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatus.aidl @@ -27,6 +27,7 @@ import android.hardware.tv.tuner.FrontendModulation; import android.hardware.tv.tuner.FrontendModulationStatus; import android.hardware.tv.tuner.FrontendRollOff; import android.hardware.tv.tuner.FrontendSpectralInversion; +import android.hardware.tv.tuner.FrontendScanAtsc3PlpInfo; import android.hardware.tv.tuner.FrontendStatusAtsc3PlpInfo; import android.hardware.tv.tuner.FrontendTransmissionMode; import android.hardware.tv.tuner.LnbVoltage; @@ -241,5 +242,9 @@ union FrontendStatus { */ int[] dvbtCellIds; - + /** + * A list of all PLPs in the frequency band for ATSC3 frontend, which includes both tuned + * and not tuned PLPs for currently watching service. + */ + FrontendScanAtsc3PlpInfo[] allPlpInfo; } diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusReadiness.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusReadiness.aidl new file mode 100644 index 0000000000..a9e3080c69 --- /dev/null +++ b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusReadiness.aidl @@ -0,0 +1,54 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.tv.tuner; + +/** + * FrontendStatus readiness status. + * @hide + */ +@VintfStability +@Backing(type="int") +enum FrontendStatusReadiness { + /** + * The FrontendStatus’ readiness status for the given FrontendStatusType is + * undefined. + */ + UNDEFINED, + + /** + * The FrontendStatus for the given FrontendStatusType is currently + * unavailable. + */ + UNAVAILABLE, + + /** + * The FrontendStatus for the given FrontendStatusType can be read, but it’s + * unstable. + */ + UNSTABLE, + + /** + * The FrontendStatus for the given FrontendStatusType can be ready, and it’s + * stable. + */ + STABLE, + + /** + * The FrontendStatus for the given FrontendStatusType is not supported. + */ + UNSUPPORTED, +} diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusType.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusType.aidl index e7da51767a..8f3f2c56c1 100644 --- a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusType.aidl +++ b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusType.aidl @@ -130,7 +130,7 @@ enum FrontendStatusType { RF_LOCK, /** - * PLP information in a frequency band for ATSC3.0 frontend. + * Current tuned PLP information in a frequency band for ATSC3 frontend. */ ATSC3_PLP_INFO, @@ -222,10 +222,16 @@ enum FrontendStatusType { /** * Stream ID list included in a transponder. */ - STREAM_ID_LIST, + STREAM_ID_LIST, - /** - * DVB-T Cell Id. - */ - DVBT_CELL_IDS, + /** + * DVB-T Cell Id. + */ + DVBT_CELL_IDS, + + /** + * All PLP information in a frequency band for ATSC3 frontend, which includes both tuned + * and not tuned PLPs for currently watching service. + */ + ATSC3_ALL_PLP_INFO, } diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/IFrontend.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/IFrontend.aidl index 5b3ce39c29..12f2692992 100644 --- a/tv/tuner/aidl/android/hardware/tv/tuner/IFrontend.aidl +++ b/tv/tuner/aidl/android/hardware/tv/tuner/IFrontend.aidl @@ -19,6 +19,7 @@ package android.hardware.tv.tuner; import android.hardware.tv.tuner.FrontendScanType; import android.hardware.tv.tuner.FrontendSettings; import android.hardware.tv.tuner.FrontendStatus; +import android.hardware.tv.tuner.FrontendStatusReadiness; import android.hardware.tv.tuner.FrontendStatusType; import android.hardware.tv.tuner.IFrontendCallback; @@ -146,4 +147,23 @@ interface IFrontend { * @return the frontend hardware information. */ String getHardwareInfo(); + + /** + * Filter out unnecessary PID from frontend output. + * + * @param pid specify the PID will be filtered out. + * + * @return UNAVAILABLE if the frontend doesn’t support PID filtering out. + */ + void removeOutputPid(int pid); + + /** + * Gets FrontendStatus’ readiness statuses for given status types. + * + * @param statusTypes an array of status types. + * + * @return an array of current readiness statuses. The ith readiness status in + * the array presents fronted type statusTypes[i]’s readiness status. + */ + FrontendStatusReadiness[] getFrontendStatusReadiness(in FrontendStatusType[] statusTypes); } diff --git a/tv/tuner/aidl/default/Frontend.cpp b/tv/tuner/aidl/default/Frontend.cpp index 714612d006..056d01468a 100644 --- a/tv/tuner/aidl/default/Frontend.cpp +++ b/tv/tuner/aidl/default/Frontend.cpp @@ -34,6 +34,140 @@ Frontend::Frontend(FrontendType type, int32_t id, std::shared_ptr<Tuner> tuner) mTuner = tuner; // Init callback to nullptr mCallback = nullptr; + + switch (mType) { + case FrontendType::ISDBS: { + mFrontendCaps.set<FrontendCapabilities::Tag::isdbsCaps>(FrontendIsdbsCapabilities()); + mFrontendStatusCaps = { + FrontendStatusType::DEMOD_LOCK, + FrontendStatusType::SNR, + FrontendStatusType::FEC, + FrontendStatusType::MODULATION, + FrontendStatusType::MODULATIONS, + FrontendStatusType::ROLL_OFF, + FrontendStatusType::STREAM_ID_LIST, + }; + break; + } + case FrontendType::ATSC3: { + mFrontendCaps.set<FrontendCapabilities::Tag::atsc3Caps>(FrontendAtsc3Capabilities()); + mFrontendStatusCaps = { + FrontendStatusType::BER, + FrontendStatusType::PER, + FrontendStatusType::ATSC3_PLP_INFO, + FrontendStatusType::MODULATIONS, + FrontendStatusType::BERS, + FrontendStatusType::INTERLEAVINGS, + FrontendStatusType::BANDWIDTH, + FrontendStatusType::ATSC3_ALL_PLP_INFO, + }; + break; + } + case FrontendType::DVBC: { + mFrontendCaps.set<FrontendCapabilities::Tag::dvbcCaps>(FrontendDvbcCapabilities()); + mFrontendStatusCaps = { + FrontendStatusType::PRE_BER, FrontendStatusType::SIGNAL_QUALITY, + FrontendStatusType::MODULATION, FrontendStatusType::SPECTRAL, + FrontendStatusType::MODULATIONS, FrontendStatusType::CODERATES, + FrontendStatusType::INTERLEAVINGS, FrontendStatusType::BANDWIDTH, + }; + break; + } + case FrontendType::DVBS: { + mFrontendCaps.set<FrontendCapabilities::Tag::dvbsCaps>(FrontendDvbsCapabilities()); + mFrontendStatusCaps = { + FrontendStatusType::SIGNAL_STRENGTH, FrontendStatusType::SYMBOL_RATE, + FrontendStatusType::MODULATION, FrontendStatusType::MODULATIONS, + FrontendStatusType::ROLL_OFF, FrontendStatusType::IS_MISO, + }; + break; + } + case FrontendType::DVBT: { + mFrontendCaps.set<FrontendCapabilities::Tag::dvbtCaps>(FrontendDvbtCapabilities()); + mFrontendStatusCaps = { + FrontendStatusType::EWBS, + FrontendStatusType::PLP_ID, + FrontendStatusType::HIERARCHY, + FrontendStatusType::MODULATIONS, + FrontendStatusType::BANDWIDTH, + FrontendStatusType::GUARD_INTERVAL, + FrontendStatusType::TRANSMISSION_MODE, + FrontendStatusType::T2_SYSTEM_ID, + FrontendStatusType::DVBT_CELL_IDS, + }; + break; + } + case FrontendType::ISDBT: { + FrontendIsdbtCapabilities isdbtCaps{ + .modeCap = (int)FrontendIsdbtMode::MODE_1 | (int)FrontendIsdbtMode::MODE_2, + .bandwidthCap = (int)FrontendIsdbtBandwidth::BANDWIDTH_6MHZ, + .modulationCap = (int)FrontendIsdbtModulation::MOD_16QAM, + .coderateCap = (int)FrontendIsdbtCoderate::CODERATE_4_5 | + (int)FrontendIsdbtCoderate::CODERATE_6_7, + .guardIntervalCap = (int)FrontendIsdbtGuardInterval::INTERVAL_1_128, + .timeInterleaveCap = (int)FrontendIsdbtTimeInterleaveMode::AUTO | + (int)FrontendIsdbtTimeInterleaveMode::INTERLEAVE_1_0, + .isSegmentAuto = true, + .isFullSegment = true, + }; + mFrontendCaps.set<FrontendCapabilities::Tag::isdbtCaps>(isdbtCaps); + mFrontendStatusCaps = { + FrontendStatusType::AGC, + FrontendStatusType::LNA, + FrontendStatusType::MODULATION, + FrontendStatusType::MODULATIONS, + FrontendStatusType::BANDWIDTH, + FrontendStatusType::GUARD_INTERVAL, + FrontendStatusType::TRANSMISSION_MODE, + FrontendStatusType::ISDBT_SEGMENTS, + FrontendStatusType::ISDBT_MODE, + FrontendStatusType::ISDBT_PARTIAL_RECEPTION_FLAG, + FrontendStatusType::INTERLEAVINGS, + }; + break; + } + case FrontendType::ANALOG: { + mFrontendCaps.set<FrontendCapabilities::Tag::analogCaps>(FrontendAnalogCapabilities()); + mFrontendStatusCaps = { + FrontendStatusType::LAYER_ERROR, + FrontendStatusType::MER, + FrontendStatusType::UEC, + FrontendStatusType::TS_DATA_RATES, + }; + break; + } + case FrontendType::ATSC: { + mFrontendCaps.set<FrontendCapabilities::Tag::atscCaps>(FrontendAtscCapabilities()); + mFrontendStatusCaps = { + FrontendStatusType::FREQ_OFFSET, + FrontendStatusType::RF_LOCK, + FrontendStatusType::MODULATIONS, + FrontendStatusType::IS_LINEAR, + }; + break; + } + case FrontendType::ISDBS3: { + mFrontendCaps.set<FrontendCapabilities::Tag::isdbs3Caps>(FrontendIsdbs3Capabilities()); + mFrontendStatusCaps = { + FrontendStatusType::DEMOD_LOCK, FrontendStatusType::MODULATION, + FrontendStatusType::MODULATIONS, FrontendStatusType::ROLL_OFF, + FrontendStatusType::IS_SHORT_FRAMES, FrontendStatusType::STREAM_ID_LIST, + }; + break; + } + case FrontendType::DTMB: { + mFrontendCaps.set<FrontendCapabilities::Tag::dtmbCaps>(FrontendDtmbCapabilities()); + mFrontendStatusCaps = { + FrontendStatusType::MODULATIONS, FrontendStatusType::INTERLEAVINGS, + FrontendStatusType::BANDWIDTH, FrontendStatusType::GUARD_INTERVAL, + FrontendStatusType::TRANSMISSION_MODE, + }; + break; + } + default: { + break; + } + } } Frontend::~Frontend() {} @@ -708,6 +842,20 @@ void Frontend::scanThreadLoop() { status.set<FrontendStatus::dvbtCellIds>(dvbtCellIds); break; } + case FrontendStatusType::ATSC3_ALL_PLP_INFO: { + FrontendScanAtsc3PlpInfo info1; + info1.plpId = 1; + info1.bLlsFlag = false; + FrontendScanAtsc3PlpInfo info2; + info2.plpId = 2; + info2.bLlsFlag = true; + FrontendScanAtsc3PlpInfo info3; + info3.plpId = 3; + info3.bLlsFlag = false; + vector<FrontendScanAtsc3PlpInfo> infos = {info1, info2, info3}; + status.set<FrontendStatus::allPlpInfo>(infos); + break; + } default: { continue; } @@ -749,6 +897,10 @@ binder_status_t Frontend::dump(int fd, const char** /* args */, uint32_t /* numA dprintf(fd, " mType: %d\n", mType); dprintf(fd, " mIsLocked: %d\n", mIsLocked); dprintf(fd, " mCiCamId: %d\n", mCiCamId); + dprintf(fd, " mFrontendStatusCaps:"); + for (int i = 0; i < mFrontendStatusCaps.size(); i++) { + dprintf(fd, " %d\n", mFrontendStatusCaps[i]); + } return STATUS_OK; } @@ -759,6 +911,36 @@ binder_status_t Frontend::dump(int fd, const char** /* args */, uint32_t /* numA return ::ndk::ScopedAStatus::ok(); } +::ndk::ScopedAStatus Frontend::removeOutputPid(int32_t /* in_pid */) { + ALOGV("%s", __FUNCTION__); + + return ::ndk::ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(Result::UNAVAILABLE)); +} + +::ndk::ScopedAStatus Frontend::getFrontendStatusReadiness( + const std::vector<FrontendStatusType>& in_statusTypes, + std::vector<FrontendStatusReadiness>* _aidl_return) { + ALOGV("%s", __FUNCTION__); + + _aidl_return->resize(in_statusTypes.size()); + for (int i = 0; i < in_statusTypes.size(); i++) { + int j = 0; + while (j < mFrontendStatusCaps.size()) { + if (in_statusTypes[i] == mFrontendStatusCaps[j]) { + (*_aidl_return)[i] = FrontendStatusReadiness::STABLE; + break; + } + j++; + } + if (j >= mFrontendStatusCaps.size()) { + (*_aidl_return)[i] = FrontendStatusReadiness::UNSUPPORTED; + } + } + + return ::ndk::ScopedAStatus::ok(); +} + FrontendType Frontend::getFrontendType() { return mType; } @@ -776,6 +958,21 @@ bool Frontend::isLocked() { return mIsLocked; } +void Frontend::getFrontendInfo(FrontendInfo* _aidl_return) { + // assign randomly selected values for testing. + *_aidl_return = { + .type = mType, + .minFrequency = 139000000, + .maxFrequency = 1139000000, + .minSymbolRate = 45, + .maxSymbolRate = 1145, + .acquireRange = 30, + .exclusiveGroupId = 57, + .statusCaps = mFrontendStatusCaps, + .frontendCaps = mFrontendCaps, + }; +} + } // namespace tuner } // namespace tv } // namespace hardware diff --git a/tv/tuner/aidl/default/Frontend.h b/tv/tuner/aidl/default/Frontend.h index fdedf1ef6f..1d9ab53a61 100644 --- a/tv/tuner/aidl/default/Frontend.h +++ b/tv/tuner/aidl/default/Frontend.h @@ -50,6 +50,10 @@ class Frontend : public BnFrontend { ::ndk::ScopedAStatus linkCiCam(int32_t in_ciCamId, int32_t* _aidl_return) override; ::ndk::ScopedAStatus unlinkCiCam(int32_t in_ciCamId) override; ::ndk::ScopedAStatus getHardwareInfo(std::string* _aidl_return) override; + ::ndk::ScopedAStatus removeOutputPid(int32_t in_pid) override; + ::ndk::ScopedAStatus getFrontendStatusReadiness( + const std::vector<FrontendStatusType>& in_statusTypes, + std::vector<FrontendStatusReadiness>* _aidl_return) override; binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; @@ -57,6 +61,7 @@ class Frontend : public BnFrontend { int32_t getFrontendId(); string getSourceFile(); bool isLocked(); + void getFrontendInfo(FrontendInfo* _aidl_return); private: virtual ~Frontend(); @@ -73,6 +78,8 @@ class Frontend : public BnFrontend { FrontendSettings mFrontendSettings; FrontendScanType mFrontendScanType; std::ifstream mFrontendData; + FrontendCapabilities mFrontendCaps; + vector<FrontendStatusType> mFrontendStatusCaps; }; } // namespace tuner diff --git a/tv/tuner/aidl/default/Tuner.cpp b/tv/tuner/aidl/default/Tuner.cpp index 48c1b66a50..fa74288b09 100644 --- a/tv/tuner/aidl/default/Tuner.cpp +++ b/tv/tuner/aidl/default/Tuner.cpp @@ -49,153 +49,15 @@ void Tuner::init() { mFrontends[8] = ndk::SharedRefBase::make<Frontend>(FrontendType::ISDBS3, 8, this->ref<Tuner>()); mFrontends[9] = ndk::SharedRefBase::make<Frontend>(FrontendType::DTMB, 9, this->ref<Tuner>()); - vector<FrontendStatusType> statusCaps; - - FrontendCapabilities capsIsdbs; - capsIsdbs.set<FrontendCapabilities::Tag::isdbsCaps>(FrontendIsdbsCapabilities()); - mFrontendCaps[0] = capsIsdbs; - statusCaps = { - FrontendStatusType::DEMOD_LOCK, - FrontendStatusType::SNR, - FrontendStatusType::FEC, - FrontendStatusType::MODULATION, - FrontendStatusType::MODULATIONS, - FrontendStatusType::ROLL_OFF, - FrontendStatusType::STREAM_ID_LIST, - }; - mFrontendStatusCaps[0] = statusCaps; mMaxUsableFrontends[FrontendType::ISDBS] = 1; - - FrontendCapabilities capsAtsc3; - capsAtsc3.set<FrontendCapabilities::Tag::atsc3Caps>(FrontendAtsc3Capabilities()); - mFrontendCaps[1] = capsAtsc3; - statusCaps = { - FrontendStatusType::BER, - FrontendStatusType::PER, - FrontendStatusType::ATSC3_PLP_INFO, - FrontendStatusType::MODULATIONS, - FrontendStatusType::BERS, - FrontendStatusType::INTERLEAVINGS, - FrontendStatusType::BANDWIDTH, - }; - mFrontendStatusCaps[1] = statusCaps; mMaxUsableFrontends[FrontendType::ATSC3] = 1; - - FrontendCapabilities capsDvbc; - capsDvbc.set<FrontendCapabilities::Tag::dvbcCaps>(FrontendDvbcCapabilities()); - mFrontendCaps[2] = capsDvbc; - statusCaps = { - FrontendStatusType::PRE_BER, FrontendStatusType::SIGNAL_QUALITY, - FrontendStatusType::MODULATION, FrontendStatusType::SPECTRAL, - FrontendStatusType::MODULATIONS, FrontendStatusType::CODERATES, - FrontendStatusType::INTERLEAVINGS, FrontendStatusType::BANDWIDTH, - }; - mFrontendStatusCaps[2] = statusCaps; mMaxUsableFrontends[FrontendType::DVBC] = 1; - - FrontendCapabilities capsDvbs; - capsDvbs.set<FrontendCapabilities::Tag::dvbsCaps>(FrontendDvbsCapabilities()); - mFrontendCaps[3] = capsDvbs; - statusCaps = { - FrontendStatusType::SIGNAL_STRENGTH, FrontendStatusType::SYMBOL_RATE, - FrontendStatusType::MODULATION, FrontendStatusType::MODULATIONS, - FrontendStatusType::ROLL_OFF, FrontendStatusType::IS_MISO, - }; - mFrontendStatusCaps[3] = statusCaps; mMaxUsableFrontends[FrontendType::DVBS] = 1; - - FrontendCapabilities capsDvbt; - capsDvbt.set<FrontendCapabilities::Tag::dvbtCaps>(FrontendDvbtCapabilities()); - mFrontendCaps[4] = capsDvbt; - statusCaps = { - FrontendStatusType::EWBS, - FrontendStatusType::PLP_ID, - FrontendStatusType::HIERARCHY, - FrontendStatusType::MODULATIONS, - FrontendStatusType::BANDWIDTH, - FrontendStatusType::GUARD_INTERVAL, - FrontendStatusType::TRANSMISSION_MODE, - FrontendStatusType::T2_SYSTEM_ID, - FrontendStatusType::DVBT_CELL_IDS, - }; - mFrontendStatusCaps[4] = statusCaps; mMaxUsableFrontends[FrontendType::DVBT] = 1; - - FrontendCapabilities capsIsdbt; - FrontendIsdbtCapabilities isdbtCaps{ - .modeCap = (int)FrontendIsdbtMode::MODE_1 | (int)FrontendIsdbtMode::MODE_2, - .bandwidthCap = (int)FrontendIsdbtBandwidth::BANDWIDTH_6MHZ, - .modulationCap = (int)FrontendIsdbtModulation::MOD_16QAM, - .coderateCap = (int)FrontendIsdbtCoderate::CODERATE_4_5 | - (int)FrontendIsdbtCoderate::CODERATE_6_7, - .guardIntervalCap = (int)FrontendIsdbtGuardInterval::INTERVAL_1_128, - .timeInterleaveCap = (int)FrontendIsdbtTimeInterleaveMode::AUTO | - (int)FrontendIsdbtTimeInterleaveMode::INTERLEAVE_1_0, - .isSegmentAuto = true, - .isFullSegment = true, - }; - capsIsdbt.set<FrontendCapabilities::Tag::isdbtCaps>(isdbtCaps); - mFrontendCaps[5] = capsIsdbt; - statusCaps = { - FrontendStatusType::AGC, - FrontendStatusType::LNA, - FrontendStatusType::MODULATION, - FrontendStatusType::MODULATIONS, - FrontendStatusType::BANDWIDTH, - FrontendStatusType::GUARD_INTERVAL, - FrontendStatusType::TRANSMISSION_MODE, - FrontendStatusType::ISDBT_SEGMENTS, - FrontendStatusType::ISDBT_MODE, - FrontendStatusType::ISDBT_PARTIAL_RECEPTION_FLAG, - FrontendStatusType::INTERLEAVINGS, - }; - mFrontendStatusCaps[5] = statusCaps; mMaxUsableFrontends[FrontendType::ISDBT] = 1; - - FrontendCapabilities capsAnalog; - capsAnalog.set<FrontendCapabilities::Tag::analogCaps>(FrontendAnalogCapabilities()); - mFrontendCaps[6] = capsAnalog; - statusCaps = { - FrontendStatusType::LAYER_ERROR, - FrontendStatusType::MER, - FrontendStatusType::UEC, - FrontendStatusType::TS_DATA_RATES, - }; - mFrontendStatusCaps[6] = statusCaps; mMaxUsableFrontends[FrontendType::ANALOG] = 1; - - FrontendCapabilities capsAtsc; - capsAtsc.set<FrontendCapabilities::Tag::atscCaps>(FrontendAtscCapabilities()); - mFrontendCaps[7] = capsAtsc; - statusCaps = { - FrontendStatusType::FREQ_OFFSET, - FrontendStatusType::RF_LOCK, - FrontendStatusType::MODULATIONS, - FrontendStatusType::IS_LINEAR, - }; - mFrontendStatusCaps[7] = statusCaps; mMaxUsableFrontends[FrontendType::ATSC] = 1; - - FrontendCapabilities capsIsdbs3; - capsIsdbs3.set<FrontendCapabilities::Tag::isdbs3Caps>(FrontendIsdbs3Capabilities()); - mFrontendCaps[8] = capsIsdbs3; - statusCaps = { - FrontendStatusType::DEMOD_LOCK, FrontendStatusType::MODULATION, - FrontendStatusType::MODULATIONS, FrontendStatusType::ROLL_OFF, - FrontendStatusType::IS_SHORT_FRAMES, FrontendStatusType::STREAM_ID_LIST, - }; - mFrontendStatusCaps[8] = statusCaps; mMaxUsableFrontends[FrontendType::ISDBS3] = 1; - - FrontendCapabilities capsDtmb; - capsDtmb.set<FrontendCapabilities::Tag::dtmbCaps>(FrontendDtmbCapabilities()); - mFrontendCaps[9] = capsDtmb; - statusCaps = { - FrontendStatusType::MODULATIONS, FrontendStatusType::INTERLEAVINGS, - FrontendStatusType::BANDWIDTH, FrontendStatusType::GUARD_INTERVAL, - FrontendStatusType::TRANSMISSION_MODE, - }; - mFrontendStatusCaps[9] = statusCaps; mMaxUsableFrontends[FrontendType::DTMB] = 1; mLnbs.resize(2); @@ -266,24 +128,12 @@ Tuner::~Tuner() {} ::ndk::ScopedAStatus Tuner::getFrontendInfo(int32_t in_frontendId, FrontendInfo* _aidl_return) { ALOGV("%s", __FUNCTION__); - if (in_frontendId >= mFrontendSize) { + if (in_frontendId < 0 || in_frontendId >= mFrontendSize) { return ::ndk::ScopedAStatus::fromServiceSpecificError( static_cast<int32_t>(Result::INVALID_ARGUMENT)); } - // assign randomly selected values for testing. - *_aidl_return = { - .type = mFrontends[in_frontendId]->getFrontendType(), - .minFrequency = 139000000, - .maxFrequency = 1139000000, - .minSymbolRate = 45, - .maxSymbolRate = 1145, - .acquireRange = 30, - .exclusiveGroupId = 57, - .statusCaps = mFrontendStatusCaps[in_frontendId], - .frontendCaps = mFrontendCaps[in_frontendId], - }; - + mFrontends[in_frontendId]->getFrontendInfo(_aidl_return); return ::ndk::ScopedAStatus::ok(); } @@ -359,9 +209,6 @@ binder_status_t Tuner::dump(int fd, const char** args, uint32_t numArgs) { dprintf(fd, "Frontends:\n"); for (int i = 0; i < mFrontendSize; i++) { mFrontends[i]->dump(fd, args, numArgs); - for (int j = 0; j < mFrontendStatusCaps[i].size(); j++) { - dprintf(fd, " statusCap: %d\n", mFrontendStatusCaps[i][j]); - } } } { diff --git a/tv/tuner/aidl/default/Tuner.h b/tv/tuner/aidl/default/Tuner.h index 216a2b6280..ad73003eeb 100644 --- a/tv/tuner/aidl/default/Tuner.h +++ b/tv/tuner/aidl/default/Tuner.h @@ -75,8 +75,6 @@ class Tuner : public BnTuner { private: // Static mFrontends array to maintain local frontends information map<int32_t, std::shared_ptr<Frontend>> mFrontends; - map<int32_t, FrontendCapabilities> mFrontendCaps; - map<int32_t, vector<FrontendStatusType>> mFrontendStatusCaps; map<int32_t, int32_t> mFrontendToDemux; map<int32_t, std::shared_ptr<Demux>> mDemuxes; // To maintain how many Frontends we have diff --git a/tv/tuner/aidl/vts/functional/FrontendTests.cpp b/tv/tuner/aidl/vts/functional/FrontendTests.cpp index 41e98ea586..a1f51dfe1a 100644 --- a/tv/tuner/aidl/vts/functional/FrontendTests.cpp +++ b/tv/tuner/aidl/vts/functional/FrontendTests.cpp @@ -298,6 +298,13 @@ AssertionResult FrontendTests::linkCiCam(int32_t ciCamId) { return AssertionResult(status.isOk()); } +AssertionResult FrontendTests::removeOutputPid(int32_t removePid) { + ndk::ScopedAStatus status; + status = mFrontend->removeOutputPid(removePid); + return AssertionResult(status.isOk() || status.getServiceSpecificError() == + static_cast<int32_t>(Result::UNAVAILABLE)); +} + AssertionResult FrontendTests::unlinkCiCam(int32_t ciCamId) { ndk::ScopedAStatus status = mFrontend->unlinkCiCam(ciCamId); return AssertionResult(status.isOk()); @@ -414,6 +421,13 @@ void FrontendTests::verifyFrontendStatus(vector<FrontendStatusType> statusTypes, expectStatuses[i].get<FrontendStatus::Tag::dvbtCellIds>().begin())); break; } + case FrontendStatusType::ATSC3_ALL_PLP_INFO: { + ASSERT_TRUE(std::equal( + realStatuses[i].get<FrontendStatus::Tag::allPlpInfo>().begin(), + realStatuses[i].get<FrontendStatus::Tag::allPlpInfo>().end(), + expectStatuses[i].get<FrontendStatus::Tag::allPlpInfo>().begin())); + break; + } default: { continue; } @@ -501,6 +515,7 @@ void FrontendTests::tuneTest(FrontendConfig frontendConf) { ASSERT_TRUE(setFrontendCallback()); if (frontendConf.canConnectToCiCam) { ASSERT_TRUE(linkCiCam(frontendConf.ciCamId)); + ASSERT_TRUE(removeOutputPid(frontendConf.removePid)); ASSERT_TRUE(unlinkCiCam(frontendConf.ciCamId)); } ASSERT_TRUE(tuneFrontend(frontendConf, false /*testWithDemux*/)); @@ -566,3 +581,47 @@ void FrontendTests::scanTest(FrontendConfig frontendConf, FrontendScanType scanT ASSERT_TRUE(stopScanFrontend()); ASSERT_TRUE(closeFrontend()); } + +void FrontendTests::statusReadinessTest(FrontendConfig frontendConf) { + int32_t feId; + vector<FrontendStatusType> allTypes; + vector<FrontendStatusReadiness> readiness; + getFrontendIdByType(frontendConf.type, feId); + ASSERT_TRUE(feId != INVALID_ID); + ASSERT_TRUE(openFrontendById(feId)); + ASSERT_TRUE(setFrontendCallback()); + if (frontendConf.canConnectToCiCam) { + ASSERT_TRUE(linkCiCam(frontendConf.ciCamId)); + ASSERT_TRUE(removeOutputPid(frontendConf.removePid)); + ASSERT_TRUE(unlinkCiCam(frontendConf.ciCamId)); + } + ASSERT_TRUE(getFrontendInfo(feId)); + ASSERT_TRUE(tuneFrontend(frontendConf, false /*testWithDemux*/)); + + // TODO: find a better way to push all frontend status types + for (int32_t i = 0; i < static_cast<int32_t>(FrontendStatusType::ATSC3_ALL_PLP_INFO); i++) { + allTypes.push_back(static_cast<FrontendStatusType>(i)); + } + ndk::ScopedAStatus status = mFrontend->getFrontendStatusReadiness(allTypes, &readiness); + ASSERT_TRUE(status.isOk()); + ASSERT_TRUE(readiness.size() == allTypes.size()); + for (int32_t i = 0; i < readiness.size(); i++) { + int32_t j = 0; + while (j < mFrontendInfo.statusCaps.size()) { + if (allTypes[i] == mFrontendInfo.statusCaps[j]) { + ASSERT_TRUE(readiness[i] == FrontendStatusReadiness::UNAVAILABLE || + readiness[i] == FrontendStatusReadiness::UNSTABLE || + readiness[i] == FrontendStatusReadiness::STABLE); + break; + } + j++; + } + + if (j >= mFrontendInfo.statusCaps.size()) { + ASSERT_TRUE(readiness[i] == FrontendStatusReadiness::UNSUPPORTED); + } + } + + ASSERT_TRUE(stopTuneFrontend(false /*testWithDemux*/)); + ASSERT_TRUE(closeFrontend()); +} diff --git a/tv/tuner/aidl/vts/functional/FrontendTests.h b/tv/tuner/aidl/vts/functional/FrontendTests.h index 1745f76cd8..1746c8efcc 100644 --- a/tv/tuner/aidl/vts/functional/FrontendTests.h +++ b/tv/tuner/aidl/vts/functional/FrontendTests.h @@ -95,12 +95,14 @@ class FrontendTests { AssertionResult linkCiCam(int32_t ciCamId); AssertionResult unlinkCiCam(int32_t ciCamId); AssertionResult verifyHardwareInfo(); + AssertionResult removeOutputPid(int32_t removePid); void getFrontendIdByType(FrontendType feType, int32_t& feId); void tuneTest(FrontendConfig frontendConf); void scanTest(FrontendConfig frontend, FrontendScanType type); void debugInfoTest(FrontendConfig frontendConf); void maxNumberOfFrontendsTest(); + void statusReadinessTest(FrontendConfig frontendConf); void setDvrTests(DvrTests* dvrTests) { mExternalDvrTests = dvrTests; } void setDemux(std::shared_ptr<IDemux> demux) { getDvrTests()->setDemux(demux); } diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp index 0566089833..c99da419ed 100644 --- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp +++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp @@ -907,6 +907,14 @@ TEST_P(TunerFrontendAidlTest, maxNumberOfFrontends) { mFrontendTests.maxNumberOfFrontendsTest(); } +TEST_P(TunerFrontendAidlTest, statusReadinessTest) { + description("Test Max Frontend status readiness"); + if (!live.hasFrontendConnection) { + return; + } + mFrontendTests.statusReadinessTest(frontendMap[live.frontendId]); +} + TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowVideoFilterTest) { description("Test Video Filter functionality in Broadcast use case."); if (!live.hasFrontendConnection) { diff --git a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h index b73d59411b..3009c4a28d 100644 --- a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h +++ b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h @@ -83,6 +83,7 @@ struct FrontendConfig { FrontendType type; bool canConnectToCiCam; int32_t ciCamId; + int32_t removePid; FrontendSettings settings; vector<FrontendStatusType> tuneStatusTypes; vector<FrontendStatus> expectTuneStatuses; @@ -304,7 +305,8 @@ struct TunerTestingConfigAidlReader1_0 { // TODO: b/182519645 complete the tune status config frontendMap[id].tuneStatusTypes = types; frontendMap[id].expectTuneStatuses = statuses; - getCiCamInfo(feConfig, frontendMap[id].canConnectToCiCam, frontendMap[id].ciCamId); + getCiCamInfo(feConfig, frontendMap[id].canConnectToCiCam, frontendMap[id].ciCamId, + frontendMap[id].removePid); } } } @@ -1004,13 +1006,16 @@ struct TunerTestingConfigAidlReader1_0 { return recordSettings; } - static void getCiCamInfo(Frontend feConfig, bool& canConnectToCiCam, int32_t& ciCamId) { + static void getCiCamInfo(Frontend feConfig, bool& canConnectToCiCam, int32_t& ciCamId, + int32_t& removePid) { if (!feConfig.hasConnectToCicamId()) { canConnectToCiCam = false; ciCamId = -1; + removePid = -1; return; } canConnectToCiCam = true; ciCamId = static_cast<int32_t>(feConfig.getConnectToCicamId()); + removePid = static_cast<int32_t>(feConfig.getRemoveOutputPid()); } }; diff --git a/tv/tuner/config/api/current.txt b/tv/tuner/config/api/current.txt index 4d519d7cb5..383d49f452 100644 --- a/tv/tuner/config/api/current.txt +++ b/tv/tuner/config/api/current.txt @@ -317,6 +317,7 @@ package android.media.tuner.testing.configuration.V1_0 { method @Nullable public java.math.BigInteger getFrequency(); method @Nullable public String getId(); method @Nullable public boolean getIsSoftwareFrontend(); + method @Nullable public java.math.BigInteger getRemoveOutputPid(); method @Nullable public android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum getType(); method public void setConnectToCicamId(@Nullable java.math.BigInteger); method public void setDvbsFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings); @@ -325,6 +326,7 @@ package android.media.tuner.testing.configuration.V1_0 { method public void setFrequency(@Nullable java.math.BigInteger); method public void setId(@Nullable String); method public void setIsSoftwareFrontend(@Nullable boolean); + method public void setRemoveOutputPid(@Nullable java.math.BigInteger); method public void setType(@Nullable android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum); } diff --git a/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml b/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml index da77200f8a..fefe86edab 100644 --- a/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml +++ b/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml @@ -42,6 +42,8 @@ "softwareFeInputPath": used as the source of the software frontend. "connectToCicamId": if the device supports frontend connecting to cicam, the target cicam id needs to be configured here. Supported in Tuner 1.1 or higher. + "removeOutputPid": the unnecessary PID will be filtered out from frontend + output. Supported in Tuner 2.0 or higher. "frequency": the frequency used to configure tune and scan. "endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher. @@ -53,11 +55,13 @@ --> <frontends> <frontend id="FE_DEFAULT" type="DVBT" isSoftwareFrontend="true" - connectToCicamId="0" frequency="578000000" endFrequency="800000000"> + connectToCicamId="0" removeOutputPid="10" frequency="578000000" + endFrequency="800000000"> <dvbtFrontendSettings bandwidth="8" transmissionMode="128" isHighPriority="1"/> </frontend> <frontend id="FE_DVBS_0" type="DVBS" isSoftwareFrontend="true" - connectToCicamId="0" frequency="578000000" endFrequency="800000000"> + connectToCicamId="0" removeOutputPid="10" frequency="578000000" + endFrequency="800000000"> </frontend> </frontends> <!-- Filter section: diff --git a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd index 94f108b43a..59abd9aa61 100644 --- a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd +++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd @@ -94,6 +94,8 @@ "connectToCicamId": if the device supports frontend connecting to cicam, the target cicam id needs to be configured here. Supported in Tuner 1.1 or higher. + "removeOutputPid": the unnecessary PID will be filtered out from frontend + output. Supported in Tuner 2.0 or higher. "frequency": the frequency used to configure tune and scan. "endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher. @@ -125,6 +127,7 @@ <xs:attribute name="isSoftwareFrontend" type="xs:boolean" use="required"/> <xs:attribute name="frequency" type="xs:nonNegativeInteger" use="required"/> <xs:attribute name="connectToCicamId" type="xs:nonNegativeInteger" use="optional"/> + <xs:attribute name="removeOutputPid" type="xs:nonNegativeInteger" use="optional"/> <xs:attribute name="endFrequency" type="xs:nonNegativeInteger" use="optional"/> </xs:complexType> diff --git a/usb/aidl/Android.bp b/usb/aidl/Android.bp new file mode 100644 index 0000000000..d1e9e680e9 --- /dev/null +++ b/usb/aidl/Android.bp @@ -0,0 +1,33 @@ +// Copyright (C) 2021 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. + +aidl_interface { + name: "android.hardware.usb", + vendor_available: true, + srcs: ["android/hardware/usb/*.aidl"], + stability: "vintf", + backend: { + cpp: { + enabled: false, + }, + java: { + sdk_version: "module_current", + }, + ndk: { + vndk: { + enabled: true, + }, + }, + }, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantDetectionStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantDetectionStatus.aidl new file mode 100644 index 0000000000..24c69664f7 --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantDetectionStatus.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +enum ContaminantDetectionStatus { + NOT_SUPPORTED = 0, + DISABLED = 1, + NOT_DETECTED = 2, + DETECTED = 3, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionMode.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionMode.aidl new file mode 100644 index 0000000000..99798693ff --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionMode.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +enum ContaminantProtectionMode { + NONE = 0, + FORCE_SINK = 1, + FORCE_SOURCE = 2, + FORCE_DISABLE = 3, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionStatus.aidl new file mode 100644 index 0000000000..9642261444 --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionStatus.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +enum ContaminantProtectionStatus { + NONE = 0, + FORCE_SINK = 1, + FORCE_SOURCE = 2, + FORCE_DISABLE = 3, + DISABLED = 4, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl new file mode 100644 index 0000000000..4ba9ff8dff --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +interface IUsb { + oneway void enableContaminantPresenceDetection(in String portName, in boolean enable, long transactionId); + oneway void enableUsbData(in String portName, boolean enable, long transactionId); + oneway void enableUsbDataWhileDocked(in String portName, long transactionId); + oneway void queryPortStatus(long transactionId); + oneway void setCallback(in android.hardware.usb.IUsbCallback callback); + oneway void switchRole(in String portName, in android.hardware.usb.PortRole role, long transactionId); + oneway void limitPowerTransfer(in String portName, boolean limit, long transactionId); +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsbCallback.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsbCallback.aidl new file mode 100644 index 0000000000..57f02c548c --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsbCallback.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +interface IUsbCallback { + oneway void notifyPortStatusChange(in android.hardware.usb.PortStatus[] currentPortStatus, in android.hardware.usb.Status retval); + oneway void notifyRoleSwitchStatus(in String portName, in android.hardware.usb.PortRole newRole, in android.hardware.usb.Status retval, long transactionId); + oneway void notifyEnableUsbDataStatus(in String portName, boolean enable, in android.hardware.usb.Status retval, long transactionId); + oneway void notifyEnableUsbDataWhileDockedStatus(in String portName, in android.hardware.usb.Status retval, long transactionId); + oneway void notifyContaminantEnabledStatus(in String portName, boolean enable, in android.hardware.usb.Status retval, long transactionId); + oneway void notifyQueryPortStatus(in String portName, in android.hardware.usb.Status retval, long transactionId); + oneway void notifyLimitPowerTransferStatus(in String portName, boolean limit, in android.hardware.usb.Status retval, long transactionId); +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortDataRole.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortDataRole.aidl new file mode 100644 index 0000000000..105b316775 --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortDataRole.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +enum PortDataRole { + NONE = 0, + HOST = 1, + DEVICE = 2, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortMode.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortMode.aidl new file mode 100644 index 0000000000..34e43343ad --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortMode.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +enum PortMode { + NONE = 0, + UFP = 1, + DFP = 2, + DRP = 3, + AUDIO_ACCESSORY = 4, + DEBUG_ACCESSORY = 5, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortPowerRole.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortPowerRole.aidl new file mode 100644 index 0000000000..0e6f3fb426 --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortPowerRole.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +enum PortPowerRole { + NONE = 0, + SOURCE = 1, + SINK = 2, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortRole.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortRole.aidl new file mode 100644 index 0000000000..c66aeccde9 --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortRole.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +union PortRole { + android.hardware.usb.PortPowerRole powerRole = android.hardware.usb.PortPowerRole.NONE; + android.hardware.usb.PortDataRole dataRole; + android.hardware.usb.PortMode mode; +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl new file mode 100644 index 0000000000..dfd99fb249 --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +parcelable PortStatus { + String portName; + android.hardware.usb.PortDataRole currentDataRole = android.hardware.usb.PortDataRole.NONE; + android.hardware.usb.PortPowerRole currentPowerRole = android.hardware.usb.PortPowerRole.NONE; + android.hardware.usb.PortMode currentMode = android.hardware.usb.PortMode.NONE; + boolean canChangeMode; + boolean canChangeDataRole; + boolean canChangePowerRole; + android.hardware.usb.PortMode[] supportedModes; + android.hardware.usb.ContaminantProtectionMode[] supportedContaminantProtectionModes; + boolean supportsEnableContaminantPresenceProtection; + android.hardware.usb.ContaminantProtectionStatus contaminantProtectionStatus = android.hardware.usb.ContaminantProtectionStatus.NONE; + boolean supportsEnableContaminantPresenceDetection; + android.hardware.usb.ContaminantDetectionStatus contaminantDetectionStatus = android.hardware.usb.ContaminantDetectionStatus.NOT_SUPPORTED; + android.hardware.usb.UsbDataStatus[] usbDataStatus; + boolean powerTransferLimited; + android.hardware.usb.PowerBrickStatus powerBrickStatus; +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PowerBrickStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PowerBrickStatus.aidl new file mode 100644 index 0000000000..01d2fdd9f7 --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PowerBrickStatus.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +enum PowerBrickStatus { + UNKNOWN = 0, + CONNECTED = 1, + NOT_CONNECTED = 2, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/Status.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/Status.aidl new file mode 100644 index 0000000000..f28fc2a70e --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/Status.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@Backing(type="int") @VintfStability +enum Status { + SUCCESS = 0, + ERROR = 1, + INVALID_ARGUMENT = 2, + UNRECOGNIZED_ROLE = 3, + NOT_SUPPORTED = 4, +} diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/UsbDataStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/UsbDataStatus.aidl new file mode 100644 index 0000000000..e2c0cfbef1 --- /dev/null +++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/UsbDataStatus.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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 <name>-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.usb; +@VintfStability +enum UsbDataStatus { + UNKNOWN = 0, + ENABLED = 1, + DISABLED_OVERHEAT = 2, + DISABLED_CONTAMINANT = 3, + DISABLED_DOCK = 4, + DISABLED_FORCE = 5, + DISABLED_DEBUG = 6, +} diff --git a/usb/aidl/android/hardware/usb/ContaminantDetectionStatus.aidl b/usb/aidl/android/hardware/usb/ContaminantDetectionStatus.aidl new file mode 100644 index 0000000000..d9bc576f94 --- /dev/null +++ b/usb/aidl/android/hardware/usb/ContaminantDetectionStatus.aidl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 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.usb; + +@VintfStability +enum ContaminantDetectionStatus { + /** + * Contaminant presence detection is not supported. + */ + NOT_SUPPORTED = 0, + /** + * Contaminant presence detection is supported but disabled. + */ + DISABLED = 1, + /** + * Contaminant presence detection is enabled and contaminant not detected. + */ + NOT_DETECTED = 2, + /** + * Contaminant presence detection is enabled and contaminant detected. + */ + DETECTED = 3, +} diff --git a/usb/aidl/android/hardware/usb/ContaminantProtectionMode.aidl b/usb/aidl/android/hardware/usb/ContaminantProtectionMode.aidl new file mode 100644 index 0000000000..47c073dcc4 --- /dev/null +++ b/usb/aidl/android/hardware/usb/ContaminantProtectionMode.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 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.usb; + +@VintfStability +enum ContaminantProtectionMode { + /** + * No action performed upon detection of contaminant presence. + */ + NONE = 0, + /** + * Upon detection of contaminant presence, Port is forced to sink only + * mode where a port shall only detect chargers until contaminant presence + * is no longer detected. + */ + FORCE_SINK = 1, + /** + * Upon detection of contaminant presence, Port is forced to source only + * mode where a port shall only detect usb accessories such as headsets + * until contaminant presence is no longer detected. + */ + FORCE_SOURCE = 2, + /** + * Upon detection of contaminant presence, port is disabled until contaminant + * presence is no longer detected. In the disabled state port will + * not respond to connection of chargers or usb accessories. + */ + FORCE_DISABLE = 3, +} diff --git a/usb/aidl/android/hardware/usb/ContaminantProtectionStatus.aidl b/usb/aidl/android/hardware/usb/ContaminantProtectionStatus.aidl new file mode 100644 index 0000000000..c4fa979698 --- /dev/null +++ b/usb/aidl/android/hardware/usb/ContaminantProtectionStatus.aidl @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 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.usb; + +import android.hardware.usb.ContaminantProtectionMode; + +@VintfStability +enum ContaminantProtectionStatus { + /** + * No action performed upon detection of contaminant presence. + */ + NONE = 0, + /** + * Upon detection of contaminant presence, Port is forced to sink only + * mode where a port shall only detect chargers until contaminant presence + * is no longer detected. + */ + FORCE_SINK = 1, + /** + * Upon detection of contaminant presence, Port is forced to source only + * mode where a port shall only detect usb accessories such as headsets + * until contaminant presence is no longer detected. + */ + FORCE_SOURCE = 2, + /** + * Upon detection of contaminant presence, port is disabled until contaminant + * presence is no longer detected. In the disabled state port will + * not respond to connection of chargers or usb accessories. + */ + FORCE_DISABLE = 3, + /** + * Client disabled cotaminant protection by calling + * enableContaminantPresencePortProtection set to false. Low level drivers should + * not autmomously take any corrective action when contaminant presence is detected. + */ + DISABLED = 4, +} diff --git a/usb/aidl/android/hardware/usb/IUsb.aidl b/usb/aidl/android/hardware/usb/IUsb.aidl new file mode 100644 index 0000000000..d296fbb209 --- /dev/null +++ b/usb/aidl/android/hardware/usb/IUsb.aidl @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 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.usb; + +import android.hardware.usb.IUsbCallback; +import android.hardware.usb.PortRole; + +@VintfStability +oneway interface IUsb { + /** + * When supportsEnableContaminantPresenceDetection is true, + * enableContaminantPresenceDetection enables/disables contaminant + * presence detection algorithm. Calling enableContaminantPresenceDetection + * when supportsEnableContaminantPresenceDetection is false does + * not have any effect. + * Change in contantaminant presence status should be notified to the + * client via notifyPortStatusChange through PortStatus. + * + * @param portName name of the port. + * @param enable true Enable contaminant presence detection algorithm. + * false Disable contaminant presence detection algorithm. + * @param transactionId ID to be used when invoking the callback. + */ + void enableContaminantPresenceDetection(in String portName, in boolean enable, long transactionId); + + /** + * This function is used to enable/disable USB data controller. + * + * @param portName Name of the port. + * @param enable true Enable USB data signaling. + * false Disable USB data signaling. + * @param transactionId ID to be used when invoking the callback. + * + */ + void enableUsbData(in String portName, boolean enable, long transactionId); + + /** + * This function is used to enable USB controller if and when the controller + * disabled due to docking event. + * + * @param portName Name of the port. + * @param transactionId ID to be used when invoking the callback. + */ + void enableUsbDataWhileDocked(in String portName, long transactionId); + + /** + * This functions is used to request the hal for the current status + * status of the Type-C ports. The result of the query would be sent + * through the IUsbCallback object's notifyRoleSwitchStatus + * to the caller. This api would would let the caller know of the number + * of type-c ports that are present and their connection status through the + * PortStatus type. + * @param transactionId ID to be used when invoking the callback. + */ + void queryPortStatus(long transactionId); + + /** + * This function is used to register a callback function which is + * called by the HAL to inform the client of port status updates and + * result of the requested operation. Please refer IUsbCallback for + * complete description of when each of the IUsbCallback's interface + * methods is expected to be called. + * + * @param callback IUsbCallback object used to convey status to the + * userspace. + */ + void setCallback(in IUsbCallback callback); + + /** + * This function is used to change the port role of a specific port. + * For example, when DR_SWAP or PR_SWAP is supported. + * The status of the role switch will be informed through IUsbCallback + * object's notifyPortStatusChange method. + * + * @param portName name of the port for which the role has to be changed + * @param role the new port role. + * @param transactionId ID to be used when invoking the callback. + */ + void switchRole(in String portName, in PortRole role, long transactionId); + + /** + * This function is used to limit power transfer in and out of the port. + * When limited, the port does not charge from the partner port. + * Also, the port limits sourcing power to the partner port when the USB + * specification allows it to do so. + * + * @param portName name of the port for which power transfer is being limited. + * @param limit true limit power transfer. + * false relax limiting power transfer. + * @param transactionId ID to be used when invoking the callback. + */ + void limitPowerTransfer(in String portName, boolean limit, long transactionId); +} diff --git a/usb/aidl/android/hardware/usb/IUsbCallback.aidl b/usb/aidl/android/hardware/usb/IUsbCallback.aidl new file mode 100644 index 0000000000..e33672aa2b --- /dev/null +++ b/usb/aidl/android/hardware/usb/IUsbCallback.aidl @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 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.usb; + +import android.hardware.usb.PortRole; +import android.hardware.usb.PortStatus; +import android.hardware.usb.Status; + +/** + * Callback object used for all the IUsb async methods which expects a result. + * Caller is expected to register the callback object using setCallback method + * to receive updates on the PortStatus. + */ +@VintfStability +oneway interface IUsbCallback { + /** + * Used to convey the current port status to the caller. + * Must be called either when PortState changes due to the port partner or + * when caller requested for the PortStatus update through queryPortStatus. + * + * @param currentPortStatus describes the status of all the USB ports in the + * device. + * @param retval SUCCESS when the required information was enquired form + * kernel and the PortStatus object was built. + * ERROR otherwise. + */ + void notifyPortStatusChange(in PortStatus[] currentPortStatus, in Status retval); + + /** + * Used to notify the result of the switchRole call to the caller. + * + * @param portName name of the port for which the roleswap is requested. + * @param newRole the new role requested by the caller. + * @param retval SUCCESS if the role switch succeeded. FAILURE otherwise. + * @param transactionId transactionId sent during switchRole request. + */ + void notifyRoleSwitchStatus(in String portName, in PortRole newRole, in Status retval, + long transactionId); + + /** + * Used to notify the result of notifyEnableUsbDataStatus call to the caller. + * + * @param portName name of the port for which the enableUsbData is requested. + * @param enable true when usb data is enabled. + * false when usb data is disabled. + * @param retval SUCCESS if current request succeeded. FAILURE otherwise. + * @param transactionId transactionId sent during enableUsbData request. + */ + void notifyEnableUsbDataStatus(in String portName, boolean enable, in Status retval, + long transactionId); + + /** + * Used to notify the result of enableUsbDataWhileDocked call to the caller. + * + * @param portName name of the port for which the enableUsbDataWhileDocked is requested. + * @param retval SUCCESS if current request succeeded. FAILURE otherwise. + * @param transactionId transactionId sent during enableUsbDataWhileDocked request. + */ + void notifyEnableUsbDataWhileDockedStatus(in String portName, in Status retval, + long transactionId); + + /** + * Used to notify the result of enableContaminantPresenceDetection. + * + * @param portName name of the port for which contaminant detection is enabled/disabled. + * @param enable true when contaminant detection is enabled. + * false when disabled. + * @param retval SUCCESS if the request for enabling/disabling contamiant detection succeeds. + * FAILURE otherwise. + * @param transactionId transactionId sent during queryPortStatus request + */ + void notifyContaminantEnabledStatus(in String portName, boolean enable, in Status retval, + long transactionId); + + /** + * Used to notify the request to query port status. + * + * @param portName name of the port for which port status is queried. + * @param retval SUCCESS if the port query succeeded. FAILURE otherwise. + * @param transactionId transactionId sent during queryPortStatus request + */ + void notifyQueryPortStatus(in String portName, in Status retval, long transactionId); + + /** + * Used to notify the result of requesting limitPowerTransfer. + * + * @param portName name of the port for which power transfer is being limited. + * @param limit true limit power transfer. + * false relax limiting power transfer. + * @param retval SUCCESS if the request to enable/disable limitPowerTransfer succeeds. + * FAILURE otherwise. + * @param transactionId ID sent during limitPowerTransfer request. + */ + void notifyLimitPowerTransferStatus(in String portName, boolean limit, in Status retval, long transactionId); +} diff --git a/usb/aidl/android/hardware/usb/PortDataRole.aidl b/usb/aidl/android/hardware/usb/PortDataRole.aidl new file mode 100644 index 0000000000..a69f97719e --- /dev/null +++ b/usb/aidl/android/hardware/usb/PortDataRole.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 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.usb; + +@VintfStability +enum PortDataRole { + /** + * Indicates that the port does not have a data role. + * In case of DRP, the current data role of the port is only resolved + * when the type-c handshake happens. + */ + NONE = 0, + /** + * Indicates that the port is acting as a host for data. + */ + HOST = 1, + /** + * Indicated that the port is acting as a device for data. + */ + DEVICE = 2, +} diff --git a/usb/aidl/android/hardware/usb/PortMode.aidl b/usb/aidl/android/hardware/usb/PortMode.aidl new file mode 100644 index 0000000000..399f0ebaeb --- /dev/null +++ b/usb/aidl/android/hardware/usb/PortMode.aidl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 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.usb; + +import android.hardware.usb.PortMode; + +@VintfStability +enum PortMode { + /** + * Indicates that the port does not have a mode. + * In case of DRP, the current mode of the port is only resolved + * when the type-c handshake happens. + */ + NONE = 0, + /** + * Indicates that port can only act as device for data and sink for power. + */ + UFP = 1, + /** + * Indicates the port can only act as host for data and source for power. + */ + DFP = 2, + /** + * Indicates can either act as UFP or DFP at a given point of time. + */ + DRP = 3, + /* + * Indicates that the port supports Audio Accessory mode. + */ + AUDIO_ACCESSORY = 4, + /* + * Indicates that the port supports Debug Accessory mode. + */ + DEBUG_ACCESSORY = 5, +} diff --git a/usb/aidl/android/hardware/usb/PortPowerRole.aidl b/usb/aidl/android/hardware/usb/PortPowerRole.aidl new file mode 100644 index 0000000000..ae3dc47c45 --- /dev/null +++ b/usb/aidl/android/hardware/usb/PortPowerRole.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 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.usb; + +@VintfStability +enum PortPowerRole { + /** + * Indicates that the port does not have a power role. + * In case of DRP, the current power role of the port is only resolved + * when the type-c handshake happens. + */ + NONE = 0, + /** + * Indicates that the port is supplying power to the other port. + */ + SOURCE = 1, + /** + * Indicates that the port is sinking power from the other port. + */ + SINK = 2, +} diff --git a/usb/aidl/android/hardware/usb/PortRole.aidl b/usb/aidl/android/hardware/usb/PortRole.aidl new file mode 100644 index 0000000000..e0429c8b74 --- /dev/null +++ b/usb/aidl/android/hardware/usb/PortRole.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 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.usb; + +import android.hardware.usb.PortDataRole; +import android.hardware.usb.PortMode; +import android.hardware.usb.PortPowerRole; + +/** + * Used as a container to send port role information. + */ +@VintfStability +union PortRole { + PortPowerRole powerRole = PortPowerRole.NONE; + PortDataRole dataRole; + PortMode mode; +} diff --git a/usb/aidl/android/hardware/usb/PortStatus.aidl b/usb/aidl/android/hardware/usb/PortStatus.aidl new file mode 100644 index 0000000000..51bee71389 --- /dev/null +++ b/usb/aidl/android/hardware/usb/PortStatus.aidl @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 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.usb; + +import android.hardware.usb.ContaminantDetectionStatus; +import android.hardware.usb.ContaminantProtectionMode; +import android.hardware.usb.ContaminantProtectionStatus; +import android.hardware.usb.PortDataRole; +import android.hardware.usb.PortMode; +import android.hardware.usb.PortPowerRole; +import android.hardware.usb.PowerBrickStatus; +import android.hardware.usb.UsbDataStatus; + +@VintfStability +parcelable PortStatus { + /** + * Name of the port. + * Used as the port's id by the caller. + */ + String portName; + /** + * Data role of the port. + */ + PortDataRole currentDataRole = PortDataRole.NONE; + /** + * Power Role of thte port. + */ + PortPowerRole currentPowerRole = PortPowerRole.NONE; + /** + * Mode in which the port is connected. + * Can be UFP or DFP or AUDIO_ACCESSORY or + * DEBUG_ACCESSORY. + */ + PortMode currentMode = PortMode.NONE; + /** + * True indicates that the port's mode can + * be changed. False otherwise. + */ + boolean canChangeMode; + /** + * True indicates that the port's data role + * can be changed. False otherwise. + * For example, true if Type-C PD PD_SWAP + * is supported. + */ + boolean canChangeDataRole; + /** + * True indicates that the port's power role + * can be changed. False otherwise. + * For example, true if Type-C PD PR_SWAP + * is supported. + */ + boolean canChangePowerRole; + /** + * Identifies the type of the local port. + * + * UFP - Indicates that port can only act as device for + * data and sink for power. + * DFP - Indicates the port can only act as host for data + * and source for power. + * DRP - Indicates can either act as UFP or DFP at a + * given point of time. + * AUDIO_ACCESSORY - Indicates that the port supports + * Audio Accessory mode. + * DEBUG_ACCESSORY - Indicates that the port supports + * Debug Accessory mode. + */ + PortMode[] supportedModes; + /** + * Contaminant presence protection modes supported by the port. + */ + ContaminantProtectionMode[] supportedContaminantProtectionModes; + /** + * Client can enable/disable contaminant presence protection through + * enableContaminantPresenceProtection when true. + */ + boolean supportsEnableContaminantPresenceProtection; + /** + * Contaminant presence protection modes currently active for the port. + */ + ContaminantProtectionStatus contaminantProtectionStatus = ContaminantProtectionStatus.NONE; + /** + * Client can enable/disable contaminant presence detection through + * enableContaminantPresenceDetection when true. + */ + boolean supportsEnableContaminantPresenceDetection; + /** + * Current status of contaminant detection algorithm. + */ + ContaminantDetectionStatus contaminantDetectionStatus = ContaminantDetectionStatus.NOT_SUPPORTED; + /** + * UsbData status of the port. + * Lists reasons for USB data being disabled. + */ + UsbDataStatus[] usbDataStatus; + /** + * Denoted whether power transfer is limited in the port. + */ + boolean powerTransferLimited; + /** + * Denotes whether Power brick is connected. + */ + PowerBrickStatus powerBrickStatus; +} diff --git a/usb/aidl/android/hardware/usb/PowerBrickStatus.aidl b/usb/aidl/android/hardware/usb/PowerBrickStatus.aidl new file mode 100644 index 0000000000..620fb25922 --- /dev/null +++ b/usb/aidl/android/hardware/usb/PowerBrickStatus.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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.usb; + +@VintfStability +enum PowerBrickStatus { + /** + * Status not known. + */ + UNKNOWN = 0, + /** + * Port partner is power brick. + */ + CONNECTED = 1, + /** + * Port partner is not power brick. + */ + NOT_CONNECTED = 2, +} diff --git a/usb/aidl/android/hardware/usb/Status.aidl b/usb/aidl/android/hardware/usb/Status.aidl new file mode 100644 index 0000000000..468ba4a068 --- /dev/null +++ b/usb/aidl/android/hardware/usb/Status.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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.usb; + +@VintfStability +@Backing(type="int") +enum Status { + SUCCESS = 0, + /** + * error value when the HAL operation fails for reasons not listed here. + */ + ERROR = 1, + /** + * error value returned when input argument is invalid. + */ + INVALID_ARGUMENT = 2, + /** + * error value returned when role string is unrecognized. + */ + UNRECOGNIZED_ROLE = 3, + /** + * Error value returned when the operation is not supported. + */ + NOT_SUPPORTED = 4, +} diff --git a/usb/aidl/android/hardware/usb/UsbDataStatus.aidl b/usb/aidl/android/hardware/usb/UsbDataStatus.aidl new file mode 100644 index 0000000000..4b6a41a01e --- /dev/null +++ b/usb/aidl/android/hardware/usb/UsbDataStatus.aidl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 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.usb; + +@VintfStability +enum UsbDataStatus { + /** + * USB data status not known. + */ + UNKNOWN = 0, + /** + * USB data is enabled. + */ + ENABLED = 1, + /** + * USB data is disabled as the port is hot. + */ + DISABLED_OVERHEAT = 2, + /** + * USB data is disabled as port is contaminated. + */ + DISABLED_CONTAMINANT = 3, + /** + * USB data is disabled due to dock. + */ + DISABLED_DOCK = 4, + /** + * USB data is disabled by USB Service. + */ + DISABLED_FORCE = 5, + /** + * USB data disabled for debug. + */ + DISABLED_DEBUG = 6 +} diff --git a/usb/aidl/conversion.log b/usb/aidl/conversion.log new file mode 100644 index 0000000000..c09044689f --- /dev/null +++ b/usb/aidl/conversion.log @@ -0,0 +1,11 @@ +Notes relating to hidl2aidl conversion of android.hardware.usb@1.3 to android.hardware.usb (if any) follow: +Unhandled comments from android.hardware.usb@1.1::types follow. Consider using hidl-lint to locate these and fixup as many as possible. + // NOTE: suffix '_1_1' is for legacy ABI compatibility. It cannot be + // changed to 'PortMode' which the convention dictates. + // NOTE: suffix '_1_1' is for legacy ABI compatibility. It cannot be + // changed to 'PortStatus' which the convention dictates. + +An unknown named type was found in translation: android.hardware.usb@1.0::PortStatus +An unknown named type was found in translation: android.hardware.usb@1.0::PortStatus +An unknown named type was found in translation: android.hardware.usb@1.0::PortStatus +END OF LOG diff --git a/usb/aidl/default/Android.bp b/usb/aidl/default/Android.bp new file mode 100644 index 0000000000..da0cff23c3 --- /dev/null +++ b/usb/aidl/default/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2021 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. +// + +cc_binary { + name: "android.hardware.usb-service.example", + relative_install_path: "hw", + init_rc: ["android.hardware.usb-service.example.rc"], + vintf_fragments: ["android.hardware.usb-service.example.xml"], + vendor: true, + srcs: [ + "service.cpp", + "Usb.cpp", + ], + shared_libs: [ + "android.hardware.usb-V1-ndk", + "libbase", + "libbinder_ndk", + "libcutils", + "liblog", + "libutils", + ], +} diff --git a/usb/aidl/default/Usb.cpp b/usb/aidl/default/Usb.cpp new file mode 100644 index 0000000000..92b09a2b44 --- /dev/null +++ b/usb/aidl/default/Usb.cpp @@ -0,0 +1,727 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "android.hardware.usb.aidl-service" + +#include <aidl/android/hardware/usb/PortRole.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/strings.h> +#include <assert.h> +#include <dirent.h> +#include <pthread.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <chrono> +#include <regex> +#include <thread> +#include <unordered_map> + +#include <cutils/uevent.h> +#include <sys/epoll.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> + +#include "Usb.h" + +using android::base::GetProperty; +using android::base::Trim; + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { + +constexpr char kTypecPath[] = "/sys/class/typec/"; +constexpr char kDataRoleNode[] = "/data_role"; +constexpr char kPowerRoleNode[] = "/power_role"; + +// Set by the signal handler to destroy the thread +volatile bool destroyThread; + +void queryVersionHelper(android::hardware::usb::Usb *usb, + std::vector<PortStatus> *currentPortStatus); + +ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable, int64_t in_transactionId) { + std::vector<PortStatus> currentPortStatus; + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyEnableUsbDataStatus( + in_portName, true, in_enable ? Status::SUCCESS : Status::ERROR, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyEnableUsbDataStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + queryVersionHelper(this, ¤tPortStatus); + + return ScopedAStatus::ok(); +} + +ScopedAStatus Usb::enableUsbDataWhileDocked(const string& in_portName, int64_t in_transactionId) { + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyEnableUsbDataWhileDockedStatus( + in_portName, Status::NOT_SUPPORTED, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyEnableUsbDataWhileDockedStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + + return ScopedAStatus::ok(); +} + +Status queryMoistureDetectionStatus(std::vector<PortStatus> *currentPortStatus) { + string enabled, status, path, DetectedPath; + + for (int i = 0; i < currentPortStatus->size(); i++) { + (*currentPortStatus)[i].supportedContaminantProtectionModes + .push_back(ContaminantProtectionMode::NONE); + (*currentPortStatus)[i].contaminantProtectionStatus + = ContaminantProtectionStatus::NONE; + (*currentPortStatus)[i].contaminantDetectionStatus + = ContaminantDetectionStatus::NOT_SUPPORTED; + (*currentPortStatus)[i].supportsEnableContaminantPresenceDetection = false; + (*currentPortStatus)[i].supportsEnableContaminantPresenceProtection = false; + } + + return Status::SUCCESS; +} + +string appendRoleNodeHelper(const string &portName, PortRole::Tag tag) { + string node(kTypecPath + portName); + + switch (tag) { + case PortRole::dataRole: + return node + kDataRoleNode; + case PortRole::powerRole: + return node + kPowerRoleNode; + case PortRole::mode: + return node + "/port_type"; + default: + return ""; + } +} + +string convertRoletoString(PortRole role) { + if (role.getTag() == PortRole::powerRole) { + if (role.get<PortRole::powerRole>() == PortPowerRole::SOURCE) + return "source"; + else if (role.get<PortRole::powerRole>() == PortPowerRole::SINK) + return "sink"; + } else if (role.getTag() == PortRole::dataRole) { + if (role.get<PortRole::dataRole>() == PortDataRole::HOST) + return "host"; + if (role.get<PortRole::dataRole>() == PortDataRole::DEVICE) + return "device"; + } else if (role.getTag() == PortRole::mode) { + if (role.get<PortRole::mode>() == PortMode::UFP) + return "sink"; + if (role.get<PortRole::mode>() == PortMode::DFP) + return "source"; + } + return "none"; +} + +void extractRole(string *roleName) { + std::size_t first, last; + + first = roleName->find("["); + last = roleName->find("]"); + + if (first != string::npos && last != string::npos) { + *roleName = roleName->substr(first + 1, last - first - 1); + } +} + +void switchToDrp(const string &portName) { + string filename = appendRoleNodeHelper(string(portName.c_str()), PortRole::mode); + FILE *fp; + + if (filename != "") { + fp = fopen(filename.c_str(), "w"); + if (fp != NULL) { + int ret = fputs("dual", fp); + fclose(fp); + if (ret == EOF) + ALOGE("Fatal: Error while switching back to drp"); + } else { + ALOGE("Fatal: Cannot open file to switch back to drp"); + } + } else { + ALOGE("Fatal: invalid node type"); + } +} + +bool switchMode(const string &portName, const PortRole &in_role, struct Usb *usb) { + string filename = appendRoleNodeHelper(string(portName.c_str()), in_role.getTag()); + string written; + FILE *fp; + bool roleSwitch = false; + + if (filename == "") { + ALOGE("Fatal: invalid node type"); + return false; + } + + fp = fopen(filename.c_str(), "w"); + if (fp != NULL) { + // Hold the lock here to prevent loosing connected signals + // as once the file is written the partner added signal + // can arrive anytime. + pthread_mutex_lock(&usb->mPartnerLock); + usb->mPartnerUp = false; + int ret = fputs(convertRoletoString(in_role).c_str(), fp); + fclose(fp); + + if (ret != EOF) { + struct timespec to; + struct timespec now; + + wait_again: + clock_gettime(CLOCK_MONOTONIC, &now); + to.tv_sec = now.tv_sec + PORT_TYPE_TIMEOUT; + to.tv_nsec = now.tv_nsec; + + int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to); + // There are no uevent signals which implies role swap timed out. + if (err == ETIMEDOUT) { + ALOGI("uevents wait timedout"); + // Validity check. + } else if (!usb->mPartnerUp) { + goto wait_again; + // Role switch succeeded since usb->mPartnerUp is true. + } else { + roleSwitch = true; + } + } else { + ALOGI("Role switch failed while wrting to file"); + } + pthread_mutex_unlock(&usb->mPartnerLock); + } + + if (!roleSwitch) + switchToDrp(string(portName.c_str())); + + return roleSwitch; +} + +Usb::Usb() + : mLock(PTHREAD_MUTEX_INITIALIZER), + mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER), + mPartnerLock(PTHREAD_MUTEX_INITIALIZER), + mPartnerUp(false) +{ + pthread_condattr_t attr; + if (pthread_condattr_init(&attr)) { + ALOGE("pthread_condattr_init failed: %s", strerror(errno)); + abort(); + } + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) { + ALOGE("pthread_condattr_setclock failed: %s", strerror(errno)); + abort(); + } + if (pthread_cond_init(&mPartnerCV, &attr)) { + ALOGE("pthread_cond_init failed: %s", strerror(errno)); + abort(); + } + if (pthread_condattr_destroy(&attr)) { + ALOGE("pthread_condattr_destroy failed: %s", strerror(errno)); + abort(); + } +} + +ScopedAStatus Usb::switchRole(const string& in_portName, + const PortRole& in_role, int64_t in_transactionId) { + string filename = appendRoleNodeHelper(string(in_portName.c_str()), in_role.getTag()); + string written; + FILE *fp; + bool roleSwitch = false; + + if (filename == "") { + ALOGE("Fatal: invalid node type"); + return ScopedAStatus::ok(); + } + + pthread_mutex_lock(&mRoleSwitchLock); + + ALOGI("filename write: %s role:%s", filename.c_str(), convertRoletoString(in_role).c_str()); + + if (in_role.getTag() == PortRole::mode) { + roleSwitch = switchMode(in_portName, in_role, this); + } else { + fp = fopen(filename.c_str(), "w"); + if (fp != NULL) { + int ret = fputs(convertRoletoString(in_role).c_str(), fp); + fclose(fp); + if ((ret != EOF) && ReadFileToString(filename, &written)) { + written = Trim(written); + extractRole(&written); + ALOGI("written: %s", written.c_str()); + if (written == convertRoletoString(in_role)) { + roleSwitch = true; + } else { + ALOGE("Role switch failed"); + } + } else { + ALOGE("failed to update the new role"); + } + } else { + ALOGE("fopen failed"); + } + } + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyRoleSwitchStatus( + in_portName, in_role, roleSwitch ? Status::SUCCESS : Status::ERROR, in_transactionId); + if (!ret.isOk()) + ALOGE("RoleSwitchStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + pthread_mutex_unlock(&mRoleSwitchLock); + + return ScopedAStatus::ok(); +} + +ScopedAStatus Usb::limitPowerTransfer(const string& in_portName, bool /*in_limit*/, + int64_t in_transactionId) { + std::vector<PortStatus> currentPortStatus; + + pthread_mutex_lock(&mLock); + if (mCallback != NULL && in_transactionId >= 0) { + ScopedAStatus ret = mCallback->notifyLimitPowerTransferStatus( + in_portName, false, Status::NOT_SUPPORTED, in_transactionId); + if (!ret.isOk()) + ALOGE("limitPowerTransfer error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + + return ScopedAStatus::ok(); +} + +Status getAccessoryConnected(const string &portName, string *accessory) { + string filename = kTypecPath + portName + "-partner/accessory_mode"; + + if (!ReadFileToString(filename, accessory)) { + ALOGE("getAccessoryConnected: Failed to open filesystem node: %s", filename.c_str()); + return Status::ERROR; + } + *accessory = Trim(*accessory); + + return Status::SUCCESS; +} + +Status getCurrentRoleHelper(const string &portName, bool connected, PortRole *currentRole) { + string filename; + string roleName; + string accessory; + + // Mode + + if (currentRole->getTag() == PortRole::powerRole) { + filename = kTypecPath + portName + kPowerRoleNode; + currentRole->set<PortRole::powerRole>(PortPowerRole::NONE); + } else if (currentRole->getTag() == PortRole::dataRole) { + filename = kTypecPath + portName + kDataRoleNode; + currentRole->set<PortRole::dataRole>(PortDataRole::NONE); + } else if (currentRole->getTag() == PortRole::mode) { + filename = kTypecPath + portName + kDataRoleNode; + currentRole->set<PortRole::mode>(PortMode::NONE); + } else { + return Status::ERROR; + } + + if (!connected) + return Status::SUCCESS; + + if (currentRole->getTag() == PortRole::mode) { + if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) { + return Status::ERROR; + } + if (accessory == "analog_audio") { + currentRole->set<PortRole::mode>(PortMode::AUDIO_ACCESSORY); + return Status::SUCCESS; + } else if (accessory == "debug") { + currentRole->set<PortRole::mode>(PortMode::DEBUG_ACCESSORY); + return Status::SUCCESS; + } + } + + if (!ReadFileToString(filename, &roleName)) { + ALOGE("getCurrentRole: Failed to open filesystem node: %s", filename.c_str()); + return Status::ERROR; + } + + roleName = Trim(roleName); + extractRole(&roleName); + + if (roleName == "source") { + currentRole->set<PortRole::powerRole>(PortPowerRole::SOURCE); + } else if (roleName == "sink") { + currentRole->set<PortRole::powerRole>(PortPowerRole::SINK); + } else if (roleName == "host") { + if (currentRole->getTag() == PortRole::dataRole) + currentRole->set<PortRole::dataRole>(PortDataRole::HOST); + else + currentRole->set<PortRole::mode>(PortMode::DFP); + } else if (roleName == "device") { + if (currentRole->getTag() == PortRole::dataRole) + currentRole->set<PortRole::dataRole>(PortDataRole::DEVICE); + else + currentRole->set<PortRole::mode>(PortMode::UFP); + } else if (roleName != "none") { + /* case for none has already been addressed. + * so we check if the role isn't none. + */ + return Status::UNRECOGNIZED_ROLE; + } + + return Status::SUCCESS; +} + +Status getTypeCPortNamesHelper(std::unordered_map<string, bool> *names) { + DIR *dp; + + dp = opendir(kTypecPath); + if (dp != NULL) { + struct dirent *ep; + + while ((ep = readdir(dp))) { + if (ep->d_type == DT_LNK) { + if (string::npos == string(ep->d_name).find("-partner")) { + std::unordered_map<string, bool>::const_iterator portName = + names->find(ep->d_name); + if (portName == names->end()) { + names->insert({ep->d_name, false}); + } + } else { + (*names)[std::strtok(ep->d_name, "-")] = true; + } + } + } + closedir(dp); + return Status::SUCCESS; + } + + ALOGE("Failed to open /sys/class/typec"); + return Status::ERROR; +} + +bool canSwitchRoleHelper(const string &portName) { + string filename = kTypecPath + portName + "-partner/supports_usb_power_delivery"; + string supportsPD; + + if (ReadFileToString(filename, &supportsPD)) { + supportsPD = Trim(supportsPD); + if (supportsPD == "yes") { + return true; + } + } + + return false; +} + +Status getPortStatusHelper(std::vector<PortStatus> *currentPortStatus) { + std::unordered_map<string, bool> names; + Status result = getTypeCPortNamesHelper(&names); + int i = -1; + + if (result == Status::SUCCESS) { + currentPortStatus->resize(names.size()); + for (std::pair<string, bool> port : names) { + i++; + ALOGI("%s", port.first.c_str()); + (*currentPortStatus)[i].portName = port.first; + + PortRole currentRole; + currentRole.set<PortRole::powerRole>(PortPowerRole::NONE); + if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS){ + (*currentPortStatus)[i].currentPowerRole = currentRole.get<PortRole::powerRole>(); + } else { + ALOGE("Error while retrieving portNames"); + goto done; + } + + currentRole.set<PortRole::dataRole>(PortDataRole::NONE); + if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) { + (*currentPortStatus)[i].currentDataRole = currentRole.get<PortRole::dataRole>(); + } else { + ALOGE("Error while retrieving current port role"); + goto done; + } + + currentRole.set<PortRole::mode>(PortMode::NONE); + if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) { + (*currentPortStatus)[i].currentMode = currentRole.get<PortRole::mode>(); + } else { + ALOGE("Error while retrieving current data role"); + goto done; + } + + (*currentPortStatus)[i].canChangeMode = true; + (*currentPortStatus)[i].canChangeDataRole = + port.second ? canSwitchRoleHelper(port.first) : false; + (*currentPortStatus)[i].canChangePowerRole = + port.second ? canSwitchRoleHelper(port.first) : false; + + (*currentPortStatus)[i].supportedModes.push_back(PortMode::DRP); + (*currentPortStatus)[i].usbDataStatus.push_back(UsbDataStatus::ENABLED); + + ALOGI("%d:%s connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d " + "usbDataEnabled:%d", + i, port.first.c_str(), port.second, + (*currentPortStatus)[i].canChangeMode, + (*currentPortStatus)[i].canChangeDataRole, + (*currentPortStatus)[i].canChangePowerRole, 0); + } + + return Status::SUCCESS; + } +done: + return Status::ERROR; +} + +void queryVersionHelper(android::hardware::usb::Usb *usb, + std::vector<PortStatus> *currentPortStatus) { + Status status; + pthread_mutex_lock(&usb->mLock); + status = getPortStatusHelper(currentPortStatus); + queryMoistureDetectionStatus(currentPortStatus); + if (usb->mCallback != NULL) { + ScopedAStatus ret = usb->mCallback->notifyPortStatusChange(*currentPortStatus, + status); + if (!ret.isOk()) + ALOGE("queryPortStatus error %s", ret.getDescription().c_str()); + } else { + ALOGI("Notifying userspace skipped. Callback is NULL"); + } + pthread_mutex_unlock(&usb->mLock); +} + +ScopedAStatus Usb::queryPortStatus(int64_t in_transactionId) { + std::vector<PortStatus> currentPortStatus; + + queryVersionHelper(this, ¤tPortStatus); + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyQueryPortStatus( + "all", Status::SUCCESS, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyQueryPortStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + + return ScopedAStatus::ok(); +} + +ScopedAStatus Usb::enableContaminantPresenceDetection(const string& in_portName, + bool /*in_enable*/, int64_t in_transactionId) { + std::vector<PortStatus> currentPortStatus; + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyContaminantEnabledStatus( + in_portName, false, Status::ERROR, in_transactionId); + if (!ret.isOk()) + ALOGE("enableContaminantPresenceDetection error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + + queryVersionHelper(this, ¤tPortStatus); + return ScopedAStatus::ok(); +} + + +struct data { + int uevent_fd; + ::aidl::android::hardware::usb::Usb *usb; +}; + +static void uevent_event(uint32_t /*epevents*/, struct data *payload) { + char msg[UEVENT_MSG_LEN + 2]; + char *cp; + int n; + + n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN); + if (n <= 0) + return; + if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ + return; + + msg[n] = '\0'; + msg[n + 1] = '\0'; + cp = msg; + + while (*cp) { + if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) { + ALOGI("partner added"); + pthread_mutex_lock(&payload->usb->mPartnerLock); + payload->usb->mPartnerUp = true; + pthread_cond_signal(&payload->usb->mPartnerCV); + pthread_mutex_unlock(&payload->usb->mPartnerLock); + } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) { + std::vector<PortStatus> currentPortStatus; + queryVersionHelper(payload->usb, ¤tPortStatus); + + // Role switch is not in progress and port is in disconnected state + if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) { + for (unsigned long i = 0; i < currentPortStatus.size(); i++) { + DIR *dp = + opendir(string(kTypecPath + + string(currentPortStatus[i].portName.c_str()) + + "-partner").c_str()); + if (dp == NULL) { + switchToDrp(currentPortStatus[i].portName); + } else { + closedir(dp); + } + } + pthread_mutex_unlock(&payload->usb->mRoleSwitchLock); + } + break; + } /* advance to after the next \0 */ + while (*cp++) { + } + } +} + +void *work(void *param) { + int epoll_fd, uevent_fd; + struct epoll_event ev; + int nevents = 0; + struct data payload; + + uevent_fd = uevent_open_socket(UEVENT_MAX_EVENTS * UEVENT_MSG_LEN, true); + + if (uevent_fd < 0) { + ALOGE("uevent_init: uevent_open_socket failed\n"); + return NULL; + } + + payload.uevent_fd = uevent_fd; + payload.usb = (::aidl::android::hardware::usb::Usb *)param; + + fcntl(uevent_fd, F_SETFL, O_NONBLOCK); + + ev.events = EPOLLIN; + ev.data.ptr = (void *)uevent_event; + + epoll_fd = epoll_create(UEVENT_MAX_EVENTS); + if (epoll_fd == -1) { + ALOGE("epoll_create failed; errno=%d", errno); + goto error; + } + + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) { + ALOGE("epoll_ctl failed; errno=%d", errno); + goto error; + } + + while (!destroyThread) { + struct epoll_event events[UEVENT_MAX_EVENTS]; + + nevents = epoll_wait(epoll_fd, events, UEVENT_MAX_EVENTS, -1); + if (nevents == -1) { + if (errno == EINTR) + continue; + ALOGE("usb epoll_wait failed; errno=%d", errno); + break; + } + + for (int n = 0; n < nevents; ++n) { + if (events[n].data.ptr) + (*(void (*)(int, struct data *payload))events[n].data.ptr)(events[n].events, + &payload); + } + } + + ALOGI("exiting worker thread"); +error: + close(uevent_fd); + + if (epoll_fd >= 0) + close(epoll_fd); + + return NULL; +} + +void sighandler(int sig) { + if (sig == SIGUSR1) { + destroyThread = true; + ALOGI("destroy set"); + return; + } + signal(SIGUSR1, sighandler); +} + +ScopedAStatus Usb::setCallback( + const shared_ptr<IUsbCallback>& in_callback) { + + pthread_mutex_lock(&mLock); + if ((mCallback == NULL && in_callback == NULL) || + (mCallback != NULL && in_callback != NULL)) { + mCallback = in_callback; + pthread_mutex_unlock(&mLock); + return ScopedAStatus::ok(); + } + + mCallback = in_callback; + ALOGI("registering callback"); + + if (mCallback == NULL) { + if (!pthread_kill(mPoll, SIGUSR1)) { + pthread_join(mPoll, NULL); + ALOGI("pthread destroyed"); + } + pthread_mutex_unlock(&mLock); + return ScopedAStatus::ok(); + } + + destroyThread = false; + signal(SIGUSR1, sighandler); + + /* + * Create a background thread if the old callback value is NULL + * and being updated with a new value. + */ + if (pthread_create(&mPoll, NULL, work, this)) { + ALOGE("pthread creation failed %d", errno); + mCallback = NULL; + } + + pthread_mutex_unlock(&mLock); + return ScopedAStatus::ok(); +} + +} // namespace usb +} // namespace hardware +} // namespace android +} // aidl diff --git a/usb/aidl/default/Usb.h b/usb/aidl/default/Usb.h new file mode 100644 index 0000000000..7e8422e39b --- /dev/null +++ b/usb/aidl/default/Usb.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021 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 <android-base/file.h> +#include <aidl/android/hardware/usb/BnUsb.h> +#include <aidl/android/hardware/usb/BnUsbCallback.h> +#include <utils/Log.h> + +#define UEVENT_MSG_LEN 2048 +#define UEVENT_MAX_EVENTS 64 +// The type-c stack waits for 4.5 - 5.5 secs before declaring a port non-pd. +// The -partner directory would not be created until this is done. +// Having a margin of ~3 secs for the directory and other related bookeeping +// structures created and uvent fired. +#define PORT_TYPE_TIMEOUT 8 + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { + +using ::aidl::android::hardware::usb::IUsbCallback; +using ::aidl::android::hardware::usb::PortRole; +using ::android::base::ReadFileToString; +using ::android::base::WriteStringToFile; +using ::android::sp; +using ::ndk::ScopedAStatus; +using ::std::shared_ptr; +using ::std::string; + +struct Usb : public BnUsb { + Usb(); + + ScopedAStatus enableContaminantPresenceDetection(const std::string& in_portName, + bool in_enable, int64_t in_transactionId) override; + ScopedAStatus queryPortStatus(int64_t in_transactionId) override; + ScopedAStatus setCallback(const shared_ptr<IUsbCallback>& in_callback) override; + ScopedAStatus switchRole(const string& in_portName, const PortRole& in_role, + int64_t in_transactionId) override; + ScopedAStatus enableUsbData(const string& in_portName, bool in_enable, + int64_t in_transactionId) override; + ScopedAStatus enableUsbDataWhileDocked(const string& in_portName, + int64_t in_transactionId) override; + ScopedAStatus limitPowerTransfer(const std::string& in_portName, bool in_limit, + int64_t in_transactionId)override; + + shared_ptr<IUsbCallback> mCallback; + // Protects mCallback variable + pthread_mutex_t mLock; + // Protects roleSwitch operation + pthread_mutex_t mRoleSwitchLock; + // Threads waiting for the partner to come back wait here + pthread_cond_t mPartnerCV; + // lock protecting mPartnerCV + pthread_mutex_t mPartnerLock; + // Variable to signal partner coming back online after type switch + bool mPartnerUp; + private: + pthread_t mPoll; +}; + +} // namespace usb +} // namespace hardware +} // namespace android +} // aidl diff --git a/usb/aidl/default/android.hardware.usb-service.example.rc b/usb/aidl/default/android.hardware.usb-service.example.rc new file mode 100644 index 0000000000..335bca744d --- /dev/null +++ b/usb/aidl/default/android.hardware.usb-service.example.rc @@ -0,0 +1,4 @@ +service vendor.usb_default /vendor/bin/hw/android.hardware.usb-service.example + class hal + user system + group system diff --git a/usb/aidl/default/android.hardware.usb-service.example.xml b/usb/aidl/default/android.hardware.usb-service.example.xml new file mode 100644 index 0000000000..6088194890 --- /dev/null +++ b/usb/aidl/default/android.hardware.usb-service.example.xml @@ -0,0 +1,10 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.usb</name> + <version>1</version> + <interface> + <name>IUsb</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/usb/aidl/default/service.cpp b/usb/aidl/default/service.cpp new file mode 100644 index 0000000000..398458aff7 --- /dev/null +++ b/usb/aidl/default/service.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 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 <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +#include "Usb.h" + +using ::aidl::android::hardware::usb::Usb; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr<Usb> usb = ndk::SharedRefBase::make<Usb>(); + + const std::string instance = std::string() + Usb::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(usb->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return -1; // Should never be reached +} diff --git a/usb/aidl/vts/Android.bp b/usb/aidl/vts/Android.bp new file mode 100644 index 0000000000..00a7c93ec3 --- /dev/null +++ b/usb/aidl/vts/Android.bp @@ -0,0 +1,43 @@ +// +// Copyright (C) 2021 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"], +} + +cc_test { + name: "VtsAidlUsbTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: ["VtsAidlUsbTargetTest.cpp"], + shared_libs: [ + "libbinder_ndk", + ], + static_libs: [ + "android.hardware.usb-V1-ndk", + ], + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/usb/aidl/vts/VtsAidlUsbTargetTest.cpp b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp new file mode 100644 index 0000000000..ed3bd6e7db --- /dev/null +++ b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2021 The Android Open Source Probject + * + * 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. + */ + +#define LOG_TAG "UsbAidlTest" +#include <android-base/logging.h> + +#include <aidl/android/hardware/usb/IUsb.h> +#include <aidl/android/hardware/usb/IUsbCallback.h> +#include <aidl/android/hardware/usb/BnUsbCallback.h> +#include <aidl/android/hardware/usb/PortDataRole.h> +#include <aidl/android/hardware/usb/PortMode.h> +#include <aidl/android/hardware/usb/PortPowerRole.h> +#include <aidl/android/hardware/usb/PortRole.h> +#include <aidl/android/hardware/usb/PortStatus.h> +#include <aidl/android/hardware/usb/Status.h> +#include <aidl/Vintf.h> +#include <aidl/Gtest.h> + +#include <android/binder_auto_utils.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> +#include <gtest/gtest.h> + +#include <log/log.h> +#include <stdlib.h> +#include <chrono> +#include <condition_variable> +#include <mutex> + +#define TIMEOUT_PERIOD 10 + +using ::aidl::android::hardware::usb::BnUsbCallback; +using ::aidl::android::hardware::usb::IUsb; +using ::aidl::android::hardware::usb::IUsbCallback; +using ::aidl::android::hardware::usb::PortDataRole; +using ::aidl::android::hardware::usb::PortMode; +using ::aidl::android::hardware::usb::PortPowerRole; +using ::aidl::android::hardware::usb::PortRole; +using ::aidl::android::hardware::usb::PortStatus; +using ::aidl::android::hardware::usb::Status; + +using ::ndk::ScopedAStatus; +using ::ndk::SpAIBinder; +using std::vector; +using std::shared_ptr; +using std::string; + +// The main test class for the USB aidl hal +class UsbAidlTest : public testing::TestWithParam<std::string> { + public: + // Callback class for the USB aidl hal. + // Usb Hal will call this object upon role switch or port query. + class UsbCallback : public BnUsbCallback { + UsbAidlTest& parent_; + int cookie; + + public: + UsbCallback(UsbAidlTest& parent, int cookie) + : parent_(parent), cookie(cookie){}; + + virtual ~UsbCallback() = default; + + // Callback method for the port status. + ScopedAStatus notifyPortStatusChange(const vector<PortStatus>& currentPortStatus, + Status retval) override { + if (retval == Status::SUCCESS && currentPortStatus.size() > 0) { + parent_.usb_last_port_status.portName = + currentPortStatus[0].portName.c_str(); + parent_.usb_last_port_status.currentDataRole = + currentPortStatus[0].currentDataRole; + parent_.usb_last_port_status.currentPowerRole = + currentPortStatus[0].currentPowerRole; + parent_.usb_last_port_status.currentMode = + currentPortStatus[0].currentMode; + } + parent_.usb_last_cookie = cookie; + return ScopedAStatus::ok(); + } + + // Callback method for the status of role switch operation. + ScopedAStatus notifyRoleSwitchStatus(const string& /*portName*/, const PortRole& newRole, + Status retval, int64_t transactionId) override { + parent_.usb_last_status = retval; + parent_.usb_last_cookie = cookie; + parent_.usb_last_port_role = newRole; + parent_.usb_role_switch_done = true; + parent_.last_transactionId = transactionId; + parent_.notify(); + return ScopedAStatus::ok(); + } + + // Callback method for the status of enableUsbData operation + ScopedAStatus notifyEnableUsbDataStatus(const string& /*portName*/, bool /*enable*/, + Status /*retval*/, int64_t transactionId) override { + parent_.last_transactionId = transactionId; + parent_.usb_last_cookie = cookie; + parent_.enable_usb_data_done = true; + parent_.notify(); + return ScopedAStatus::ok(); + } + + // Callback method for the status of enableUsbData operation + ScopedAStatus notifyEnableUsbDataWhileDockedStatus(const string& /*portName*/, + Status /*retval*/, + int64_t transactionId) override { + parent_.last_transactionId = transactionId; + parent_.usb_last_cookie = cookie; + parent_.enable_usb_data_while_docked_done = true; + parent_.notify(); + return ScopedAStatus::ok(); + } + + // Callback method for the status of enableContaminantPresenceDetection + ScopedAStatus notifyContaminantEnabledStatus(const string& /*portName*/, bool /*enable*/, + Status /*retval*/, int64_t transactionId) override { + parent_.last_transactionId = transactionId; + parent_.usb_last_cookie = cookie; + parent_.enable_contaminant_done = true; + parent_.notify(); + return ScopedAStatus::ok(); + } + + // Callback method for the status of queryPortStatus operation + ScopedAStatus notifyQueryPortStatus(const string& /*portName*/, Status /*retval*/, + int64_t transactionId) override { + parent_.last_transactionId = transactionId; + parent_.notify(); + return ScopedAStatus::ok(); + } + + // Callback method for the status of limitPowerTransfer operation + ScopedAStatus notifyLimitPowerTransferStatus(const string& /*portName*/, bool /*limit*/, + Status /*retval*/, int64_t transactionId) override { + parent_.last_transactionId = transactionId; + parent_.usb_last_cookie = cookie; + parent_.limit_power_transfer_done = true; + parent_.notify(); + return ScopedAStatus::ok(); + } + }; + + virtual void SetUp() override { + ALOGI("Setup"); + usb = IUsb::fromBinder( + SpAIBinder(AServiceManager_waitForService(GetParam().c_str()))); + ASSERT_NE(usb, nullptr); + + usb_cb_2 = ::ndk::SharedRefBase::make<UsbCallback>(*this, 2); + ASSERT_NE(usb_cb_2, nullptr); + const auto& ret = usb->setCallback(usb_cb_2); + ASSERT_TRUE(ret.isOk()); + } + + virtual void TearDown() override { ALOGI("Teardown"); } + + // Used as a mechanism to inform the test about data/event callback. + inline void notify() { + std::unique_lock<std::mutex> lock(usb_mtx); + usb_count++; + usb_cv.notify_one(); + } + + // Test code calls this function to wait for data/event callback. + inline std::cv_status wait() { + std::unique_lock<std::mutex> lock(usb_mtx); + + std::cv_status status = std::cv_status::no_timeout; + auto now = std::chrono::system_clock::now(); + while (usb_count == 0) { + status = + usb_cv.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD)); + if (status == std::cv_status::timeout) { + ALOGI("timeout"); + return status; + } + } + usb_count--; + return status; + } + + // USB aidl hal Proxy + shared_ptr<IUsb> usb; + + // Callback objects for usb aidl + // Methods of these objects are called to notify port status updates. + shared_ptr<IUsbCallback> usb_cb_1, usb_cb_2; + + // The last conveyed status of the USB ports. + // Stores information of currentt_data_role, power_role for all the USB ports + PortStatus usb_last_port_status; + + // Status of the last role switch operation. + Status usb_last_status; + + // Port role information of the last role switch operation. + PortRole usb_last_port_role; + + // Flag to indicate the invocation of role switch callback. + bool usb_role_switch_done; + + // Flag to indicate the invocation of notifyContaminantEnabledStatus callback. + bool enable_contaminant_done; + + // Flag to indicate the invocation of notifyEnableUsbDataStatus callback. + bool enable_usb_data_done; + + // Flag to indicate the invocation of notifyEnableUsbDataWhileDockedStatus callback. + bool enable_usb_data_while_docked_done; + + // Flag to indicate the invocation of notifyLimitPowerTransferStatus callback. + bool limit_power_transfer_done; + + // Stores the cookie of the last invoked usb callback object. + int usb_last_cookie; + + // Last transaction ID that was recorded. + int64_t last_transactionId; + // synchronization primitives to coordinate between main test thread + // and the callback thread. + std::mutex usb_mtx; + std::condition_variable usb_cv; + int usb_count = 0; +}; + +/* + * Test to see if setCallback succeeds. + * Callback object is created and registered. + */ +TEST_P(UsbAidlTest, setCallback) { + ALOGI("UsbAidlTest setCallback start"); + usb_cb_1 = ::ndk::SharedRefBase::make<UsbCallback>(*this, 1); + ASSERT_NE(usb_cb_1, nullptr); + const auto& ret = usb->setCallback(usb_cb_1); + ASSERT_TRUE(ret.isOk()); + ALOGI("UsbAidlTest setCallback end"); +} + +/* + * Check to see if querying type-c + * port status succeeds. + * The callback parameters are checked to see if the transaction id + * matches. + */ +TEST_P(UsbAidlTest, queryPortStatus) { + ALOGI("UsbAidlTest queryPortStatus start"); + int64_t transactionId = rand() % 10000; + const auto& ret = usb->queryPortStatus(transactionId); + ASSERT_TRUE(ret.isOk()); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + ALOGI("UsbAidlTest queryPortStatus end: %s", usb_last_port_status.portName.c_str()); +} + +/* + * Trying to switch a non-existent port should fail. + * This test case tried to switch the port with empty + * name which is expected to fail. + * The callback parameters are checked to see if the transaction id + * matches. + */ +TEST_P(UsbAidlTest, switchEmptyPort) { + ALOGI("UsbAidlTest switchEmptyPort start"); + PortRole role; + role.set<PortRole::powerRole>(PortPowerRole::SOURCE); + int64_t transactionId = rand() % 10000; + const auto& ret = usb->switchRole("", role, transactionId); + ASSERT_TRUE(ret.isOk()); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(Status::ERROR, usb_last_status); + EXPECT_EQ(transactionId, last_transactionId); + EXPECT_EQ(2, usb_last_cookie); + ALOGI("UsbAidlTest switchEmptyPort end"); +} + +/* + * Test switching the power role of usb port. + * Test case queries the usb ports present in device. + * If there is at least one usb port, a power role switch + * to SOURCE is attempted for the port. + * The callback parameters are checked to see if the transaction id + * matches. + */ +TEST_P(UsbAidlTest, switchPowerRole) { + ALOGI("UsbAidlTest switchPowerRole start"); + PortRole role; + role.set<PortRole::powerRole>(PortPowerRole::SOURCE); + int64_t transactionId = rand() % 10000; + const auto& ret = usb->queryPortStatus(transactionId); + ASSERT_TRUE(ret.isOk()); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + + if (!usb_last_port_status.portName.empty()) { + string portBeingSwitched = usb_last_port_status.portName; + ALOGI("switchPower role portname:%s", portBeingSwitched.c_str()); + usb_role_switch_done = false; + transactionId = rand() % 10000; + const auto& ret = usb->switchRole(portBeingSwitched, role, transactionId); + ASSERT_TRUE(ret.isOk()); + + std::cv_status waitStatus = wait(); + while (waitStatus == std::cv_status::no_timeout && + usb_role_switch_done == false) + waitStatus = wait(); + + EXPECT_EQ(std::cv_status::no_timeout, waitStatus); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + } + ALOGI("UsbAidlTest switchPowerRole end"); +} + +/* + * Test switching the data role of usb port. + * Test case queries the usb ports present in device. + * If there is at least one usb port, a data role switch + * to device is attempted for the port. + * The callback parameters are checked to see if transaction id + * matches. + */ +TEST_P(UsbAidlTest, switchDataRole) { + ALOGI("UsbAidlTest switchDataRole start"); + PortRole role; + role.set<PortRole::dataRole>(PortDataRole::DEVICE); + int64_t transactionId = rand() % 10000; + const auto& ret = usb->queryPortStatus(transactionId); + ASSERT_TRUE(ret.isOk()); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + + if (!usb_last_port_status.portName.empty()) { + string portBeingSwitched = usb_last_port_status.portName; + ALOGI("portname:%s", portBeingSwitched.c_str()); + usb_role_switch_done = false; + transactionId = rand() % 10000; + const auto& ret = usb->switchRole(portBeingSwitched, role, transactionId); + ASSERT_TRUE(ret.isOk()); + + std::cv_status waitStatus = wait(); + while (waitStatus == std::cv_status::no_timeout && + usb_role_switch_done == false) + waitStatus = wait(); + + EXPECT_EQ(std::cv_status::no_timeout, waitStatus); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + } + ALOGI("UsbAidlTest switchDataRole end"); +} + +/* + * Test enabling contaminant presence detection of the port. + * Test case queries the usb ports present in device. + * If there is at least one usb port, enabling contaminant detection + * is attempted for the port. + * The callback parameters are checked to see if transaction id + * matches. + */ +TEST_P(UsbAidlTest, enableContaminantPresenceDetection) { + ALOGI("UsbAidlTest enableContaminantPresenceDetection start"); + int64_t transactionId = rand() % 10000; + const auto& ret = usb->queryPortStatus(transactionId); + ASSERT_TRUE(ret.isOk()); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + + if (!usb_last_port_status.portName.empty()) { + ALOGI("portname:%s", usb_last_port_status.portName.c_str()); + enable_contaminant_done = false; + transactionId = rand() % 10000; + const auto& ret = usb->enableContaminantPresenceDetection(usb_last_port_status.portName, + true, transactionId); + ASSERT_TRUE(ret.isOk()); + + std::cv_status waitStatus = wait(); + while (waitStatus == std::cv_status::no_timeout && + enable_contaminant_done == false) + waitStatus = wait(); + + EXPECT_EQ(std::cv_status::no_timeout, waitStatus); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + } + ALOGI("UsbAidlTest enableContaminantPresenceDetection end"); +} + +/* + * Test enabling Usb data of the port. + * Test case queries the usb ports present in device. + * If there is at least one usb port, enabling Usb data is attempted + * for the port. + * The callback parameters are checked to see if transaction id + * matches. + */ +TEST_P(UsbAidlTest, enableUsbData) { + ALOGI("UsbAidlTest enableUsbData start"); + int64_t transactionId = rand() % 10000; + const auto& ret = usb->queryPortStatus(transactionId); + ASSERT_TRUE(ret.isOk()); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + + if (!usb_last_port_status.portName.empty()) { + ALOGI("portname:%s", usb_last_port_status.portName.c_str()); + enable_usb_data_done = false; + transactionId = rand() % 10000; + const auto& ret = usb->enableUsbData(usb_last_port_status.portName, true, transactionId); + ASSERT_TRUE(ret.isOk()); + + std::cv_status waitStatus = wait(); + while (waitStatus == std::cv_status::no_timeout && + enable_usb_data_done == false) + waitStatus = wait(); + + EXPECT_EQ(std::cv_status::no_timeout, waitStatus); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + } + ALOGI("UsbAidlTest enableUsbData end"); +} + +/* + * Test enabling Usb data while being docked. + * Test case queries the usb ports present in device. + * If there is at least one usb port, enabling Usb data while docked + * is attempted for the port. + * The callback parameters are checked to see if transaction id + * matches. + */ +TEST_P(UsbAidlTest, enableUsbDataWhileDocked) { + ALOGI("UsbAidlTest enableUsbDataWhileDocked start"); + int64_t transactionId = rand() % 10000; + const auto& ret = usb->queryPortStatus(transactionId); + ASSERT_TRUE(ret.isOk()); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + + if (!usb_last_port_status.portName.empty()) { + ALOGI("portname:%s", usb_last_port_status.portName.c_str()); + enable_usb_data_while_docked_done = false; + transactionId = rand() % 10000; + const auto& ret = usb->enableUsbDataWhileDocked(usb_last_port_status.portName, transactionId); + ASSERT_TRUE(ret.isOk()); + + std::cv_status waitStatus = wait(); + while (waitStatus == std::cv_status::no_timeout && + enable_usb_data_while_docked_done == false) + waitStatus = wait(); + + EXPECT_EQ(std::cv_status::no_timeout, waitStatus); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + } + ALOGI("UsbAidlTest enableUsbDataWhileDocked end"); +} + +/* + * Test enabling Usb data of the port. + * Test case queries the usb ports present in device. + * If there is at least one usb port, relaxing limit power transfer + * is attempted for the port. + * The callback parameters are checked to see if transaction id + * matches. + */ +TEST_P(UsbAidlTest, limitPowerTransfer) { + ALOGI("UsbAidlTest limitPowerTransfer start"); + int64_t transactionId = rand() % 10000; + const auto& ret = usb->queryPortStatus(transactionId); + ASSERT_TRUE(ret.isOk()); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + + if (!usb_last_port_status.portName.empty()) { + ALOGI("portname:%s", usb_last_port_status.portName.c_str()); + limit_power_transfer_done = false; + transactionId = rand() % 10000; + const auto& ret = usb->limitPowerTransfer(usb_last_port_status.portName, false, transactionId); + ASSERT_TRUE(ret.isOk()); + + std::cv_status waitStatus = wait(); + while (waitStatus == std::cv_status::no_timeout && + limit_power_transfer_done == false) + waitStatus = wait(); + + EXPECT_EQ(std::cv_status::no_timeout, waitStatus); + EXPECT_EQ(2, usb_last_cookie); + EXPECT_EQ(transactionId, last_transactionId); + } + ALOGI("UsbAidlTest limitPowerTransfer end"); +} + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UsbAidlTest); +INSTANTIATE_TEST_SUITE_P( + PerInstance, UsbAidlTest, + testing::ValuesIn(::android::getAidlHalInstanceNames(IUsb::descriptor)), + ::android::PrintInstanceNameToString); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +} diff --git a/uwb/aidl/Android.bp b/uwb/aidl/Android.bp index 99fd094e67..2cc1e6a8f9 100755 --- a/uwb/aidl/Android.bp +++ b/uwb/aidl/Android.bp @@ -16,7 +16,7 @@ aidl_interface { stability: "vintf", backend: { java: { - sdk_version: "module_current", + sdk_version: "module_Tiramisu", enabled: false, }, ndk: { @@ -27,7 +27,7 @@ aidl_interface { "//apex_available:platform", "com.android.uwb", ], - min_sdk_version: "current", + min_sdk_version: "Tiramisu", }, rust: { enabled: true, @@ -35,7 +35,7 @@ aidl_interface { "//apex_available:platform", "com.android.uwb", ], - min_sdk_version: "current", + min_sdk_version: "Tiramisu", }, }, } @@ -47,7 +47,7 @@ aidl_interface { stability: "vintf", backend: { java: { - sdk_version: "module_current", + sdk_version: "module_Tiramisu", enabled: true, apex_available: [ "com.android.uwb", @@ -58,7 +58,7 @@ aidl_interface { "//apex_available:platform", "com.android.uwb", ], - min_sdk_version: "current", + min_sdk_version: "Tiramisu", }, }, } diff --git a/wifi/1.6/Android.bp b/wifi/1.6/Android.bp index d293c73a79..14cb2e0466 100644 --- a/wifi/1.6/Android.bp +++ b/wifi/1.6/Android.bp @@ -14,6 +14,13 @@ hidl_interface { root: "android.hardware", srcs: [ "IWifi.hal", + "IWifiChip.hal", + "IWifiNanIface.hal", + "IWifiNanIfaceEventCallback.hal", + "IWifiRttController.hal", + "IWifiRttControllerEventCallback.hal", + "IWifiStaIface.hal", + "types.hal", ], interfaces: [ "android.hardware.wifi@1.0", diff --git a/wifi/1.6/IWifiChip.hal b/wifi/1.6/IWifiChip.hal new file mode 100644 index 0000000000..301bd82ca5 --- /dev/null +++ b/wifi/1.6/IWifiChip.hal @@ -0,0 +1,90 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.wifi@1.6; + +import @1.0::IWifiIface; +import @1.0::WifiStatus; +import @1.5::WifiBand; +import @1.5::IWifiChip; +import @1.5::WifiIfaceMode; +import IWifiRttController; + +/** + * Interface that represents a chip that must be configured as a single unit. + */ +interface IWifiChip extends @1.5::IWifiChip { + + /** + * Create a RTTController instance. + * + * RTT controller can be either: + * a) Bound to a specific iface by passing in the corresponding |IWifiIface| + * object in |iface| param, OR + * b) Let the implementation decide the iface to use for RTT operations by + * passing null in |iface| param. + * + * @param boundIface HIDL interface object representing the iface if + * the responder must be bound to a specific iface, null otherwise. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_CHIP_INVALID| + */ + createRttController_1_6(IWifiIface boundIface) + generates (WifiStatus status, IWifiRttController rtt); + + /** + * Retrieve list of usable Wifi channels for the specified band & + * operational modes. + * + * The list of usable Wifi channels in a given band depends on factors + * like current country code, operational mode (e.g. STA, SAP, WFD-CLI, + * WFD-GO, TDLS, NAN) and other restrictons due to DFS, cellular coexistence + * and conncurency state of the device. + * + * @param band |WifiBand| for which list of usable channels is requested. + * @param ifaceModeMask Bitmask of the modes represented by |WifiIfaceMode| + * Bitmask respresents all the modes that the caller is interested + * in (e.g. STA, SAP, CLI, GO, TDLS, NAN). E.g. If the caller is + * interested in knowing usable channels for P2P CLI, P2P GO & NAN, + * ifaceModeMask would be set to + * IFACE_MODE_P2P_CLIENT|IFACE_MODE_P2P_GO|IFACE_MODE_NAN. + * @param filterMask Bitmask of filters represented by + * |UsableChannelFilter|. Specifies whether driver should filter + * channels based on additional criteria. If no filter is specified + * driver should return usable channels purely based on regulatory + * constraints. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_NOT_SUPPORTED|, + * |WifiStatusCode.ERROR_INVALID_ARGS|, + * |WifiStatusCode.FAILURE_UNKNOWN| + * @return channels List of channels represented by |WifiUsableChannel| + * Each entry represents a channel frequency, bandwidth and + * bitmask of modes (e.g. STA, SAP, CLI, GO, TDLS, NAN) that are + * allowed on that channel. E.g. If only STA mode can be supported + * on an indoor channel, only the IFACE_MODE_STA bit would be set + * for that channel. If 5GHz SAP cannot be supported, then none of + * the 5GHz channels will have IFACE_MODE_SOFTAP bit set. + * Note: Bits do not represent concurrency state. Each bit only + * represents whether particular mode is allowed on that channel. + */ + getUsableChannels_1_6(WifiBand band, bitfield<WifiIfaceMode> ifaceModeMask, + bitfield<UsableChannelFilter> filterMask) + generates (WifiStatus status, vec<WifiUsableChannel> channels); +}; diff --git a/wifi/1.6/IWifiNanIface.hal b/wifi/1.6/IWifiNanIface.hal new file mode 100644 index 0000000000..b92a880364 --- /dev/null +++ b/wifi/1.6/IWifiNanIface.hal @@ -0,0 +1,43 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.wifi@1.6; + +import @1.0::WifiStatus; +import @1.5::IWifiNanIface; +import IWifiNanIfaceEventCallback; + +/** + * Interface used to represent a single NAN (Neighbour Aware Network) iface. + * + * References to "NAN Spec" are to the Wi-Fi Alliance "Wi-Fi Neighbor Awareness + * Networking (NAN) Technical Specification". + */ +interface IWifiNanIface extends @1.5::IWifiNanIface { + /** + * Requests notifications of significant events on this iface. Multiple calls + * to this must register multiple callbacks each of which must receive all + * events. + * + * @param callback An instance of the |IWifiNanIfaceEventCallback| HIDL interface + * object. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID| + */ + registerEventCallback_1_6(IWifiNanIfaceEventCallback callback) generates (WifiStatus status); +}; diff --git a/wifi/1.6/IWifiNanIfaceEventCallback.hal b/wifi/1.6/IWifiNanIfaceEventCallback.hal new file mode 100644 index 0000000000..05b8ddfa52 --- /dev/null +++ b/wifi/1.6/IWifiNanIfaceEventCallback.hal @@ -0,0 +1,48 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.wifi@1.6; + +import @1.0::CommandIdShort; +import @1.0::WifiNanStatus; +import @1.5::IWifiNanIfaceEventCallback; + +/** + * NAN Response and Asynchronous Event Callbacks. + * + * References to "NAN Spec" are to the Wi-Fi Alliance "Wi-Fi Neighbor Awareness + * Networking (NAN) Technical Specification". + */ +interface IWifiNanIfaceEventCallback extends @1.5::IWifiNanIfaceEventCallback { + /** + * Asynchronous callback indicating a data-path (NDP) setup has been completed: received by + * both Initiator and Responder. + * + * Note: supersedes the @1.0::IWifiNanIfaceEventCallback.eventDataPathConfirm() method which is + * deprecated as of HAL version 1.2. + * + * @param event: NanDataPathConfirmInd containing event details. + */ + oneway eventDataPathConfirm_1_6(NanDataPathConfirmInd event); + + /** + * Asynchronous callback indicating a data-path (NDP) schedule has been updated (e.g. channels + * have been changed). + * + * @param event: NanDataPathScheduleUpdateInd containing event details. + */ + oneway eventDataPathScheduleUpdate_1_6(NanDataPathScheduleUpdateInd event); +}; diff --git a/wifi/1.6/IWifiRttController.hal b/wifi/1.6/IWifiRttController.hal new file mode 100644 index 0000000000..0db1d2c72b --- /dev/null +++ b/wifi/1.6/IWifiRttController.hal @@ -0,0 +1,100 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.wifi@1.6; + +import @1.0::CommandId; +import @1.0::WifiStatus; +import @1.4::IWifiRttController; +import IWifiRttControllerEventCallback; +/** + * Interface used to perform RTT(Round trip time) operations. + */ +interface IWifiRttController extends @1.4::IWifiRttController { + /** + * Requests notifications of significant events on this rtt controller. + * Multiple calls to this must register multiple callbacks each of which must + * receive all events. + * + * @param callback An instance of the |IWifiRttControllerEventCallback| HIDL + * interface object. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID| + */ + registerEventCallback_1_6(IWifiRttControllerEventCallback callback) + generates (WifiStatus status); + + /** + * API to request RTT measurement. + * + * @param cmdId command Id to use for this invocation. + * @param rttConfigs Vector of |RttConfig| parameters. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID|, + * |WifiStatusCode.ERROR_INVALID_ARGS|, + * |WifiStatusCode.ERROR_NOT_AVAILABLE|, + * |WifiStatusCode.ERROR_UNKNOWN| + */ + rangeRequest_1_6(CommandId cmdId, vec<RttConfig> rttConfigs) generates (WifiStatus status); + + /** + * Get RTT responder information e.g. WiFi channel to enable responder on. + * + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID|, + * |WifiStatusCode.ERROR_NOT_AVAILABLE|, + * |WifiStatusCode.ERROR_UNKNOWN| + * @return info Instance of |RttResponderInfo|. + */ + getResponderInfo_1_6() generates (WifiStatus status, RttResponder info); + + /** + * Enable RTT responder mode. + * + * @param cmdId command Id to use for this invocation. + * @parm channelHint Hint of the channel information where RTT responder must + * be enabled on. + * @param maxDurationInSeconds Timeout of responder mode. + * @param info Instance of |RttResponderInfo|. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID|, + * |WifiStatusCode.ERROR_INVALID_ARGS|, + * |WifiStatusCode.ERROR_NOT_AVAILABLE|, + * |WifiStatusCode.ERROR_UNKNOWN| + */ + enableResponder_1_6(CommandId cmdId, WifiChannelInfo channelHint, + uint32_t maxDurationInSeconds, RttResponder info) generates (WifiStatus status); + + /** + * RTT capabilities of the device. + * + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID|, + * |WifiStatusCode.ERROR_UNKNOWN| + * @return capabilities Instance of |RttCapabilities|. + */ + getCapabilities_1_6() generates (WifiStatus status, RttCapabilities capabilities); +}; diff --git a/wifi/1.6/IWifiRttControllerEventCallback.hal b/wifi/1.6/IWifiRttControllerEventCallback.hal new file mode 100644 index 0000000000..0857b66038 --- /dev/null +++ b/wifi/1.6/IWifiRttControllerEventCallback.hal @@ -0,0 +1,33 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.wifi@1.6; + +import @1.4::IWifiRttControllerEventCallback; +import @1.0::CommandId; + +/** + * RTT Response and Event Callbacks. + */ +interface IWifiRttControllerEventCallback extends @1.4::IWifiRttControllerEventCallback { + /* + * Invoked when an RTT result is available. + * + * @param cmdId command Id corresponding to the original request. + * @param results Vector of |RttResult| instances. + */ + oneway onResults_1_6(CommandId cmdId, vec<RttResult> results); +}; diff --git a/wifi/1.6/IWifiStaIface.hal b/wifi/1.6/IWifiStaIface.hal new file mode 100644 index 0000000000..c26e1a0981 --- /dev/null +++ b/wifi/1.6/IWifiStaIface.hal @@ -0,0 +1,44 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.wifi@1.6; + +import @1.0::WifiStatus; +import @1.5::IWifiStaIface; + +/** + * Interface used to represent a single STA iface. + * + * IWifiChip.createStaIface() must return a @1.6::IWifiStaIface when supported. + */ +interface IWifiStaIface extends @1.5::IWifiStaIface { + /** + * Retrieve the latest link layer stats. + * Must fail if |StaIfaceCapabilityMask.LINK_LAYER_STATS| is not set or if + * link layer stats collection hasn't been explicitly enabled. + * + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|, + * |WifiStatusCode.ERROR_NOT_SUPPORTED|, + * |WifiStatusCode.ERROR_NOT_STARTED|, + * |WifiStatusCode.ERROR_NOT_AVAILABLE|, + * |WifiStatusCode.ERROR_UNKNOWN| + * @return stats Instance of |LinkLayerStats|. + */ + getLinkLayerStats_1_6() generates (WifiStatus status, StaLinkLayerStats stats); +}; diff --git a/wifi/1.6/default/hidl_struct_util.cpp b/wifi/1.6/default/hidl_struct_util.cpp index 3489c9e4d3..2a6b13199b 100644 --- a/wifi/1.6/default/hidl_struct_util.cpp +++ b/wifi/1.6/default/hidl_struct_util.cpp @@ -438,7 +438,7 @@ uint32_t convertHidlUsableChannelFilterToLegacy(uint32_t hidl_filter_mask) { bool convertLegacyWifiUsableChannelToHidl( const legacy_hal::wifi_usable_channel& legacy_usable_channel, - V1_5::WifiUsableChannel* hidl_usable_channel) { + V1_6::WifiUsableChannel* hidl_usable_channel) { if (!hidl_usable_channel) { return false; } @@ -454,13 +454,13 @@ bool convertLegacyWifiUsableChannelToHidl( bool convertLegacyWifiUsableChannelsToHidl( const std::vector<legacy_hal::wifi_usable_channel>& legacy_usable_channels, - std::vector<V1_5::WifiUsableChannel>* hidl_usable_channels) { + std::vector<V1_6::WifiUsableChannel>* hidl_usable_channels) { if (!hidl_usable_channels) { return false; } *hidl_usable_channels = {}; for (const auto& legacy_usable_channel : legacy_usable_channels) { - V1_5::WifiUsableChannel hidl_usable_channel; + V1_6::WifiUsableChannel hidl_usable_channel; if (!convertLegacyWifiUsableChannelToHidl(legacy_usable_channel, &hidl_usable_channel)) { return false; } @@ -894,28 +894,28 @@ bool convertLegacyVectorOfDebugRxPacketFateToHidl( bool convertLegacyLinkLayerRadioStatsToHidl( const legacy_hal::LinkLayerRadioStats& legacy_radio_stat, - V1_5::StaLinkLayerRadioStats* hidl_radio_stat) { + V1_6::StaLinkLayerRadioStats* hidl_radio_stat) { if (!hidl_radio_stat) { return false; } *hidl_radio_stat = {}; hidl_radio_stat->radioId = legacy_radio_stat.stats.radio; - hidl_radio_stat->V1_3.V1_0.onTimeInMs = legacy_radio_stat.stats.on_time; - hidl_radio_stat->V1_3.V1_0.txTimeInMs = legacy_radio_stat.stats.tx_time; - hidl_radio_stat->V1_3.V1_0.rxTimeInMs = legacy_radio_stat.stats.rx_time; - hidl_radio_stat->V1_3.V1_0.onTimeInMsForScan = legacy_radio_stat.stats.on_time_scan; - hidl_radio_stat->V1_3.V1_0.txTimeInMsPerLevel = legacy_radio_stat.tx_time_per_levels; - hidl_radio_stat->V1_3.onTimeInMsForNanScan = legacy_radio_stat.stats.on_time_nbd; - hidl_radio_stat->V1_3.onTimeInMsForBgScan = legacy_radio_stat.stats.on_time_gscan; - hidl_radio_stat->V1_3.onTimeInMsForRoamScan = legacy_radio_stat.stats.on_time_roam_scan; - hidl_radio_stat->V1_3.onTimeInMsForPnoScan = legacy_radio_stat.stats.on_time_pno_scan; - hidl_radio_stat->V1_3.onTimeInMsForHs20Scan = legacy_radio_stat.stats.on_time_hs20; - - std::vector<V1_3::WifiChannelStats> hidl_channel_stats; + hidl_radio_stat->V1_0.onTimeInMs = legacy_radio_stat.stats.on_time; + hidl_radio_stat->V1_0.txTimeInMs = legacy_radio_stat.stats.tx_time; + hidl_radio_stat->V1_0.rxTimeInMs = legacy_radio_stat.stats.rx_time; + hidl_radio_stat->V1_0.onTimeInMsForScan = legacy_radio_stat.stats.on_time_scan; + hidl_radio_stat->V1_0.txTimeInMsPerLevel = legacy_radio_stat.tx_time_per_levels; + hidl_radio_stat->onTimeInMsForNanScan = legacy_radio_stat.stats.on_time_nbd; + hidl_radio_stat->onTimeInMsForBgScan = legacy_radio_stat.stats.on_time_gscan; + hidl_radio_stat->onTimeInMsForRoamScan = legacy_radio_stat.stats.on_time_roam_scan; + hidl_radio_stat->onTimeInMsForPnoScan = legacy_radio_stat.stats.on_time_pno_scan; + hidl_radio_stat->onTimeInMsForHs20Scan = legacy_radio_stat.stats.on_time_hs20; + + std::vector<V1_6::WifiChannelStats> hidl_channel_stats; for (const auto& channel_stat : legacy_radio_stat.channel_stats) { - V1_3::WifiChannelStats hidl_channel_stat; + V1_6::WifiChannelStats hidl_channel_stat; hidl_channel_stat.onTimeInMs = channel_stat.on_time; hidl_channel_stat.ccaBusyTimeInMs = channel_stat.cca_busy_time; /* @@ -929,13 +929,13 @@ bool convertLegacyLinkLayerRadioStatsToHidl( hidl_channel_stats.push_back(hidl_channel_stat); } - hidl_radio_stat->V1_3.channelStats = hidl_channel_stats; + hidl_radio_stat->channelStats = hidl_channel_stats; return true; } bool convertLegacyLinkLayerStatsToHidl(const legacy_hal::LinkLayerStats& legacy_stats, - V1_5::StaLinkLayerStats* hidl_stats) { + V1_6::StaLinkLayerStats* hidl_stats) { if (!hidl_stats) { return false; } @@ -1010,9 +1010,9 @@ bool convertLegacyLinkLayerStatsToHidl(const legacy_hal::LinkLayerStats& legacy_ hidl_stats->iface.timeSliceDutyCycleInPercent = legacy_stats.iface.info.time_slicing_duty_cycle_percent; // peer info legacy_stats conversion. - std::vector<V1_5::StaPeerInfo> hidl_peers_info_stats; + std::vector<V1_6::StaPeerInfo> hidl_peers_info_stats; for (const auto& legacy_peer_info_stats : legacy_stats.peers) { - V1_5::StaPeerInfo hidl_peer_info_stats; + V1_6::StaPeerInfo hidl_peer_info_stats; if (!convertLegacyPeerInfoStatsToHidl(legacy_peer_info_stats, &hidl_peer_info_stats)) { return false; } @@ -1020,9 +1020,9 @@ bool convertLegacyLinkLayerStatsToHidl(const legacy_hal::LinkLayerStats& legacy_ } hidl_stats->iface.peers = hidl_peers_info_stats; // radio legacy_stats conversion. - std::vector<V1_5::StaLinkLayerRadioStats> hidl_radios_stats; + std::vector<V1_6::StaLinkLayerRadioStats> hidl_radios_stats; for (const auto& legacy_radio_stats : legacy_stats.radios) { - V1_5::StaLinkLayerRadioStats hidl_radio_stats; + V1_6::StaLinkLayerRadioStats hidl_radio_stats; if (!convertLegacyLinkLayerRadioStatsToHidl(legacy_radio_stats, &hidl_radio_stats)) { return false; } @@ -1036,7 +1036,7 @@ bool convertLegacyLinkLayerStatsToHidl(const legacy_hal::LinkLayerStats& legacy_ } bool convertLegacyPeerInfoStatsToHidl(const legacy_hal::WifiPeerInfo& legacy_peer_info_stats, - V1_5::StaPeerInfo* hidl_peer_info_stats) { + V1_6::StaPeerInfo* hidl_peer_info_stats) { if (!hidl_peer_info_stats) { return false; } @@ -1044,9 +1044,9 @@ bool convertLegacyPeerInfoStatsToHidl(const legacy_hal::WifiPeerInfo& legacy_pee hidl_peer_info_stats->staCount = legacy_peer_info_stats.peer_info.bssload.sta_count; hidl_peer_info_stats->chanUtil = legacy_peer_info_stats.peer_info.bssload.chan_util; - std::vector<V1_5::StaRateStat> hidlRateStats; + std::vector<V1_6::StaRateStat> hidlRateStats; for (const auto& legacy_rate_stats : legacy_peer_info_stats.rate_stats) { - V1_5::StaRateStat rateStat; + V1_6::StaRateStat rateStat; if (!convertLegacyWifiRateInfoToHidl(legacy_rate_stats.rate, &rateStat.rateInfo)) { return false; } @@ -2134,7 +2134,7 @@ bool convertLegacyNanDataPathRequestIndToHidl(const legacy_hal::NanDataPathReque } bool convertLegacyNdpChannelInfoToHidl(const legacy_hal::NanChannelInfo& legacy_struct, - V1_2::NanDataPathChannelInfo* hidl_struct) { + V1_6::NanDataPathChannelInfo* hidl_struct) { if (!hidl_struct) { LOG(ERROR) << "convertLegacyNdpChannelInfoToHidl: hidl_struct is null"; return false; @@ -2150,7 +2150,7 @@ bool convertLegacyNdpChannelInfoToHidl(const legacy_hal::NanChannelInfo& legacy_ } bool convertLegacyNanDataPathConfirmIndToHidl(const legacy_hal::NanDataPathConfirmInd& legacy_ind, - V1_2::NanDataPathConfirmInd* hidl_ind) { + V1_6::NanDataPathConfirmInd* hidl_ind) { if (!hidl_ind) { LOG(ERROR) << "convertLegacyNanDataPathConfirmIndToHidl: hidl_ind is null"; return false; @@ -2166,9 +2166,9 @@ bool convertLegacyNanDataPathConfirmIndToHidl(const legacy_hal::NanDataPathConfi hidl_ind->V1_0.status.status = convertLegacyNanStatusTypeToHidl(legacy_ind.reason_code); hidl_ind->V1_0.status.description = ""; // TODO: b/34059183 - std::vector<V1_2::NanDataPathChannelInfo> channelInfo; + std::vector<V1_6::NanDataPathChannelInfo> channelInfo; for (unsigned int i = 0; i < legacy_ind.num_channels; ++i) { - V1_2::NanDataPathChannelInfo hidl_struct; + V1_6::NanDataPathChannelInfo hidl_struct; if (!convertLegacyNdpChannelInfoToHidl(legacy_ind.channel_info[i], &hidl_struct)) { return false; } @@ -2181,7 +2181,7 @@ bool convertLegacyNanDataPathConfirmIndToHidl(const legacy_hal::NanDataPathConfi bool convertLegacyNanDataPathScheduleUpdateIndToHidl( const legacy_hal::NanDataPathScheduleUpdateInd& legacy_ind, - V1_2::NanDataPathScheduleUpdateInd* hidl_ind) { + V1_6::NanDataPathScheduleUpdateInd* hidl_ind) { if (!hidl_ind) { LOG(ERROR) << "convertLegacyNanDataPathScheduleUpdateIndToHidl: " "hidl_ind is null"; @@ -2190,9 +2190,9 @@ bool convertLegacyNanDataPathScheduleUpdateIndToHidl( *hidl_ind = {}; hidl_ind->peerDiscoveryAddress = hidl_array<uint8_t, 6>(legacy_ind.peer_mac_addr); - std::vector<V1_2::NanDataPathChannelInfo> channelInfo; + std::vector<V1_6::NanDataPathChannelInfo> channelInfo; for (unsigned int i = 0; i < legacy_ind.num_channels; ++i) { - V1_2::NanDataPathChannelInfo hidl_struct; + V1_6::NanDataPathChannelInfo hidl_struct; if (!convertLegacyNdpChannelInfoToHidl(legacy_ind.channel_info[i], &hidl_struct)) { return false; } @@ -2260,13 +2260,16 @@ legacy_hal::wifi_channel_width convertHidlWifiChannelWidthToLegacy(WifiChannelWi return legacy_hal::WIFI_CHAN_WIDTH_5; case WifiChannelWidthInMhz::WIDTH_10: return legacy_hal::WIFI_CHAN_WIDTH_10; + case V1_6::WifiChannelWidthInMhz::WIDTH_320: + return legacy_hal::WIFI_CHAN_WIDTH_320; case WifiChannelWidthInMhz::WIDTH_INVALID: return legacy_hal::WIFI_CHAN_WIDTH_INVALID; }; CHECK(false); } -WifiChannelWidthInMhz convertLegacyWifiChannelWidthToHidl(legacy_hal::wifi_channel_width type) { +V1_6::WifiChannelWidthInMhz convertLegacyWifiChannelWidthToHidl( + legacy_hal::wifi_channel_width type) { switch (type) { case legacy_hal::WIFI_CHAN_WIDTH_20: return WifiChannelWidthInMhz::WIDTH_20; @@ -2282,35 +2285,41 @@ WifiChannelWidthInMhz convertLegacyWifiChannelWidthToHidl(legacy_hal::wifi_chann return WifiChannelWidthInMhz::WIDTH_5; case legacy_hal::WIFI_CHAN_WIDTH_10: return WifiChannelWidthInMhz::WIDTH_10; + case legacy_hal::WIFI_CHAN_WIDTH_320: + return V1_6::WifiChannelWidthInMhz::WIDTH_320; default: return WifiChannelWidthInMhz::WIDTH_INVALID; }; } -legacy_hal::wifi_rtt_preamble convertHidlRttPreambleToLegacy(V1_4::RttPreamble type) { +legacy_hal::wifi_rtt_preamble convertHidlRttPreambleToLegacy(V1_6::RttPreamble type) { switch (type) { - case V1_4::RttPreamble::LEGACY: + case V1_6::RttPreamble::LEGACY: return legacy_hal::WIFI_RTT_PREAMBLE_LEGACY; - case V1_4::RttPreamble::HT: + case V1_6::RttPreamble::HT: return legacy_hal::WIFI_RTT_PREAMBLE_HT; - case V1_4::RttPreamble::VHT: + case V1_6::RttPreamble::VHT: return legacy_hal::WIFI_RTT_PREAMBLE_VHT; - case V1_4::RttPreamble::HE: + case V1_6::RttPreamble::HE: return legacy_hal::WIFI_RTT_PREAMBLE_HE; + case V1_6::RttPreamble::EHT: + return legacy_hal::WIFI_RTT_PREAMBLE_EHT; }; CHECK(false); } -V1_4::RttPreamble convertLegacyRttPreambleToHidl(legacy_hal::wifi_rtt_preamble type) { +V1_6::RttPreamble convertLegacyRttPreambleToHidl(legacy_hal::wifi_rtt_preamble type) { switch (type) { case legacy_hal::WIFI_RTT_PREAMBLE_LEGACY: - return V1_4::RttPreamble::LEGACY; + return V1_6::RttPreamble::LEGACY; case legacy_hal::WIFI_RTT_PREAMBLE_HT: - return V1_4::RttPreamble::HT; + return V1_6::RttPreamble::HT; case legacy_hal::WIFI_RTT_PREAMBLE_VHT: - return V1_4::RttPreamble::VHT; + return V1_6::RttPreamble::VHT; case legacy_hal::WIFI_RTT_PREAMBLE_HE: - return V1_4::RttPreamble::HE; + return V1_6::RttPreamble::HE; + case legacy_hal::WIFI_RTT_PREAMBLE_EHT: + return V1_6::RttPreamble::EHT; }; CHECK(false) << "Unknown legacy type: " << type; } @@ -2329,6 +2338,8 @@ legacy_hal::wifi_rtt_bw convertHidlRttBwToLegacy(RttBw type) { return legacy_hal::WIFI_RTT_BW_80; case RttBw::BW_160MHZ: return legacy_hal::WIFI_RTT_BW_160; + case RttBw::BW_320MHZ: + return legacy_hal::WIFI_RTT_BW_320; }; CHECK(false); } @@ -2347,6 +2358,8 @@ RttBw convertLegacyRttBwToHidl(legacy_hal::wifi_rtt_bw type) { return RttBw::BW_80MHZ; case legacy_hal::WIFI_RTT_BW_160: return RttBw::BW_160MHZ; + case legacy_hal::WIFI_RTT_BW_320: + return RttBw::BW_320MHZ; }; CHECK(false) << "Unknown legacy type: " << type; } @@ -2363,20 +2376,22 @@ legacy_hal::wifi_motion_pattern convertHidlRttMotionPatternToLegacy(RttMotionPat CHECK(false); } -V1_4::WifiRatePreamble convertLegacyWifiRatePreambleToHidl(uint8_t preamble) { +V1_6::WifiRatePreamble convertLegacyWifiRatePreambleToHidl(uint8_t preamble) { switch (preamble) { case 0: - return V1_4::WifiRatePreamble::OFDM; + return V1_6::WifiRatePreamble::OFDM; case 1: - return V1_4::WifiRatePreamble::CCK; + return V1_6::WifiRatePreamble::CCK; case 2: - return V1_4::WifiRatePreamble::HT; + return V1_6::WifiRatePreamble::HT; case 3: - return V1_4::WifiRatePreamble::VHT; + return V1_6::WifiRatePreamble::VHT; case 4: - return V1_4::WifiRatePreamble::HE; + return V1_6::WifiRatePreamble::HE; + case 5: + return V1_6::WifiRatePreamble::EHT; default: - return V1_4::WifiRatePreamble::RESERVED; + return V1_6::WifiRatePreamble::RESERVED; }; CHECK(false) << "Unknown legacy preamble: " << preamble; } @@ -2464,7 +2479,7 @@ bool convertLegacyWifiChannelInfoToHidl(const legacy_hal::wifi_channel_info& leg return true; } -bool convertHidlRttConfigToLegacy(const V1_4::RttConfig& hidl_config, +bool convertHidlRttConfigToLegacy(const V1_6::RttConfig& hidl_config, legacy_hal::wifi_rtt_config* legacy_config) { if (!legacy_config) { return false; @@ -2491,7 +2506,7 @@ bool convertHidlRttConfigToLegacy(const V1_4::RttConfig& hidl_config, } bool convertHidlVectorOfRttConfigToLegacy( - const std::vector<V1_4::RttConfig>& hidl_configs, + const std::vector<V1_6::RttConfig>& hidl_configs, std::vector<legacy_hal::wifi_rtt_config>* legacy_configs) { if (!legacy_configs) { return false; @@ -2542,7 +2557,7 @@ bool convertHidlRttLcrInformationToLegacy(const RttLcrInformation& hidl_info, return true; } -bool convertHidlRttResponderToLegacy(const V1_4::RttResponder& hidl_responder, +bool convertHidlRttResponderToLegacy(const V1_6::RttResponder& hidl_responder, legacy_hal::wifi_rtt_responder* legacy_responder) { if (!legacy_responder) { return false; @@ -2556,7 +2571,7 @@ bool convertHidlRttResponderToLegacy(const V1_4::RttResponder& hidl_responder, } bool convertLegacyRttResponderToHidl(const legacy_hal::wifi_rtt_responder& legacy_responder, - V1_4::RttResponder* hidl_responder) { + V1_6::RttResponder* hidl_responder) { if (!hidl_responder) { return false; } @@ -2570,7 +2585,7 @@ bool convertLegacyRttResponderToHidl(const legacy_hal::wifi_rtt_responder& legac bool convertLegacyRttCapabilitiesToHidl( const legacy_hal::wifi_rtt_capabilities& legacy_capabilities, - V1_4::RttCapabilities* hidl_capabilities) { + V1_6::RttCapabilities* hidl_capabilities) { if (!hidl_capabilities) { return false; } @@ -2582,17 +2597,19 @@ bool convertLegacyRttCapabilitiesToHidl( hidl_capabilities->responderSupported = legacy_capabilities.responder_supported; hidl_capabilities->preambleSupport = 0; for (const auto flag : {legacy_hal::WIFI_RTT_PREAMBLE_LEGACY, legacy_hal::WIFI_RTT_PREAMBLE_HT, - legacy_hal::WIFI_RTT_PREAMBLE_VHT, legacy_hal::WIFI_RTT_PREAMBLE_HE}) { + legacy_hal::WIFI_RTT_PREAMBLE_VHT, legacy_hal::WIFI_RTT_PREAMBLE_HE, + legacy_hal::WIFI_RTT_PREAMBLE_EHT}) { if (legacy_capabilities.preamble_support & flag) { hidl_capabilities->preambleSupport |= - static_cast<std::underlying_type<V1_4::RttPreamble>::type>( + static_cast<std::underlying_type<V1_6::RttPreamble>::type>( convertLegacyRttPreambleToHidl(flag)); } } hidl_capabilities->bwSupport = 0; for (const auto flag : {legacy_hal::WIFI_RTT_BW_5, legacy_hal::WIFI_RTT_BW_10, legacy_hal::WIFI_RTT_BW_20, - legacy_hal::WIFI_RTT_BW_40, legacy_hal::WIFI_RTT_BW_80, legacy_hal::WIFI_RTT_BW_160}) { + legacy_hal::WIFI_RTT_BW_40, legacy_hal::WIFI_RTT_BW_80, legacy_hal::WIFI_RTT_BW_160, + legacy_hal::WIFI_RTT_BW_320}) { if (legacy_capabilities.bw_support & flag) { hidl_capabilities->bwSupport |= static_cast<std::underlying_type<RttBw>::type>(convertLegacyRttBwToHidl(flag)); @@ -2603,7 +2620,7 @@ bool convertLegacyRttCapabilitiesToHidl( } bool convertLegacyWifiRateInfoToHidl(const legacy_hal::wifi_rate& legacy_rate, - V1_4::WifiRateInfo* hidl_rate) { + V1_6::WifiRateInfo* hidl_rate) { if (!hidl_rate) { return false; } @@ -2618,7 +2635,7 @@ bool convertLegacyWifiRateInfoToHidl(const legacy_hal::wifi_rate& legacy_rate, } bool convertLegacyRttResultToHidl(const legacy_hal::wifi_rtt_result& legacy_result, - V1_4::RttResult* hidl_result) { + V1_6::RttResult* hidl_result) { if (!hidl_result) { return false; } @@ -2660,13 +2677,13 @@ bool convertLegacyRttResultToHidl(const legacy_hal::wifi_rtt_result& legacy_resu bool convertLegacyVectorOfRttResultToHidl( const std::vector<const legacy_hal::wifi_rtt_result*>& legacy_results, - std::vector<V1_4::RttResult>* hidl_results) { + std::vector<V1_6::RttResult>* hidl_results) { if (!hidl_results) { return false; } *hidl_results = {}; for (const auto legacy_result : legacy_results) { - V1_4::RttResult hidl_result; + V1_6::RttResult hidl_result; if (!convertLegacyRttResultToHidl(*legacy_result, &hidl_result)) { return false; } diff --git a/wifi/1.6/default/hidl_struct_util.h b/wifi/1.6/default/hidl_struct_util.h index 15a3205847..7f0266a478 100644 --- a/wifi/1.6/default/hidl_struct_util.h +++ b/wifi/1.6/default/hidl_struct_util.h @@ -25,8 +25,8 @@ #include <android/hardware/wifi/1.3/types.h> #include <android/hardware/wifi/1.4/IWifiChipEventCallback.h> #include <android/hardware/wifi/1.4/types.h> -#include <android/hardware/wifi/1.5/IWifiChip.h> -#include <android/hardware/wifi/1.5/types.h> +#include <android/hardware/wifi/1.6/IWifiChip.h> +#include <android/hardware/wifi/1.6/types.h> #include "wifi_legacy_hal.h" @@ -95,7 +95,7 @@ bool convertLegacyVectorOfCachedGscanResultsToHidl( const std::vector<legacy_hal::wifi_cached_scan_results>& legacy_cached_scan_results, std::vector<StaScanData>* hidl_scan_datas); bool convertLegacyLinkLayerStatsToHidl(const legacy_hal::LinkLayerStats& legacy_stats, - V1_5::StaLinkLayerStats* hidl_stats); + V1_6::StaLinkLayerStats* hidl_stats); bool convertLegacyRoamingCapabilitiesToHidl( const legacy_hal::wifi_roaming_capabilities& legacy_caps, StaRoamingCapabilities* hidl_caps); @@ -156,40 +156,40 @@ bool convertLegacyNanFollowupIndToHidl(const legacy_hal::NanFollowupInd& legacy_ bool convertLegacyNanDataPathRequestIndToHidl(const legacy_hal::NanDataPathRequestInd& legacy_ind, NanDataPathRequestInd* hidl_ind); bool convertLegacyNanDataPathConfirmIndToHidl(const legacy_hal::NanDataPathConfirmInd& legacy_ind, - V1_2::NanDataPathConfirmInd* hidl_ind); + V1_6::NanDataPathConfirmInd* hidl_ind); bool convertLegacyNanDataPathScheduleUpdateIndToHidl( const legacy_hal::NanDataPathScheduleUpdateInd& legacy_ind, - V1_2::NanDataPathScheduleUpdateInd* hidl_ind); + V1_6::NanDataPathScheduleUpdateInd* hidl_ind); // RTT controller conversion methods. -bool convertHidlVectorOfRttConfigToLegacy(const std::vector<V1_4::RttConfig>& hidl_configs, +bool convertHidlVectorOfRttConfigToLegacy(const std::vector<V1_6::RttConfig>& hidl_configs, std::vector<legacy_hal::wifi_rtt_config>* legacy_configs); bool convertHidlRttLciInformationToLegacy(const RttLciInformation& hidl_info, legacy_hal::wifi_lci_information* legacy_info); bool convertHidlRttLcrInformationToLegacy(const RttLcrInformation& hidl_info, legacy_hal::wifi_lcr_information* legacy_info); -bool convertHidlRttResponderToLegacy(const V1_4::RttResponder& hidl_responder, +bool convertHidlRttResponderToLegacy(const V1_6::RttResponder& hidl_responder, legacy_hal::wifi_rtt_responder* legacy_responder); -bool convertHidlWifiChannelInfoToLegacy(const WifiChannelInfo& hidl_info, +bool convertHidlWifiChannelInfoToLegacy(const V1_6::WifiChannelInfo& hidl_info, legacy_hal::wifi_channel_info* legacy_info); bool convertLegacyRttResponderToHidl(const legacy_hal::wifi_rtt_responder& legacy_responder, - V1_4::RttResponder* hidl_responder); + V1_6::RttResponder* hidl_responder); bool convertLegacyRttCapabilitiesToHidl( const legacy_hal::wifi_rtt_capabilities& legacy_capabilities, - V1_4::RttCapabilities* hidl_capabilities); + V1_6::RttCapabilities* hidl_capabilities); bool convertLegacyVectorOfRttResultToHidl( const std::vector<const legacy_hal::wifi_rtt_result*>& legacy_results, - std::vector<V1_4::RttResult>* hidl_results); + std::vector<V1_6::RttResult>* hidl_results); uint32_t convertHidlWifiBandToLegacyMacBand(V1_5::WifiBand band); uint32_t convertHidlWifiIfaceModeToLegacy(uint32_t hidl_iface_mask); uint32_t convertHidlUsableChannelFilterToLegacy(uint32_t hidl_filter_mask); bool convertLegacyWifiUsableChannelsToHidl( const std::vector<legacy_hal::wifi_usable_channel>& legacy_usable_channels, - std::vector<V1_5::WifiUsableChannel>* hidl_usable_channels); + std::vector<V1_6::WifiUsableChannel>* hidl_usable_channels); bool convertLegacyPeerInfoStatsToHidl(const legacy_hal::WifiPeerInfo& legacy_peer_info_stats, - V1_5::StaPeerInfo* hidl_peer_info_stats); + V1_6::StaPeerInfo* hidl_peer_info_stats); bool convertLegacyWifiRateInfoToHidl(const legacy_hal::wifi_rate& legacy_rate, - V1_4::WifiRateInfo* hidl_rate); + V1_6::WifiRateInfo* hidl_rate); } // namespace hidl_struct_util } // namespace implementation } // namespace V1_6 diff --git a/wifi/1.6/default/tests/hidl_struct_util_unit_tests.cpp b/wifi/1.6/default/tests/hidl_struct_util_unit_tests.cpp index 1182a58dbd..077c6cc8cd 100644 --- a/wifi/1.6/default/tests/hidl_struct_util_unit_tests.cpp +++ b/wifi/1.6/default/tests/hidl_struct_util_unit_tests.cpp @@ -37,7 +37,7 @@ namespace wifi { namespace V1_6 { namespace implementation { using namespace android::hardware::wifi::V1_0; -using ::android::hardware::wifi::V1_0::WifiChannelWidthInMhz; +using ::android::hardware::wifi::V1_6::WifiChannelWidthInMhz; class HidlStructUtilTest : public Test {}; @@ -216,7 +216,7 @@ TEST_F(HidlStructUtilTest, canConvertLegacyLinkLayerStatsToHidl) { peer.rate_stats.push_back(rate_stat2); } - V1_5::StaLinkLayerStats converted{}; + V1_6::StaLinkLayerStats converted{}; hidl_struct_util::convertLegacyLinkLayerStatsToHidl(legacy_stats, &converted); EXPECT_EQ(legacy_stats.iface.beacon_rx, converted.iface.V1_0.beaconRx); EXPECT_EQ(legacy_stats.iface.rssi_mgmt, converted.iface.V1_0.avgRssiMgmt); @@ -294,43 +294,42 @@ TEST_F(HidlStructUtilTest, canConvertLegacyLinkLayerStatsToHidl) { EXPECT_EQ(legacy_stats.radios.size(), converted.radios.size()); for (size_t i = 0; i < legacy_stats.radios.size(); i++) { EXPECT_EQ(legacy_stats.radios[i].stats.radio, converted.radios[i].radioId); - EXPECT_EQ(legacy_stats.radios[i].stats.on_time, converted.radios[i].V1_3.V1_0.onTimeInMs); - EXPECT_EQ(legacy_stats.radios[i].stats.tx_time, converted.radios[i].V1_3.V1_0.txTimeInMs); - EXPECT_EQ(legacy_stats.radios[i].stats.rx_time, converted.radios[i].V1_3.V1_0.rxTimeInMs); + EXPECT_EQ(legacy_stats.radios[i].stats.on_time, converted.radios[i].V1_0.onTimeInMs); + EXPECT_EQ(legacy_stats.radios[i].stats.tx_time, converted.radios[i].V1_0.txTimeInMs); + EXPECT_EQ(legacy_stats.radios[i].stats.rx_time, converted.radios[i].V1_0.rxTimeInMs); EXPECT_EQ(legacy_stats.radios[i].stats.on_time_scan, - converted.radios[i].V1_3.V1_0.onTimeInMsForScan); + converted.radios[i].V1_0.onTimeInMsForScan); EXPECT_EQ(legacy_stats.radios[i].tx_time_per_levels.size(), - converted.radios[i].V1_3.V1_0.txTimeInMsPerLevel.size()); + converted.radios[i].V1_0.txTimeInMsPerLevel.size()); for (size_t j = 0; j < legacy_stats.radios[i].tx_time_per_levels.size(); j++) { EXPECT_EQ(legacy_stats.radios[i].tx_time_per_levels[j], - converted.radios[i].V1_3.V1_0.txTimeInMsPerLevel[j]); + converted.radios[i].V1_0.txTimeInMsPerLevel[j]); } EXPECT_EQ(legacy_stats.radios[i].stats.on_time_nbd, - converted.radios[i].V1_3.onTimeInMsForNanScan); + converted.radios[i].onTimeInMsForNanScan); EXPECT_EQ(legacy_stats.radios[i].stats.on_time_gscan, - converted.radios[i].V1_3.onTimeInMsForBgScan); + converted.radios[i].onTimeInMsForBgScan); EXPECT_EQ(legacy_stats.radios[i].stats.on_time_roam_scan, - converted.radios[i].V1_3.onTimeInMsForRoamScan); + converted.radios[i].onTimeInMsForRoamScan); EXPECT_EQ(legacy_stats.radios[i].stats.on_time_pno_scan, - converted.radios[i].V1_3.onTimeInMsForPnoScan); + converted.radios[i].onTimeInMsForPnoScan); EXPECT_EQ(legacy_stats.radios[i].stats.on_time_hs20, - converted.radios[i].V1_3.onTimeInMsForHs20Scan); + converted.radios[i].onTimeInMsForHs20Scan); EXPECT_EQ(legacy_stats.radios[i].channel_stats.size(), - converted.radios[i].V1_3.channelStats.size()); + converted.radios[i].channelStats.size()); for (size_t k = 0; k < legacy_stats.radios[i].channel_stats.size(); k++) { auto& legacy_channel_st = legacy_stats.radios[i].channel_stats[k]; EXPECT_EQ(WifiChannelWidthInMhz::WIDTH_20, - converted.radios[i].V1_3.channelStats[k].channel.width); + converted.radios[i].channelStats[k].channel.width); EXPECT_EQ(WifiChannelInMhz(legacy_channel_st.channel.center_freq), - converted.radios[i].V1_3.channelStats[k].channel.centerFreq); + converted.radios[i].channelStats[k].channel.centerFreq); EXPECT_EQ(WifiChannelInMhz(legacy_channel_st.channel.center_freq0), - converted.radios[i].V1_3.channelStats[k].channel.centerFreq0); + converted.radios[i].channelStats[k].channel.centerFreq0); EXPECT_EQ(WifiChannelInMhz(legacy_channel_st.channel.center_freq1), - converted.radios[i].V1_3.channelStats[k].channel.centerFreq1); + converted.radios[i].channelStats[k].channel.centerFreq1); EXPECT_EQ(legacy_channel_st.cca_busy_time, - converted.radios[i].V1_3.channelStats[k].ccaBusyTimeInMs); - EXPECT_EQ(legacy_channel_st.on_time, - converted.radios[i].V1_3.channelStats[k].onTimeInMs); + converted.radios[i].channelStats[k].ccaBusyTimeInMs); + EXPECT_EQ(legacy_channel_st.on_time, converted.radios[i].channelStats[k].onTimeInMs); } } diff --git a/wifi/1.6/default/tests/wifi_chip_unit_tests.cpp b/wifi/1.6/default/tests/wifi_chip_unit_tests.cpp index 53904116cf..542b180482 100644 --- a/wifi/1.6/default/tests/wifi_chip_unit_tests.cpp +++ b/wifi/1.6/default/tests/wifi_chip_unit_tests.cpp @@ -238,7 +238,7 @@ class WifiChipTest : public Test { bool createRttController() { bool success = false; - chip_->createRttController_1_4( + chip_->createRttController_1_6( NULL, [&success](const WifiStatus& status, const sp<IWifiRttController>& rtt) { if (WifiStatusCode::SUCCESS == status.code) { ASSERT_NE(rtt.get(), nullptr); @@ -716,7 +716,7 @@ TEST_F(WifiChipV2_AwareIfaceCombinationTest, InvalidateAndRemoveRttControllerOnS // Create RTT controller sp<IWifiRttController> rtt_controller; - chip_->createRttController_1_4( + chip_->createRttController_1_6( NULL, [&rtt_controller](const WifiStatus& status, const sp<IWifiRttController>& rtt) { if (WifiStatusCode::SUCCESS == status.code) { ASSERT_NE(rtt.get(), nullptr); diff --git a/wifi/1.6/default/tests/wifi_nan_iface_unit_tests.cpp b/wifi/1.6/default/tests/wifi_nan_iface_unit_tests.cpp index c7c566bec1..13b2849209 100644 --- a/wifi/1.6/default/tests/wifi_nan_iface_unit_tests.cpp +++ b/wifi/1.6/default/tests/wifi_nan_iface_unit_tests.cpp @@ -85,8 +85,13 @@ class MockNanIfaceEventCallback : public V1_5::IWifiNanIfaceEventCallback { MOCK_METHOD1(eventDataPathConfirm, Return<void>(const android::hardware::wifi::V1_0::NanDataPathConfirmInd&)); MOCK_METHOD1(eventDataPathTerminated, Return<void>(uint32_t)); - MOCK_METHOD1(eventDataPathConfirm_1_2, Return<void>(const NanDataPathConfirmInd&)); - MOCK_METHOD1(eventDataPathScheduleUpdate, Return<void>(const NanDataPathScheduleUpdateInd&)); + MOCK_METHOD1(eventDataPathConfirm_1_2, + Return<void>(const android::hardware::wifi::V1_2::NanDataPathConfirmInd&)); + MOCK_METHOD1(eventDataPathConfirm_1_6, Return<void>(const NanDataPathConfirmInd&)); + MOCK_METHOD1(eventDataPathScheduleUpdate, + Return<void>(const android::hardware::wifi::V1_2::NanDataPathScheduleUpdateInd&)); + MOCK_METHOD1(eventDataPathScheduleUpdate_1_6, + Return<void>(const NanDataPathScheduleUpdateInd&)); MOCK_METHOD3(notifyCapabilitiesResponse_1_5, Return<void>(uint16_t, const WifiNanStatus&, const V1_5::NanCapabilities&)); }; diff --git a/wifi/1.6/default/wifi_chip.cpp b/wifi/1.6/default/wifi_chip.cpp index c1ce766a4f..11512f46e0 100644 --- a/wifi/1.6/default/wifi_chip.cpp +++ b/wifi/1.6/default/wifi_chip.cpp @@ -707,6 +707,21 @@ Return<void> WifiChip::triggerSubsystemRestart(triggerSubsystemRestart_cb hidl_s &WifiChip::triggerSubsystemRestartInternal, hidl_status_cb); } +Return<void> WifiChip::createRttController_1_6(const sp<IWifiIface>& bound_iface, + createRttController_1_6_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID, + &WifiChip::createRttControllerInternal_1_6, hidl_status_cb, bound_iface); +} + +Return<void> WifiChip::getUsableChannels_1_6( + WifiBand band, hidl_bitfield<V1_5::WifiIfaceMode> ifaceModeMask, + hidl_bitfield<V1_5::IWifiChip::UsableChannelFilter> filterMask, + getUsableChannels_1_6_cb _hidl_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID, + &WifiChip::getUsableChannelsInternal_1_6, _hidl_cb, band, ifaceModeMask, + filterMask); +} + void WifiChip::invalidateAndRemoveAllIfaces() { invalidateAndClearBridgedApAll(); invalidateAndClearAll(ap_ifaces_); @@ -1114,7 +1129,7 @@ WifiStatus WifiChip::removeP2pIfaceInternal(const std::string& ifname) { return createWifiStatus(WifiStatusCode::SUCCESS); } -std::pair<WifiStatus, sp<V1_5::IWifiStaIface>> WifiChip::createStaIfaceInternal() { +std::pair<WifiStatus, sp<V1_6::IWifiStaIface>> WifiChip::createStaIfaceInternal() { if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::STA)) { return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}}; } @@ -1144,7 +1159,7 @@ std::pair<WifiStatus, std::vector<hidl_string>> WifiChip::getStaIfaceNamesIntern return {createWifiStatus(WifiStatusCode::SUCCESS), getNames(sta_ifaces_)}; } -std::pair<WifiStatus, sp<V1_5::IWifiStaIface>> WifiChip::getStaIfaceInternal( +std::pair<WifiStatus, sp<V1_6::IWifiStaIface>> WifiChip::getStaIfaceInternal( const std::string& ifname) { const auto iface = findUsingName(sta_ifaces_, ifname); if (!iface.get()) { @@ -1351,16 +1366,9 @@ std::pair<WifiStatus, uint32_t> WifiChip::getCapabilitiesInternal_1_5() { } std::pair<WifiStatus, sp<V1_4::IWifiRttController>> WifiChip::createRttControllerInternal_1_4( - const sp<IWifiIface>& bound_iface) { - if (sta_ifaces_.size() == 0 && !canCurrentModeSupportIfaceOfType(IfaceType::STA)) { - LOG(ERROR) << "createRttControllerInternal_1_4: Chip cannot support STAs " - "(and RTT by extension)"; - return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}}; - } - sp<WifiRttController> rtt = - new WifiRttController(getFirstActiveWlanIfaceName(), bound_iface, legacy_hal_); - rtt_controllers_.emplace_back(rtt); - return {createWifiStatus(WifiStatusCode::SUCCESS), rtt}; + const sp<IWifiIface>& /*bound_iface*/) { + LOG(ERROR) << "createRttController_1_4 is not supported on this HAL"; + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}}; } WifiStatus WifiChip::registerEventCallbackInternal_1_4( @@ -1409,7 +1417,31 @@ WifiStatus WifiChip::setCountryCodeInternal(const std::array<int8_t, 2>& code) { return createWifiStatusFromLegacyError(legacy_status); } -std::pair<WifiStatus, std::vector<WifiUsableChannel>> WifiChip::getUsableChannelsInternal( +std::pair<WifiStatus, std::vector<V1_5::WifiUsableChannel>> WifiChip::getUsableChannelsInternal( + WifiBand /*band*/, uint32_t /*ifaceModeMask*/, uint32_t /*filterMask*/) { + LOG(ERROR) << "getUsableChannels is not supported on this HAL"; + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}}; +} + +WifiStatus WifiChip::triggerSubsystemRestartInternal() { + auto legacy_status = legacy_hal_.lock()->triggerSubsystemRestart(); + return createWifiStatusFromLegacyError(legacy_status); +} + +std::pair<WifiStatus, sp<V1_6::IWifiRttController>> WifiChip::createRttControllerInternal_1_6( + const sp<IWifiIface>& bound_iface) { + if (sta_ifaces_.size() == 0 && !canCurrentModeSupportIfaceOfType(IfaceType::STA)) { + LOG(ERROR) << "createRttControllerInternal_1_6: Chip cannot support STAs " + "(and RTT by extension)"; + return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}}; + } + sp<WifiRttController> rtt = + new WifiRttController(getFirstActiveWlanIfaceName(), bound_iface, legacy_hal_); + rtt_controllers_.emplace_back(rtt); + return {createWifiStatus(WifiStatusCode::SUCCESS), rtt}; +} + +std::pair<WifiStatus, std::vector<V1_6::WifiUsableChannel>> WifiChip::getUsableChannelsInternal_1_6( WifiBand band, uint32_t ifaceModeMask, uint32_t filterMask) { legacy_hal::wifi_error legacy_status; std::vector<legacy_hal::wifi_usable_channel> legacy_usable_channels; @@ -1421,7 +1453,7 @@ std::pair<WifiStatus, std::vector<WifiUsableChannel>> WifiChip::getUsableChannel if (legacy_status != legacy_hal::WIFI_SUCCESS) { return {createWifiStatusFromLegacyError(legacy_status), {}}; } - std::vector<WifiUsableChannel> hidl_usable_channels; + std::vector<V1_6::WifiUsableChannel> hidl_usable_channels; if (!hidl_struct_util::convertLegacyWifiUsableChannelsToHidl(legacy_usable_channels, &hidl_usable_channels)) { return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), {}}; @@ -1429,11 +1461,6 @@ std::pair<WifiStatus, std::vector<WifiUsableChannel>> WifiChip::getUsableChannel return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_usable_channels}; } -WifiStatus WifiChip::triggerSubsystemRestartInternal() { - auto legacy_status = legacy_hal_.lock()->triggerSubsystemRestart(); - return createWifiStatusFromLegacyError(legacy_status); -} - WifiStatus WifiChip::handleChipConfiguration( /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id) { // If the chip is already configured in a different mode, stop diff --git a/wifi/1.6/default/wifi_chip.h b/wifi/1.6/default/wifi_chip.h index 8a068985d1..73bdf3ada1 100644 --- a/wifi/1.6/default/wifi_chip.h +++ b/wifi/1.6/default/wifi_chip.h @@ -22,8 +22,9 @@ #include <mutex> #include <android-base/macros.h> -#include <android/hardware/wifi/1.4/IWifiRttController.h> -#include <android/hardware/wifi/1.5/IWifiChip.h> +#include <android/hardware/wifi/1.6/IWifiChip.h> +#include <android/hardware/wifi/1.6/IWifiRttController.h> +#include <android/hardware/wifi/1.6/IWifiStaIface.h> #include "hidl_callback_util.h" #include "ringbuffer.h" @@ -43,14 +44,13 @@ namespace V1_6 { namespace implementation { using namespace android::hardware::wifi::V1_0; using V1_5::WifiBand; -using V1_5::WifiUsableChannel; /** * HIDL interface object used to control a Wifi HAL chip instance. * Since there is only a single chip instance used today, there is no * identifying handle information stored here. */ -class WifiChip : public V1_5::IWifiChip { +class WifiChip : public V1_6::IWifiChip { public: WifiChip(ChipId chip_id, bool is_primary, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal, @@ -154,6 +154,12 @@ class WifiChip : public V1_5::IWifiChip { hidl_bitfield<UsableChannelFilter> filterMask, getUsableChannels_cb _hidl_cb) override; Return<void> triggerSubsystemRestart(triggerSubsystemRestart_cb hidl_status_cb) override; + Return<void> createRttController_1_6(const sp<IWifiIface>& bound_iface, + createRttController_1_6_cb hidl_status_cb) override; + Return<void> getUsableChannels_1_6(WifiBand band, + hidl_bitfield<V1_5::WifiIfaceMode> ifaceModeMask, + hidl_bitfield<UsableChannelFilter> filterMask, + getUsableChannels_1_6_cb _hidl_cb) override; private: void invalidateAndRemoveAllIfaces(); @@ -191,9 +197,9 @@ class WifiChip : public V1_5::IWifiChip { std::pair<WifiStatus, std::vector<hidl_string>> getP2pIfaceNamesInternal(); std::pair<WifiStatus, sp<IWifiP2pIface>> getP2pIfaceInternal(const std::string& ifname); WifiStatus removeP2pIfaceInternal(const std::string& ifname); - std::pair<WifiStatus, sp<V1_5::IWifiStaIface>> createStaIfaceInternal(); + std::pair<WifiStatus, sp<V1_6::IWifiStaIface>> createStaIfaceInternal(); std::pair<WifiStatus, std::vector<hidl_string>> getStaIfaceNamesInternal(); - std::pair<WifiStatus, sp<V1_5::IWifiStaIface>> getStaIfaceInternal(const std::string& ifname); + std::pair<WifiStatus, sp<V1_6::IWifiStaIface>> getStaIfaceInternal(const std::string& ifname); WifiStatus removeStaIfaceInternal(const std::string& ifname); std::pair<WifiStatus, sp<V1_0::IWifiRttController>> createRttControllerInternal( const sp<IWifiIface>& bound_iface); @@ -225,13 +231,12 @@ class WifiChip : public V1_5::IWifiChip { WifiStatus setCoexUnsafeChannelsInternal(std::vector<CoexUnsafeChannel> unsafe_channels, uint32_t restrictions); WifiStatus setCountryCodeInternal(const std::array<int8_t, 2>& code); - std::pair<WifiStatus, std::vector<WifiUsableChannel>> getUsableChannelsInternal( + std::pair<WifiStatus, std::vector<V1_5::WifiUsableChannel>> getUsableChannelsInternal( WifiBand band, uint32_t ifaceModeMask, uint32_t filterMask); WifiStatus handleChipConfiguration(std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id); WifiStatus registerDebugRingBufferCallback(); WifiStatus registerRadioModeChangeCallback(); - std::vector<V1_4::IWifiChip::ChipIfaceCombination> getCurrentModeIfaceCombinations(); std::map<IfaceType, size_t> getCurrentIfaceCombination(); std::vector<std::map<IfaceType, size_t>> expandIfaceCombinations( @@ -258,6 +263,10 @@ class WifiChip : public V1_5::IWifiChip { void invalidateAndClearBridgedAp(const std::string& br_name); bool findUsingNameFromBridgedApInstances(const std::string& name); WifiStatus triggerSubsystemRestartInternal(); + std::pair<WifiStatus, sp<V1_6::IWifiRttController>> createRttControllerInternal_1_6( + const sp<IWifiIface>& bound_iface); + std::pair<WifiStatus, std::vector<V1_6::WifiUsableChannel>> getUsableChannelsInternal_1_6( + WifiBand band, uint32_t ifaceModeMask, uint32_t filterMask); ChipId chip_id_; std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_; diff --git a/wifi/1.6/default/wifi_legacy_hal.h b/wifi/1.6/default/wifi_legacy_hal.h index d87242cdd5..7dc6bd643e 100644 --- a/wifi/1.6/default/wifi_legacy_hal.h +++ b/wifi/1.6/default/wifi_legacy_hal.h @@ -216,6 +216,7 @@ using ::wifi_cached_scan_results; using ::WIFI_CHAN_WIDTH_10; using ::WIFI_CHAN_WIDTH_160; using ::WIFI_CHAN_WIDTH_20; +using ::WIFI_CHAN_WIDTH_320; using ::WIFI_CHAN_WIDTH_40; using ::WIFI_CHAN_WIDTH_5; using ::WIFI_CHAN_WIDTH_80; @@ -289,12 +290,14 @@ using ::wifi_rtt_bw; using ::WIFI_RTT_BW_10; using ::WIFI_RTT_BW_160; using ::WIFI_RTT_BW_20; +using ::WIFI_RTT_BW_320; using ::WIFI_RTT_BW_40; using ::WIFI_RTT_BW_5; using ::WIFI_RTT_BW_80; using ::wifi_rtt_capabilities; using ::wifi_rtt_config; using ::wifi_rtt_preamble; +using ::WIFI_RTT_PREAMBLE_EHT; using ::WIFI_RTT_PREAMBLE_HE; using ::WIFI_RTT_PREAMBLE_HT; using ::WIFI_RTT_PREAMBLE_LEGACY; diff --git a/wifi/1.6/default/wifi_nan_iface.cpp b/wifi/1.6/default/wifi_nan_iface.cpp index 236cb64e32..1add6dce5c 100644 --- a/wifi/1.6/default/wifi_nan_iface.cpp +++ b/wifi/1.6/default/wifi_nan_iface.cpp @@ -378,15 +378,15 @@ WifiNanIface::WifiNanIface(const std::string& ifname, bool is_dedicated_iface, LOG(ERROR) << "Callback invoked on an invalid object"; return; } - V1_2::NanDataPathConfirmInd hidl_struct; + V1_6::NanDataPathConfirmInd hidl_struct; if (!hidl_struct_util::convertLegacyNanDataPathConfirmIndToHidl(msg, &hidl_struct)) { LOG(ERROR) << "Failed to convert nan capabilities response"; return; } - for (const auto& callback : shared_ptr_this->getEventCallbacks_1_2()) { - if (!callback->eventDataPathConfirm_1_2(hidl_struct).isOk()) { + for (const auto& callback : shared_ptr_this->getEventCallbacks_1_6()) { + if (!callback->eventDataPathConfirm_1_6(hidl_struct).isOk()) { LOG(ERROR) << "Failed to invoke the callback"; } } @@ -430,15 +430,15 @@ WifiNanIface::WifiNanIface(const std::string& ifname, bool is_dedicated_iface, LOG(ERROR) << "Callback invoked on an invalid object"; return; } - V1_2::NanDataPathScheduleUpdateInd hidl_struct; + V1_6::NanDataPathScheduleUpdateInd hidl_struct; if (!hidl_struct_util::convertLegacyNanDataPathScheduleUpdateIndToHidl( msg, &hidl_struct)) { LOG(ERROR) << "Failed to convert nan capabilities response"; return; } - for (const auto& callback : shared_ptr_this->getEventCallbacks_1_2()) { - if (!callback->eventDataPathScheduleUpdate(hidl_struct).isOk()) { + for (const auto& callback : shared_ptr_this->getEventCallbacks_1_6()) { + if (!callback->eventDataPathScheduleUpdate_1_6(hidl_struct).isOk()) { LOG(ERROR) << "Failed to invoke the callback"; } } @@ -510,6 +510,10 @@ std::set<sp<V1_5::IWifiNanIfaceEventCallback>> WifiNanIface::getEventCallbacks_1 return event_cb_handler_1_5_.getCallbacks(); } +std::set<sp<V1_6::IWifiNanIfaceEventCallback>> WifiNanIface::getEventCallbacks_1_6() { + return event_cb_handler_1_6_.getCallbacks(); +} + Return<void> WifiNanIface::getName(getName_cb hidl_status_cb) { return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID, &WifiNanIface::getNameInternal, hidl_status_cb); @@ -703,6 +707,14 @@ std::pair<WifiStatus, IfaceType> WifiNanIface::getTypeInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), IfaceType::NAN}; } +Return<void> WifiNanIface::registerEventCallback_1_6( + const sp<V1_6::IWifiNanIfaceEventCallback>& callback, + registerEventCallback_1_6_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID, + &WifiNanIface::registerEventCallback_1_6Internal, hidl_status_cb, + callback); +} + WifiStatus WifiNanIface::registerEventCallbackInternal( const sp<V1_0::IWifiNanIfaceEventCallback>& callback) { if (!event_cb_handler_.addCallback(callback)) { @@ -898,6 +910,25 @@ WifiStatus WifiNanIface::configRequest_1_5Internal(uint16_t cmd_id, return createWifiStatusFromLegacyError(legacy_status); } +WifiStatus WifiNanIface::registerEventCallback_1_6Internal( + const sp<V1_6::IWifiNanIfaceEventCallback>& callback) { + sp<V1_0::IWifiNanIfaceEventCallback> callback_1_0 = callback; + if (!event_cb_handler_.addCallback(callback_1_0)) { + return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN); + } + sp<V1_2::IWifiNanIfaceEventCallback> callback_1_2 = callback; + if (!event_cb_handler_1_2_.addCallback(callback_1_2)) { + return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN); + } + sp<V1_5::IWifiNanIfaceEventCallback> callback_1_5 = callback; + if (!event_cb_handler_1_5_.addCallback(callback_1_5)) { + return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN); + } + if (!event_cb_handler_1_6_.addCallback(callback)) { + return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN); + } + return createWifiStatus(WifiStatusCode::SUCCESS); +} } // namespace implementation } // namespace V1_6 } // namespace wifi diff --git a/wifi/1.6/default/wifi_nan_iface.h b/wifi/1.6/default/wifi_nan_iface.h index c445afc541..b732ef1371 100644 --- a/wifi/1.6/default/wifi_nan_iface.h +++ b/wifi/1.6/default/wifi_nan_iface.h @@ -18,8 +18,8 @@ #define WIFI_NAN_IFACE_H_ #include <android-base/macros.h> -#include <android/hardware/wifi/1.5/IWifiNanIface.h> -#include <android/hardware/wifi/1.5/IWifiNanIfaceEventCallback.h> +#include <android/hardware/wifi/1.6/IWifiNanIface.h> +#include <android/hardware/wifi/1.6/IWifiNanIfaceEventCallback.h> #include "hidl_callback_util.h" #include "wifi_iface_util.h" @@ -36,7 +36,7 @@ using namespace android::hardware::wifi::V1_2; /** * HIDL interface object used to control a NAN Iface instance. */ -class WifiNanIface : public V1_5::IWifiNanIface { +class WifiNanIface : public V1_6::IWifiNanIface { public: WifiNanIface(const std::string& ifname, bool is_dedicated_iface, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal, @@ -104,6 +104,8 @@ class WifiNanIface : public V1_5::IWifiNanIface { configRequest_1_4_cb hidl_status_cb) override; Return<void> getCapabilitiesRequest_1_5(uint16_t cmd_id, getCapabilitiesRequest_cb hidl_status_cb) override; + Return<void> registerEventCallback_1_6(const sp<V1_6::IWifiNanIfaceEventCallback>& callback, + registerEventCallback_1_6_cb hidl_status_cb) override; private: // Corresponding worker functions for the HIDL methods. @@ -145,6 +147,8 @@ class WifiNanIface : public V1_5::IWifiNanIface { WifiStatus configRequest_1_5Internal(uint16_t cmd_id, const V1_4::NanConfigRequest& msg, const V1_5::NanConfigRequestSupplemental& msg2); WifiStatus getCapabilitiesRequest_1_5Internal(uint16_t cmd_id); + WifiStatus registerEventCallback_1_6Internal( + const sp<V1_6::IWifiNanIfaceEventCallback>& callback); // all 1_0 and descendant callbacks std::set<sp<V1_0::IWifiNanIfaceEventCallback>> getEventCallbacks(); @@ -152,6 +156,8 @@ class WifiNanIface : public V1_5::IWifiNanIface { std::set<sp<V1_2::IWifiNanIfaceEventCallback>> getEventCallbacks_1_2(); // all 1_5 and descendant callbacks std::set<sp<V1_5::IWifiNanIfaceEventCallback>> getEventCallbacks_1_5(); + // all 1_6 and descendant callbacks + std::set<sp<V1_6::IWifiNanIfaceEventCallback>> getEventCallbacks_1_6(); std::string ifname_; bool is_dedicated_iface_; @@ -161,6 +167,7 @@ class WifiNanIface : public V1_5::IWifiNanIface { hidl_callback_util::HidlCallbackHandler<V1_0::IWifiNanIfaceEventCallback> event_cb_handler_; hidl_callback_util::HidlCallbackHandler<V1_2::IWifiNanIfaceEventCallback> event_cb_handler_1_2_; hidl_callback_util::HidlCallbackHandler<V1_5::IWifiNanIfaceEventCallback> event_cb_handler_1_5_; + hidl_callback_util::HidlCallbackHandler<V1_6::IWifiNanIfaceEventCallback> event_cb_handler_1_6_; DISALLOW_COPY_AND_ASSIGN(WifiNanIface); }; diff --git a/wifi/1.6/default/wifi_rtt_controller.cpp b/wifi/1.6/default/wifi_rtt_controller.cpp index f5e1d5aafc..b328f311ab 100644 --- a/wifi/1.6/default/wifi_rtt_controller.cpp +++ b/wifi/1.6/default/wifi_rtt_controller.cpp @@ -43,7 +43,7 @@ bool WifiRttController::isValid() { return is_valid_; } -std::vector<sp<V1_4::IWifiRttControllerEventCallback>> WifiRttController::getEventCallbacks() { +std::vector<sp<V1_6::IWifiRttControllerEventCallback>> WifiRttController::getEventCallbacks() { return event_callbacks_; } @@ -102,7 +102,7 @@ Return<void> WifiRttController::getResponderInfo(getResponderInfo_cb hidl_status } Return<void> WifiRttController::enableResponder(uint32_t cmd_id, - const WifiChannelInfo& channel_hint, + const V1_0::WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, const V1_0::RttResponder& info, enableResponder_cb hidl_status_cb) { @@ -144,7 +144,7 @@ Return<void> WifiRttController::getResponderInfo_1_4(getResponderInfo_1_4_cb hid } Return<void> WifiRttController::enableResponder_1_4(uint32_t cmd_id, - const WifiChannelInfo& channel_hint, + const V1_0::WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, const V1_4::RttResponder& info, enableResponder_1_4_cb hidl_status_cb) { @@ -153,6 +153,42 @@ Return<void> WifiRttController::enableResponder_1_4(uint32_t cmd_id, channel_hint, max_duration_seconds, info); } +Return<void> WifiRttController::registerEventCallback_1_6( + const sp<V1_6::IWifiRttControllerEventCallback>& callback, + registerEventCallback_1_6_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::registerEventCallbackInternal_1_6, hidl_status_cb, + callback); +} + +Return<void> WifiRttController::rangeRequest_1_6(uint32_t cmd_id, + const hidl_vec<V1_6::RttConfig>& rtt_configs, + rangeRequest_1_6_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::rangeRequestInternal_1_6, hidl_status_cb, cmd_id, + rtt_configs); +} + +Return<void> WifiRttController::getCapabilities_1_6(getCapabilities_1_6_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::getCapabilitiesInternal_1_6, hidl_status_cb); +} + +Return<void> WifiRttController::getResponderInfo_1_6(getResponderInfo_1_6_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::getResponderInfoInternal_1_6, hidl_status_cb); +} + +Return<void> WifiRttController::enableResponder_1_6(uint32_t cmd_id, + const V1_6::WifiChannelInfo& channel_hint, + uint32_t max_duration_seconds, + const V1_6::RttResponder& info, + enableResponder_1_6_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::enableResponderInternal_1_6, hidl_status_cb, cmd_id, + channel_hint, max_duration_seconds, info); +} + std::pair<WifiStatus, sp<IWifiIface>> WifiRttController::getBoundIfaceInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), bound_iface_}; } @@ -210,10 +246,9 @@ std::pair<WifiStatus, V1_0::RttResponder> WifiRttController::getResponderInfoInt return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}}; } -WifiStatus WifiRttController::enableResponderInternal(uint32_t /* cmd_id */, - const WifiChannelInfo& /* channel_hint */, - uint32_t /* max_duration_seconds */, - const V1_0::RttResponder& /* info */) { +WifiStatus WifiRttController::enableResponderInternal( + uint32_t /* cmd_id */, const V1_0::WifiChannelInfo& /* channel_hint */, + uint32_t /* max_duration_seconds */, const V1_0::RttResponder& /* info */) { // Deprecated support for this api return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED)}; } @@ -224,14 +259,43 @@ WifiStatus WifiRttController::disableResponderInternal(uint32_t cmd_id) { } WifiStatus WifiRttController::registerEventCallbackInternal_1_4( - const sp<V1_4::IWifiRttControllerEventCallback>& callback) { + const sp<V1_4::IWifiRttControllerEventCallback>& /* callback */) { + // Deprecated support for this api + return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED); +} + +WifiStatus WifiRttController::rangeRequestInternal_1_4( + uint32_t /* cmd_id */, const std::vector<V1_4::RttConfig>& /* rtt_configs */) { + // Deprecated support for this api + return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED); +} + +std::pair<WifiStatus, V1_4::RttCapabilities> WifiRttController::getCapabilitiesInternal_1_4() { + // Deprecated support for this api + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}}; +} + +std::pair<WifiStatus, V1_4::RttResponder> WifiRttController::getResponderInfoInternal_1_4() { + // Deprecated support for this api + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}}; +} + +WifiStatus WifiRttController::enableResponderInternal_1_4( + uint32_t /* cmd_id */, const V1_0::WifiChannelInfo& /* channel_hint */, + uint32_t /* max_duration_seconds */, const V1_4::RttResponder& /* info */) { + // Deprecated support for this api + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED)}; +} + +WifiStatus WifiRttController::registerEventCallbackInternal_1_6( + const sp<V1_6::IWifiRttControllerEventCallback>& callback) { // TODO(b/31632518): remove the callback when the client is destroyed event_callbacks_.emplace_back(callback); return createWifiStatus(WifiStatusCode::SUCCESS); } -WifiStatus WifiRttController::rangeRequestInternal_1_4( - uint32_t cmd_id, const std::vector<V1_4::RttConfig>& rtt_configs) { +WifiStatus WifiRttController::rangeRequestInternal_1_6( + uint32_t cmd_id, const std::vector<V1_6::RttConfig>& rtt_configs) { std::vector<legacy_hal::wifi_rtt_config> legacy_configs; if (!hidl_struct_util::convertHidlVectorOfRttConfigToLegacy(rtt_configs, &legacy_configs)) { return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS); @@ -245,14 +309,14 @@ WifiStatus WifiRttController::rangeRequestInternal_1_4( LOG(ERROR) << "Callback invoked on an invalid object"; return; } - std::vector<V1_4::RttResult> hidl_results; + std::vector<V1_6::RttResult> hidl_results; if (!hidl_struct_util::convertLegacyVectorOfRttResultToHidl(results, &hidl_results)) { LOG(ERROR) << "Failed to convert rtt results to HIDL structs"; return; } for (const auto& callback : shared_ptr_this->getEventCallbacks()) { - callback->onResults_1_4(id, hidl_results); + callback->onResults_1_6(id, hidl_results); } }; legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->startRttRangeRequest( @@ -260,38 +324,38 @@ WifiStatus WifiRttController::rangeRequestInternal_1_4( return createWifiStatusFromLegacyError(legacy_status); } -std::pair<WifiStatus, V1_4::RttCapabilities> WifiRttController::getCapabilitiesInternal_1_4() { +std::pair<WifiStatus, V1_6::RttCapabilities> WifiRttController::getCapabilitiesInternal_1_6() { legacy_hal::wifi_error legacy_status; legacy_hal::wifi_rtt_capabilities legacy_caps; std::tie(legacy_status, legacy_caps) = legacy_hal_.lock()->getRttCapabilities(ifname_); if (legacy_status != legacy_hal::WIFI_SUCCESS) { return {createWifiStatusFromLegacyError(legacy_status), {}}; } - V1_4::RttCapabilities hidl_caps; + V1_6::RttCapabilities hidl_caps; if (!hidl_struct_util::convertLegacyRttCapabilitiesToHidl(legacy_caps, &hidl_caps)) { return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), {}}; } return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_caps}; } -std::pair<WifiStatus, V1_4::RttResponder> WifiRttController::getResponderInfoInternal_1_4() { +std::pair<WifiStatus, V1_6::RttResponder> WifiRttController::getResponderInfoInternal_1_6() { legacy_hal::wifi_error legacy_status; legacy_hal::wifi_rtt_responder legacy_responder; std::tie(legacy_status, legacy_responder) = legacy_hal_.lock()->getRttResponderInfo(ifname_); if (legacy_status != legacy_hal::WIFI_SUCCESS) { return {createWifiStatusFromLegacyError(legacy_status), {}}; } - V1_4::RttResponder hidl_responder; + V1_6::RttResponder hidl_responder; if (!hidl_struct_util::convertLegacyRttResponderToHidl(legacy_responder, &hidl_responder)) { return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), {}}; } return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_responder}; } -WifiStatus WifiRttController::enableResponderInternal_1_4(uint32_t cmd_id, - const WifiChannelInfo& channel_hint, +WifiStatus WifiRttController::enableResponderInternal_1_6(uint32_t cmd_id, + const V1_6::WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, - const V1_4::RttResponder& info) { + const V1_6::RttResponder& info) { legacy_hal::wifi_channel_info legacy_channel_info; if (!hidl_struct_util::convertHidlWifiChannelInfoToLegacy(channel_hint, &legacy_channel_info)) { return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS); diff --git a/wifi/1.6/default/wifi_rtt_controller.h b/wifi/1.6/default/wifi_rtt_controller.h index b4a2116f2b..fd5f68b6a8 100644 --- a/wifi/1.6/default/wifi_rtt_controller.h +++ b/wifi/1.6/default/wifi_rtt_controller.h @@ -19,8 +19,8 @@ #include <android-base/macros.h> #include <android/hardware/wifi/1.0/IWifiIface.h> -#include <android/hardware/wifi/1.4/IWifiRttController.h> -#include <android/hardware/wifi/1.4/IWifiRttControllerEventCallback.h> +#include <android/hardware/wifi/1.6/IWifiRttController.h> +#include <android/hardware/wifi/1.6/IWifiRttControllerEventCallback.h> #include "wifi_legacy_hal.h" @@ -33,14 +33,14 @@ namespace implementation { /** * HIDL interface object used to control all RTT operations. */ -class WifiRttController : public V1_4::IWifiRttController { +class WifiRttController : public V1_6::IWifiRttController { public: WifiRttController(const std::string& iface_name, const sp<IWifiIface>& bound_iface, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal); // Refer to |WifiChip::invalidate()|. void invalidate(); bool isValid(); - std::vector<sp<V1_4::IWifiRttControllerEventCallback>> getEventCallbacks(); + std::vector<sp<V1_6::IWifiRttControllerEventCallback>> getEventCallbacks(); std::string getIfaceName(); // HIDL methods exposed. @@ -57,7 +57,7 @@ class WifiRttController : public V1_4::IWifiRttController { Return<void> setLcr(uint32_t cmd_id, const RttLcrInformation& lcr, setLcr_cb hidl_status_cb) override; Return<void> getResponderInfo(getResponderInfo_cb hidl_status_cb) override; - Return<void> enableResponder(uint32_t cmd_id, const WifiChannelInfo& channel_hint, + Return<void> enableResponder(uint32_t cmd_id, const V1_0::WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, const V1_0::RttResponder& info, enableResponder_cb hidl_status_cb) override; Return<void> disableResponder(uint32_t cmd_id, disableResponder_cb hidl_status_cb) override; @@ -68,9 +68,19 @@ class WifiRttController : public V1_4::IWifiRttController { rangeRequest_1_4_cb hidl_status_cb) override; Return<void> getCapabilities_1_4(getCapabilities_1_4_cb hidl_status_cb) override; Return<void> getResponderInfo_1_4(getResponderInfo_1_4_cb hidl_status_cb) override; - Return<void> enableResponder_1_4(uint32_t cmd_id, const WifiChannelInfo& channel_hint, + Return<void> enableResponder_1_4(uint32_t cmd_id, const V1_0::WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, const V1_4::RttResponder& info, enableResponder_1_4_cb hidl_status_cb) override; + Return<void> registerEventCallback_1_6( + const sp<V1_6::IWifiRttControllerEventCallback>& callback, + registerEventCallback_1_6_cb hidl_status_cb) override; + Return<void> rangeRequest_1_6(uint32_t cmd_id, const hidl_vec<V1_6::RttConfig>& rtt_configs, + rangeRequest_1_6_cb hidl_status_cb) override; + Return<void> getCapabilities_1_6(getCapabilities_1_6_cb hidl_status_cb) override; + Return<void> getResponderInfo_1_6(getResponderInfo_1_6_cb hidl_status_cb) override; + Return<void> enableResponder_1_6(uint32_t cmd_id, const V1_6::WifiChannelInfo& channel_hint, + uint32_t max_duration_seconds, const V1_6::RttResponder& info, + enableResponder_1_6_cb hidl_status_cb) override; private: // Corresponding worker functions for the HIDL methods. @@ -85,7 +95,7 @@ class WifiRttController : public V1_4::IWifiRttController { WifiStatus setLciInternal(uint32_t cmd_id, const RttLciInformation& lci); WifiStatus setLcrInternal(uint32_t cmd_id, const RttLcrInformation& lcr); std::pair<WifiStatus, V1_0::RttResponder> getResponderInfoInternal(); - WifiStatus enableResponderInternal(uint32_t cmd_id, const WifiChannelInfo& channel_hint, + WifiStatus enableResponderInternal(uint32_t cmd_id, const V1_0::WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, const V1_0::RttResponder& info); WifiStatus disableResponderInternal(uint32_t cmd_id); @@ -95,14 +105,25 @@ class WifiRttController : public V1_4::IWifiRttController { const std::vector<V1_4::RttConfig>& rtt_configs); std::pair<WifiStatus, V1_4::RttCapabilities> getCapabilitiesInternal_1_4(); std::pair<WifiStatus, V1_4::RttResponder> getResponderInfoInternal_1_4(); - WifiStatus enableResponderInternal_1_4(uint32_t cmd_id, const WifiChannelInfo& channel_hint, + WifiStatus enableResponderInternal_1_4(uint32_t cmd_id, + const V1_0::WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, const V1_4::RttResponder& info); + WifiStatus registerEventCallbackInternal_1_6( + const sp<V1_6::IWifiRttControllerEventCallback>& callback); + WifiStatus rangeRequestInternal_1_6(uint32_t cmd_id, + const std::vector<V1_6::RttConfig>& rtt_configs); + std::pair<WifiStatus, V1_6::RttCapabilities> getCapabilitiesInternal_1_6(); + std::pair<WifiStatus, V1_6::RttResponder> getResponderInfoInternal_1_6(); + WifiStatus enableResponderInternal_1_6(uint32_t cmd_id, + const V1_6::WifiChannelInfo& channel_hint, + uint32_t max_duration_seconds, + const V1_6::RttResponder& info); std::string ifname_; sp<IWifiIface> bound_iface_; std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_; - std::vector<sp<V1_4::IWifiRttControllerEventCallback>> event_callbacks_; + std::vector<sp<V1_6::IWifiRttControllerEventCallback>> event_callbacks_; bool is_valid_; DISALLOW_COPY_AND_ASSIGN(WifiRttController); diff --git a/wifi/1.6/default/wifi_sta_iface.cpp b/wifi/1.6/default/wifi_sta_iface.cpp index f852d36689..dd11839c97 100644 --- a/wifi/1.6/default/wifi_sta_iface.cpp +++ b/wifi/1.6/default/wifi_sta_iface.cpp @@ -150,6 +150,11 @@ Return<void> WifiStaIface::getLinkLayerStats_1_5(getLinkLayerStats_1_5_cb hidl_s &WifiStaIface::getLinkLayerStatsInternal_1_5, hidl_status_cb); } +Return<void> WifiStaIface::getLinkLayerStats_1_6(getLinkLayerStats_1_6_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID, + &WifiStaIface::getLinkLayerStatsInternal_1_6, hidl_status_cb); +} + Return<void> WifiStaIface::startRssiMonitoring(uint32_t cmd_id, int32_t max_rssi, int32_t min_rssi, startRssiMonitoring_cb hidl_status_cb) { return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID, @@ -422,13 +427,17 @@ std::pair<WifiStatus, V1_3::StaLinkLayerStats> WifiStaIface::getLinkLayerStatsIn } std::pair<WifiStatus, V1_5::StaLinkLayerStats> WifiStaIface::getLinkLayerStatsInternal_1_5() { + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}}; +} + +std::pair<WifiStatus, V1_6::StaLinkLayerStats> WifiStaIface::getLinkLayerStatsInternal_1_6() { legacy_hal::wifi_error legacy_status; legacy_hal::LinkLayerStats legacy_stats; std::tie(legacy_status, legacy_stats) = legacy_hal_.lock()->getLinkLayerStats(ifname_); if (legacy_status != legacy_hal::WIFI_SUCCESS) { return {createWifiStatusFromLegacyError(legacy_status), {}}; } - V1_5::StaLinkLayerStats hidl_stats; + V1_6::StaLinkLayerStats hidl_stats; if (!hidl_struct_util::convertLegacyLinkLayerStatsToHidl(legacy_stats, &hidl_stats)) { return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), {}}; } diff --git a/wifi/1.6/default/wifi_sta_iface.h b/wifi/1.6/default/wifi_sta_iface.h index 37358a5fb0..c01c50b612 100644 --- a/wifi/1.6/default/wifi_sta_iface.h +++ b/wifi/1.6/default/wifi_sta_iface.h @@ -19,7 +19,7 @@ #include <android-base/macros.h> #include <android/hardware/wifi/1.0/IWifiStaIfaceEventCallback.h> -#include <android/hardware/wifi/1.5/IWifiStaIface.h> +#include <android/hardware/wifi/1.6/IWifiStaIface.h> #include "hidl_callback_util.h" #include "wifi_iface_util.h" @@ -35,7 +35,7 @@ using namespace android::hardware::wifi::V1_0; /** * HIDL interface object used to control a STA Iface instance. */ -class WifiStaIface : public V1_5::IWifiStaIface { +class WifiStaIface : public V1_6::IWifiStaIface { public: WifiStaIface(const std::string& ifname, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal, @@ -71,6 +71,7 @@ class WifiStaIface : public V1_5::IWifiStaIface { Return<void> getLinkLayerStats(getLinkLayerStats_cb hidl_status_cb) override; Return<void> getLinkLayerStats_1_3(getLinkLayerStats_1_3_cb hidl_status_cb) override; Return<void> getLinkLayerStats_1_5(getLinkLayerStats_1_5_cb hidl_status_cb) override; + Return<void> getLinkLayerStats_1_6(getLinkLayerStats_1_6_cb hidl_status_cb) override; Return<void> startRssiMonitoring(uint32_t cmd_id, int32_t max_rssi, int32_t min_rssi, startRssiMonitoring_cb hidl_status_cb) override; Return<void> stopRssiMonitoring(uint32_t cmd_id, stopRssiMonitoring_cb hidl_status_cb) override; @@ -116,6 +117,7 @@ class WifiStaIface : public V1_5::IWifiStaIface { std::pair<WifiStatus, V1_0::StaLinkLayerStats> getLinkLayerStatsInternal(); std::pair<WifiStatus, V1_3::StaLinkLayerStats> getLinkLayerStatsInternal_1_3(); std::pair<WifiStatus, V1_5::StaLinkLayerStats> getLinkLayerStatsInternal_1_5(); + std::pair<WifiStatus, V1_6::StaLinkLayerStats> getLinkLayerStatsInternal_1_6(); WifiStatus startRssiMonitoringInternal(uint32_t cmd_id, int32_t max_rssi, int32_t min_rssi); WifiStatus stopRssiMonitoringInternal(uint32_t cmd_id); std::pair<WifiStatus, StaRoamingCapabilities> getRoamingCapabilitiesInternal(); diff --git a/wifi/1.6/types.hal b/wifi/1.6/types.hal new file mode 100644 index 0000000000..f1d9d458ee --- /dev/null +++ b/wifi/1.6/types.hal @@ -0,0 +1,670 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.wifi@1.6; + +import @1.0::MacAddress; +import @1.0::NanDataPathConfirmInd; +import @1.0::Rssi; +import @1.0::RttBw; +import @1.0::RttPeerType; +import @1.0::RttStatus; +import @1.0::RttType; +import @1.0::StaLinkLayerIfaceStats; +import @1.0::StaLinkLayerRadioStats; +import @1.0::TimeSpanInPs; +import @1.0::TimeStampInUs; +import @1.0::TimeStampInMs; +import @1.0::WifiChannelInMhz; +import @1.0::WifiChannelWidthInMhz; +import @1.0::WifiInformationElement; +import @1.0::WifiRateNss; +import @1.4::RttPreamble; +import @1.4::WifiRatePreamble; +import @1.5::StaLinkLayerIfaceContentionTimeStats; +import @1.5::WifiIfaceMode; + +/** + * Channel operating width in Mhz. + */ +enum WifiChannelWidthInMhz : @1.0::WifiChannelWidthInMhz { + /** + * 320 MHz + */ + WIDTH_320 = 7, +}; + +/** + * RTT Measurement Bandwidth. + */ +enum RttBw : @1.0::RttBw { + BW_320MHZ = 0x40, +}; + +/** + * RTT Measurement Preamble. + */ +enum RttPreamble : @1.4::RttPreamble { + /** + * Preamble type for 11be + */ + EHT = 0x10, +}; + +/** + * Wifi Rate Preamble + */ +enum WifiRatePreamble : @1.4::WifiRatePreamble { + /** + * Preamble type for 11be + */ + EHT = 6, +}; + +/** + * Channel information. + */ +struct WifiChannelInfo { + /** + * Channel width (20, 40, 80, 80+80, 160, 320). + */ + WifiChannelWidthInMhz width; + /** + * Primary 20 MHz channel. + */ + WifiChannelInMhz centerFreq; + /** + * Center frequency (MHz) first segment. + */ + WifiChannelInMhz centerFreq0; + /** + * Center frequency (MHz) second segment. + */ + WifiChannelInMhz centerFreq1; +}; + +/** + * RTT configuration. + */ +struct RttConfig { + /** + * Peer device mac address. + */ + MacAddress addr; + + /** + * 1-sided or 2-sided RTT. + */ + RttType type; + + /** + * Optional - peer device hint (STA, P2P, AP). + */ + RttPeerType peer; + + /** + * Required for STA-AP mode, optional for P2P, NBD etc. + */ + WifiChannelInfo channel; + + /** + * Time interval between bursts (units: 100 ms). + * Applies to 1-sided and 2-sided RTT multi-burst requests. + * Range: 0-31, 0: no preference by initiator (2-sided RTT). + */ + uint32_t burstPeriod; + + /** + * Total number of RTT bursts to be executed. It will be + * specified in the same way as the parameter "Number of + * Burst Exponent" found in the FTM frame format. It + * applies to both: 1-sided RTT and 2-sided RTT. Valid + * values are 0 to 15 as defined in 802.11mc std. + * 0 means single shot + * The implication of this parameter on the maximum + * number of RTT results is the following: + * for 1-sided RTT: max num of RTT results = (2^num_burst)*(num_frames_per_burst) + * for 2-sided RTT: max num of RTT results = (2^num_burst)*(num_frames_per_burst - 1) + */ + uint32_t numBurst; + + /** + * Num of frames per burst. + * Minimum value = 1, Maximum value = 31 + * For 2-sided this equals the number of FTM frames + * to be attempted in a single burst. This also + * equals the number of FTM frames that the + * initiator will request that the responder send + * in a single frame. + */ + uint32_t numFramesPerBurst; + + /** + * Number of retries for a failed RTT frame. + * Applies to 1-sided RTT only. Minimum value = 0, Maximum value = 3 + */ + uint32_t numRetriesPerRttFrame; + + /** + * Following fields are only valid for 2-side RTT. + * + * + * Maximum number of retries that the initiator can + * retry an FTMR frame. + * Minimum value = 0, Maximum value = 3 + */ + uint32_t numRetriesPerFtmr; + + /** + * Whether to request location civic info or not. + */ + bool mustRequestLci; + + /** + * Whether to request location civic records or not. + */ + bool mustRequestLcr; + + /** + * Applies to 1-sided and 2-sided RTT. Valid values will + * be 2-11 and 15 as specified by the 802.11mc std for + * the FTM parameter burst duration. In a multi-burst + * request, if responder overrides with larger value, + * the initiator will return failure. In a single-burst + * request if responder overrides with larger value, + * the initiator will sent TMR_STOP to terminate RTT + * at the end of the burst_duration it requested. + */ + uint32_t burstDuration; + + /** + * RTT preamble to be used in the RTT frames. + */ + RttPreamble preamble; + + /** + * RTT BW to be used in the RTT frames. + */ + RttBw bw; +}; + +/** + * RTT Responder information + */ +struct RttResponder { + WifiChannelInfo channel; + + RttPreamble preamble; +}; + +struct WifiChannelStats { + /** + * Channel information. + */ + WifiChannelInfo channel; + /** + * Total time for which the radio is awake on this channel. + */ + uint32_t onTimeInMs; + /** + * Total time for which CCA is held busy on this channel. + */ + uint32_t ccaBusyTimeInMs; +}; + +struct StaLinkLayerRadioStats { + /** + * Baseline information as defined in HAL 1.0. + */ + @1.0::StaLinkLayerRadioStats V1_0; + + /** + * Total time for which the radio is awake due to NAN scan since boot or crash. + */ + uint32_t onTimeInMsForNanScan; + + /** + * Total time for which the radio is awake due to background scan since boot or crash. + */ + uint32_t onTimeInMsForBgScan; + + /** + * Total time for which the radio is awake due to roam scan since boot or crash. + */ + uint32_t onTimeInMsForRoamScan; + + /** + * Total time for which the radio is awake due to PNO scan since boot or crash. + */ + uint32_t onTimeInMsForPnoScan; + + /** + * Total time for which the radio is awake due to Hotspot 2.0 scans and GAS exchange since boot + * or crash. + */ + uint32_t onTimeInMsForHs20Scan; + + /** + * List of channel stats associated with this radio + */ + vec<WifiChannelStats> channelStats; + + /** + * Radio ID: An implementation specific value identifying the radio interface for which the + * stats are produced. Framework must not interpret this value. It must use this value for + * persistently identifying the statistics between calls, + * e.g. if the HAL provides them in different order. + */ + int32_t radioId; +}; + +/** + * Per peer statistics. The types of peer include the Access Point (AP), the Tunneled Direct Link + * Setup (TDLS), the Group Owner (GO), the Neighbor Awareness Networking (NAN), etc. + */ +struct StaPeerInfo { + /** + * Station count: The total number of stations currently associated with the peer. + */ + uint16_t staCount; + /** + * Channel utilization: The percentage of time (normalized to 255, i.e., x% corresponds to + * (int) x * 255 / 100) that the medium is sensed as busy measured by either physical or + * virtual carrier sense (CS) mechanism. + */ + uint16_t chanUtil; + /** + * Per rate statistics + */ + vec<StaRateStat> rateStats; +}; + +/** + * Iface statistics for the current connection. + */ +struct StaLinkLayerIfaceStats { + /** + * Baseline information as defined in HAL 1.0. + */ + @1.0::StaLinkLayerIfaceStats V1_0; + + /** + * Duty cycle for the iface. + * if this iface is being served using time slicing on a radio with one or more ifaces + * (i.e MCC), then the duty cycle assigned to this iface in %. + * If not using time slicing (i.e SCC or DBS), set to 100. + */ + uint8_t timeSliceDutyCycleInPercent; + + /** + * WME Best Effort (BE) Access Category (AC) contention time statistics. + */ + StaLinkLayerIfaceContentionTimeStats wmeBeContentionTimeStats; + + /** + * WME Background (BK) Access Category (AC) contention time statistics. + */ + StaLinkLayerIfaceContentionTimeStats wmeBkContentionTimeStats; + + /** + * WME Video (VI) Access Category (AC) contention time statistics. + */ + StaLinkLayerIfaceContentionTimeStats wmeViContentionTimeStats; + + /** + * WME Voice (VO) Access Category (AC) contention time statistics. + */ + StaLinkLayerIfaceContentionTimeStats wmeVoContentionTimeStats; + + /** + * Per peer statistics. + */ + vec<StaPeerInfo> peers; +}; + +/** + * Link layer stats retrieved via |getLinkLayerStats|. + */ +struct StaLinkLayerStats { + StaLinkLayerIfaceStats iface; + + vec<StaLinkLayerRadioStats> radios; + + /** + * TimeStamp for each stats sample. + * This is the absolute milliseconds from boot when these stats were + * sampled. + */ + TimeStampInMs timeStampInMs; +}; + +/** + * Wifi rate info. + */ +struct WifiRateInfo { + /** + * Preamble used for RTT measurements. + */ + WifiRatePreamble preamble; + + /** + * Number of spatial streams. + */ + WifiRateNss nss; + + /** + * Bandwidth of channel. + */ + WifiChannelWidthInMhz bw; + + /** + * OFDM/CCK rate code would be as per ieee std in the units of 0.5mbps. + * HT/VHT/HE/EHT it would be mcs index. + */ + uint8_t rateMcsIdx; + + /** + * Bitrate in units of 100 Kbps. + */ + uint32_t bitRateInKbps; +}; + +/** + * Per rate statistics. The rate is characterized by the combination of preamble, number of spatial + * streams, transmission bandwidth, and modulation and coding scheme (MCS). + */ +struct StaRateStat{ + /** + * Wifi rate information: preamble, number of spatial streams, bandwidth, MCS, etc. + */ + WifiRateInfo rateInfo; + /** + * Number of successfully transmitted data packets (ACK received) + */ + uint32_t txMpdu; + /** + * Number of received data packets + */ + uint32_t rxMpdu; + /** + * Number of data packet losses (no ACK) + */ + uint32_t mpduLost; + /** + * Number of data packet retries + */ + uint32_t retries; +}; + +/** + * RTT results. + */ +struct RttResult { + /** + * Peer device mac address. + */ + MacAddress addr; + + /** + * Burst number in a multi-burst request. + */ + uint32_t burstNum; + + /** + * Total RTT measurement frames attempted. + */ + uint32_t measurementNumber; + + /** + * Total successful RTT measurement frames. + */ + uint32_t successNumber; + + /** + * Maximum number of "FTM frames per burst" supported by + * the responder STA. Applies to 2-sided RTT only. + * If reponder overrides with larger value: + * - for single-burst request initiator will truncate the + * larger value and send a TMR_STOP after receiving as + * many frames as originally requested. + * - for multi-burst request, initiator will return + * failure right away. + */ + uint8_t numberPerBurstPeer; + + /** + * Ranging status. + */ + RttStatus status; + + /** + * When status == RTT_STATUS_FAIL_BUSY_TRY_LATER, + * this will be the time provided by the responder as to + * when the request can be tried again. Applies to 2-sided + * RTT only. In sec, 1-31sec. + */ + uint8_t retryAfterDuration; + + /** + * RTT type. + */ + RttType type; + + /** + * Average rssi in 0.5 dB steps e.g. 143 implies -71.5 dB. + */ + Rssi rssi; + + /** + * Rssi spread in 0.5 dB steps e.g. 5 implies 2.5 dB spread (optional). + */ + Rssi rssiSpread; + + /** + * 1-sided RTT: TX rate of RTT frame. + * 2-sided RTT: TX rate of initiator's Ack in response to FTM frame. + */ + WifiRateInfo txRate; + + /** + * 1-sided RTT: TX rate of Ack from other side. + * 2-sided RTT: TX rate of FTM frame coming from responder. + */ + WifiRateInfo rxRate; + + /** + * Round trip time in picoseconds + */ + TimeSpanInPs rtt; + + /** + * Rtt standard deviation in picoseconds. + */ + TimeSpanInPs rttSd; + + /** + * Difference between max and min rtt times recorded in picoseconds. + */ + TimeSpanInPs rttSpread; + + /** + * Distance in mm (optional). + */ + int32_t distanceInMm; + + /** + * Standard deviation in mm (optional). + */ + int32_t distanceSdInMm; + + /** + * Difference between max and min distance recorded in mm (optional). + */ + int32_t distanceSpreadInMm; + + /** + * Time of the measurement (in microseconds since boot). + */ + TimeStampInUs timeStampInUs; + + /** + * in ms, actual time taken by the FW to finish one burst + * measurement. Applies to 1-sided and 2-sided RTT. + */ + uint32_t burstDurationInMs; + + /** + * Number of bursts allowed by the responder. Applies + * to 2-sided RTT only. + */ + uint32_t negotiatedBurstNum; + + /** + * for 11mc only. + */ + WifiInformationElement lci; + + /** + * for 11mc only. + */ + WifiInformationElement lcr; +}; + +/** + * NAN data path channel information provided to the framework. + */ +struct NanDataPathChannelInfo { + /** + * Channel frequency in MHz. + */ + WifiChannelInMhz channelFreq; + /** + * Channel bandwidth in MHz. + */ + WifiChannelWidthInMhz channelBandwidth; + /** + * Number of spatial streams used in the channel. + */ + uint32_t numSpatialStreams; +}; + +/** + * NAN Data path confirmation Indication structure. + * Event indication is received on both initiator and responder side when negotiation for a + * data-path finish: on success or failure. + */ +struct NanDataPathConfirmInd { + /** + * Baseline information as defined in HAL 1.0. + */ + @1.0::NanDataPathConfirmInd V1_0; + /** + * The channel(s) on which the NDP is scheduled to operate. + * Updates to the operational channels are provided using the |eventDataPathScheduleUpdate| + * event. + */ + vec<NanDataPathChannelInfo> channelInfo; +}; + +/** + * NAN data path channel information update indication structure. + * Event indication is received by all NDP owners whenever the channels on which the NDP operates + * are updated. + * Note: multiple NDPs may share the same schedule, the indication specifies all NDPs to which it + * applies. + */ +struct NanDataPathScheduleUpdateInd { + /** + * The discovery address (NMI) of the peer to which the NDP is connected. + */ + MacAddress peerDiscoveryAddress; + /** + * The updated channel(s) information. + */ + vec<NanDataPathChannelInfo> channelInfo; + /** + * The list of NDPs to which this update applies. + */ + vec<uint32_t> ndpInstanceIds; +}; + +/** + * Wifi usable channel information. + */ +struct WifiUsableChannel { + /** + * Wifi channel freqeuncy in MHz. + */ + WifiChannelInMhz channel; + + /** + * Wifi channel bandwidth in MHz. + */ + WifiChannelWidthInMhz channelBandwidth; + + /** + * Iface modes feasible on this channel. + */ + bitfield<WifiIfaceMode> ifaceModeMask; +}; + +/** + * RTT Capabilities. + */ +struct RttCapabilities { + /** + * if 1-sided rtt data collection is supported. + */ + bool rttOneSidedSupported; + + /** + * if ftm rtt data collection is supported. + */ + bool rttFtmSupported; + + /** + * if initiator supports LCI request. Applies to 2-sided RTT. + */ + bool lciSupported; + + /** + * if initiator supports LCR request. Applies to 2-sided RTT. + */ + bool lcrSupported; + + /** + * if 11mc responder mode is supported. + */ + bool responderSupported; + + /** + * Bit mask indicates what preamble is supported by initiator. + * Combination of |RttPreamble| values. + */ + bitfield<RttPreamble> preambleSupport; + + /** + * Bit mask indicates what BW is supported by initiator. + * Combination of |RttBw| values. + */ + bitfield<RttBw> bwSupport; + + /** + * Draft 11mc spec version supported by chip. + * For instance, version 4.0 must be 40 and version 4.3 must be 43 etc. + */ + uint8_t mcVersion; +}; diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl index 18baea6961..bdc5f3483c 100644 --- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl +++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl @@ -125,6 +125,7 @@ interface ISupplicantStaNetwork { void setWapiCertSuite(in String suite); void setWepKey(in int keyIdx, in byte[] wepKey); void setWepTxKeyIdx(in int keyIdx); + void setRoamingConsortiumSelection(in byte[] selectedRcoi); const int SSID_MAX_LEN_IN_BYTES = 32; const int PSK_PASSPHRASE_MIN_LEN_IN_BYTES = 8; const int PSK_PASSPHRASE_MAX_LEN_IN_BYTES = 63; diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl index 603e2add6e..1a2087dbe1 100644 --- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl +++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl @@ -1092,4 +1092,17 @@ interface ISupplicantStaNetwork { * |SupplicantStatusCode.FAILURE_NETWORK_INVALID| */ void setWepTxKeyIdx(in int keyIdx); + + /** + * Set the roaming consortium selection. + * + * @param selectedRcoi Indicates the roaming consortium selection. This is a + * 3 or 5-octet long byte array that indicates the selected RCOI + * used for a Passpoint connection. + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_ARGS_INVALID|, + * |SupplicantStatusCode.FAILURE_UNKNOWN|, + * |SupplicantStatusCode.FAILURE_NETWORK_INVALID| + */ + void setRoamingConsortiumSelection(in byte[] selectedRcoi); } diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp index 0a35f666f4..c6dd98152d 100644 --- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp +++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp @@ -784,6 +784,14 @@ TEST_P(SupplicantStaNetworkAidlTest, GetWpsNfcConfigurationToken) { EXPECT_NE(retrievedToken.size(), 0); } +/* + * SetRoamingConsortiumSelection + */ +TEST_P(SupplicantStaNetworkAidlTest, SetRoamingConsortiumSelection) { + const std::vector<uint8_t> testSelection = std::vector<uint8_t>({0x11, 0x21, 0x33, 0x44}); + EXPECT_TRUE(sta_network_->setRoamingConsortiumSelection(testSelection).isOk()); +} + GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaNetworkAidlTest); INSTANTIATE_TEST_SUITE_P(Supplicant, SupplicantStaNetworkAidlTest, testing::ValuesIn(android::getAidlHalInstanceNames( |